Files
s390-tools/pvattest/src/attestation.c
Marc Hartmayer 330ddb7adb libpv: add copied parameter to pv_gbytes_memcpy and adapt pvattest
It's often useful to know how much data was actually copied, therefore let's
introduce an nullable parameter `@copied` to `pv_gbytes_memcpy`.

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-10-22 20:27:16 +02:00

149 lines
4.8 KiB
C

/*
* Attestation related functions
*
* Copyright IBM Corp. 2022
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
/* Must be included before any other header */
#include "config.h"
#include "libpv/cert.h"
#include "libpv/hash.h"
#include "libpv/se-hdr.h"
#include "exchange_format.h"
#include "attestation.h"
G_STATIC_ASSERT(sizeof(((att_meas_ctx_t *)0)->pld) == sizeof(((struct pv_hdr_head *)0)->pld));
G_STATIC_ASSERT(sizeof(((att_meas_ctx_t *)0)->ald) == sizeof(((struct pv_hdr_head *)0)->ald));
G_STATIC_ASSERT(sizeof(((att_meas_ctx_t *)0)->tld) == sizeof(((struct pv_hdr_head *)0)->tld));
G_STATIC_ASSERT(sizeof(((att_meas_ctx_t *)0)->tag) == sizeof(((struct pv_hdr *)0)->tag));
struct att_meas_sizes {
uint16_t user_data_len;
uint16_t zeros;
uint32_t additional_data_len;
} __packed;
G_STATIC_ASSERT(sizeof(struct att_meas_sizes) == 8);
/*
* All optional arguments may be NULL
* user_data is up to 256 bytes long, or NULL.
* nonce is 16 bytes long or NULL.
* additional_data is up to 32768 bytes long or NULL.
*/
GBytes *att_gen_measurement_hmac_sha512(const att_meas_ctx_t *meas_ctx, GBytes *measurement_key,
GBytes *optional_user_data, GBytes *optional_nonce,
GBytes *optional_additional_data, GError **error)
{
struct att_meas_sizes meas_sizes = {};
g_autoptr(HMAC_CTX) hmac_ctx = NULL;
size_t additional_data_size = 0;
size_t user_data_size = 0;
size_t nonce_size = 0;
pv_wrapped_g_assert(meas_ctx);
pv_wrapped_g_assert(measurement_key);
if (optional_user_data)
user_data_size = g_bytes_get_size(optional_user_data);
if (optional_additional_data)
additional_data_size = g_bytes_get_size(optional_additional_data);
if (optional_nonce)
nonce_size = g_bytes_get_size(optional_nonce);
/* checks for these sizes resulting in GErrors are done before */
g_assert(user_data_size <= PVATTEST_USER_DATA_MAX_SIZE);
g_assert(additional_data_size <= PVATTEST_ADDITIONAL_MAX_SIZE);
g_assert(nonce_size == 0 || nonce_size == ARCB_V1_NONCE_SIZE);
pv_wrapped_g_assert(meas_ctx);
pv_wrapped_g_assert(measurement_key);
hmac_ctx = pv_hmac_ctx_new(measurement_key, EVP_sha512(), error);
if (!hmac_ctx)
return NULL;
meas_sizes.user_data_len = GUINT16_TO_BE((uint16_t)user_data_size);
meas_sizes.zeros = 0;
meas_sizes.additional_data_len = GUINT32_TO_BE((uint32_t)additional_data_size);
if (pv_hmac_ctx_update_raw(hmac_ctx, meas_ctx, sizeof(*meas_ctx), error) != 0)
return NULL;
/* add the sizes of user and additional data. */
if (pv_hmac_ctx_update_raw(hmac_ctx, &meas_sizes, sizeof(meas_sizes), error))
return NULL;
/* update optional data. if NULL passed (or size = 0) nothing will happen to the HMAC_CTX */
if (pv_hmac_ctx_update(hmac_ctx, optional_user_data, error) != 0)
return NULL;
if (pv_hmac_ctx_update(hmac_ctx, optional_nonce, error) != 0)
return NULL;
if (pv_hmac_ctx_update(hmac_ctx, optional_additional_data, error) != 0)
return NULL;
return pv_hamc_ctx_finalize(hmac_ctx, error);
}
att_meas_ctx_t *att_extract_from_hdr(GBytes *se_hdr, GError **error)
{
g_autofree att_meas_ctx_t *meas = NULL;
const struct pv_hdr *hdr = NULL;
size_t se_hdr_tag_offset;
size_t se_hdr_size;
uint8_t *hdr_u8;
pv_wrapped_g_assert(se_hdr);
hdr = g_bytes_get_data(se_hdr, &se_hdr_size);
hdr_u8 = (uint8_t *)hdr;
if (se_hdr_size < PV_V1_PV_HDR_MIN_SIZE) {
g_set_error(error, ATT_ERROR, ATT_ERR_INVALID_HDR,
_("Invalid SE header provided."));
return NULL;
}
if (GUINT32_FROM_BE(hdr->head.phs) != se_hdr_size ||
GUINT64_FROM_BE(hdr->head.magic) != PV_MAGIC_NUMBER) {
g_set_error(error, ATT_ERROR, ATT_ERR_INVALID_HDR,
_("Invalid SE header provided."));
return NULL;
}
se_hdr_tag_offset = GUINT32_FROM_BE(hdr->head.phs) - sizeof(hdr->tag);
meas = g_new0(att_meas_ctx_t, 1);
memcpy(meas->pld, hdr->head.pld, sizeof(meas->pld));
memcpy(meas->ald, hdr->head.ald, sizeof(meas->ald));
memcpy(meas->tld, hdr->head.tld, sizeof(meas->tld));
memcpy(meas->tag, hdr_u8 + se_hdr_tag_offset, sizeof(meas->tag));
return g_steal_pointer(&meas);
}
void att_add_uid(att_meas_ctx_t *meas_ctx, GBytes *config_uid)
{
pv_wrapped_g_assert(meas_ctx);
pv_wrapped_g_assert(config_uid);
g_assert(g_bytes_get_size(config_uid) == ATT_CONFIG_UID_SIZE);
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid, NULL);
}
gboolean att_verify_measurement(const GBytes *calculated_measurement,
const GBytes *uvio_measurement, GError **error)
{
pv_wrapped_g_assert(calculated_measurement);
pv_wrapped_g_assert(uvio_measurement);
if (g_bytes_compare(calculated_measurement, uvio_measurement) != 0) {
g_set_error(error, ATT_ERROR, ATT_ERR_MEASUREMENT_VERIFICATION_FAILED,
_("Calculated and received attestation measurement are not equal."));
return FALSE;
}
return TRUE;
}