blob: 7a3dac75d5ee640112f5425267b60906c5dfc755 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Retrieve Attestation Measurement Utravisor Call tests
*
* Copyright IBM Corp. 2022
*
* Authors:
* Steffen Eiden <seiden@linux.ibm.com>
*/
#include <libcflat.h>
#include <alloc_page.h>
#include <asm/page.h>
#include <asm/facility.h>
#include <asm/uv.h>
#include <sclp.h>
#include <uv.h>
#define ARCB_VERSION_NONE 0
#define ARCB_VERSION_1 0x100
#define ARCB_MEAS_NONE 0
#define ARCB_MEAS_HMAC_SHA512 1
#define MEASUREMENT_SIZE_HMAC_SHA512 64
#define PAF_PHKH_ATT (1ULL << 61)
#define ADDITIONAL_SIZE_PAF_PHKH_ATT 32
/* arcb with one key slot and no nonce */
struct uv_arcb_v1 {
uint64_t reserved0; /* 0x0000 */
uint32_t req_ver; /* 0x0008 */
uint32_t req_len; /* 0x000c */
uint8_t iv[12]; /* 0x0010 */
uint32_t reserved1c; /* 0x001c */
uint8_t reserved20[7]; /* 0x0020 */
uint8_t nks; /* 0x0027 */
int32_t reserved28; /* 0x0028 */
uint32_t sea; /* 0x002c */
uint64_t plaint_att_flags; /* 0x0030 */
uint32_t meas_alg_id; /* 0x0038 */
uint32_t reserved3c; /* 0x003c */
uint8_t cpk[160]; /* 0x0040 */
uint8_t key_slot[80]; /* 0x00e0 */
uint8_t meas_key[64]; /* 0x0130 */
uint8_t tag[16]; /* 0x0170 */
} __attribute__((packed));
struct attest_request_v1 {
struct uv_arcb_v1 arcb;
uint8_t measurement[MEASUREMENT_SIZE_HMAC_SHA512];
uint8_t additional[ADDITIONAL_SIZE_PAF_PHKH_ATT];
};
static void test_attest_v1(uint64_t page)
{
struct uv_cb_attest uvcb = {
.header.cmd = UVC_CMD_ATTESTATION,
.header.len = sizeof(uvcb),
};
const struct uv_cb_qui *uvcb_qui = uv_get_query_data();
struct attest_request_v1 *attest_req = (void *)page;
struct uv_arcb_v1 *arcb = &attest_req->arcb;
int cc;
report_prefix_push("v1");
if (!test_bit_inv(0, &uvcb_qui->supp_att_hdr_ver)) {
report_skip("Attestation version 1 not supported");
goto done;
}
memset((void *)page, 0, PAGE_SIZE);
/*
* Create a minimal arcb/uvcb such that FW has everything to start
* unsealing the request. However, this unsealing will fail as the
* kvm-unit-test framework provides no cryptography functions that
* would be needed to seal such requests.
*/
arcb->req_ver = ARCB_VERSION_1;
arcb->req_len = sizeof(*arcb);
arcb->nks = 1;
arcb->sea = sizeof(arcb->meas_key);
arcb->plaint_att_flags = PAF_PHKH_ATT;
arcb->meas_alg_id = ARCB_MEAS_HMAC_SHA512;
uvcb.arcb_addr = (uint64_t)&attest_req->arcb;
uvcb.measurement_address = (uint64_t)attest_req->measurement;
uvcb.measurement_length = sizeof(attest_req->measurement);
uvcb.add_data_address = (uint64_t)attest_req->additional;
uvcb.add_data_length = sizeof(attest_req->additional);
uvcb.continuation_token = 0xff;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x101, "invalid continuation token");
uvcb.continuation_token = 0;
uvcb.user_data_length = sizeof(uvcb.user_data) + 1;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x102, "invalid user data size");
uvcb.user_data_length = 0;
uvcb.arcb_addr = get_ram_size() + PAGE_SIZE;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x103, "invalid address arcb");
uvcb.arcb_addr = page;
/* 0x104 - 0x105 need an unseal-able request */
arcb->req_ver = ARCB_VERSION_NONE;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x106, "unsupported version");
arcb->req_ver = ARCB_VERSION_1;
arcb->req_len += 1;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x107, "arcb too big");
arcb->req_len -= 1;
/*
* The arcb needs to grow as well if number of key slots (nks)
* is increased. However, this is not the case and there is no explicit
* 'too many/less nks for that arcb size' error code -> expect 0x107
*/
arcb->nks = 2;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x107, "too many nks for arcb");
arcb->nks = 1;
arcb->nks = 0;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x108, "invalid num key slots");
arcb->nks = 1;
/*
* Possible valid size (when using nonce).
* However, req_len too small to host a nonce
*/
arcb->sea = 80;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x109, "encrypted size too big");
arcb->sea = 17;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x109, "encrypted size too small");
arcb->sea = 64;
arcb->plaint_att_flags = uvcb_qui->supp_paf ^ GENMASK_ULL(63, 0);
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x10a, "invalid flag");
arcb->plaint_att_flags = PAF_PHKH_ATT;
arcb->meas_alg_id = ARCB_MEAS_NONE;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x10b, "invalid measurement algorithm");
arcb->meas_alg_id = ARCB_MEAS_HMAC_SHA512;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x10c, "unable unseal");
uvcb.measurement_length = 0;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x10d, "invalid measurement size");
uvcb.measurement_length = sizeof(attest_req->measurement);
uvcb.add_data_length = 0;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc == 1 && uvcb.header.rc == 0x10e, "invalid additional size");
uvcb.add_data_length = sizeof(attest_req->additional);
done:
report_prefix_pop();
}
static void test_attest(uint64_t page)
{
struct uv_cb_attest uvcb = {
.header.cmd = UVC_CMD_ATTESTATION,
.header.len = sizeof(uvcb),
};
const struct uv_cb_qui *uvcb_qui = uv_get_query_data();
int cc;
/* Verify that the UV supports at least one header version */
report(uvcb_qui->supp_att_hdr_ver, "has hdr support");
memset((void *)page, 0, PAGE_SIZE);
uvcb.header.len -= 1;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc && uvcb.header.rc == UVC_RC_INV_LEN, "uvcb too small");
uvcb.header.len += 1;
uvcb.header.len += 1;
cc = uv_call(0, (uint64_t)&uvcb);
report(cc && uvcb.header.rc == UVC_RC_INV_LEN, "uvcb too large");
uvcb.header.len -= 1;
}
int main(void)
{
bool has_uvc = test_facility(158);
uint64_t page;
report_prefix_push("attestation");
if (!has_uvc) {
report_skip("Ultravisor call facility is not available");
goto done;
}
if (!uv_os_is_guest()) {
report_skip("Not a protected guest");
goto done;
}
if (!uv_query_test_call(BIT_UVC_CMD_ATTESTATION)) {
report_skip("Attestation not supported.");
goto done;
}
page = (uint64_t)alloc_page();
/* The privilege check is done in uv-guest.c */
test_attest(page);
test_attest_v1(page);
free_page((void *)page);
done:
report_prefix_pop();
return report_summary();
}