blob: 7d22193b74dc42c8651891cdd8489aad3b119805 [file] [log] [blame]
/*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: Copyright Laurence Lundblade.
* SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
*/
/*
* This file is derived from:
* trusted-firmware-m/secure_fw/partitions/initial_attestation/attest_token_encode.c
*/
#include <assert.h>
#include <attestation.h>
#include <attestation_defs_priv.h>
#include <attestation_priv.h>
#include <attestation_token.h>
#include <debug.h>
#include <measurement.h>
#include <qcbor/qcbor.h>
#include <simd.h>
#include <t_cose/q_useful_buf.h>
#include <t_cose/t_cose_common.h>
#include <t_cose/t_cose_sign1_sign.h>
#include <utils_def.h>
/*
* According to IANA hash algorithm registry:
* - https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml
*/
static void attest_get_hash_algo_text(enum hash_algo algorithm,
struct q_useful_buf_c *algo_text)
{
const char *sha256 = "sha-256";
const char *sha512 = "sha-512";
switch (algorithm) {
case HASH_SHA_256:
*algo_text = UsefulBuf_FromSZ(sha256);
break;
case HASH_SHA_512:
*algo_text = UsefulBuf_FromSZ(sha512);
break;
default:
assert(false);
}
}
/*
* Outline of token creation. Much of this occurs inside
* t_cose_sign1_encode_parameters() and t_cose_sign1_encode_signature().
*
* - Create encoder context
* - Open the CBOR array that hold the COSE_Sign1
* - Write COSE Headers
* - Protected Header
* - Algorithm ID
* - Unprotected Headers
* - Key ID
* - Open payload bstr
* - Write payload data, maybe lots of it
* - Get bstr that is the encoded payload
* - Compute signature
* - Create a separate encoder context for Sig_structure
* - Encode CBOR context identifier
* - Encode protected headers
* - Encode two empty bstr
* - Add one more empty bstr that is a "fake payload"
* - Close off Sig_structure
* - Hash all but "fake payload" of Sig_structure
* - Get payload bstr ptr and length
* - Continue hash of the real encoded payload
* - Run ECDSA
* - Write signature into the CBOR output
* - Close CBOR array holding the COSE_Sign1
*/
static enum attest_token_err_t
attest_token_encode_start(struct attest_token_encode_ctx *me,
uint32_t opt_flags,
int32_t key_select,
int32_t cose_alg_id,
const struct q_useful_buf *out_buf)
{
enum t_cose_err_t cose_res;
struct t_cose_key attest_key __unused;
psa_key_handle_t key_handle __unused;
assert(me != NULL);
/* Remember some of the configuration values */
me->opt_flags = opt_flags;
me->key_select = key_select;
t_cose_signature_sign_restart_init(&me->restartable_signer_ctx, cose_alg_id);
t_cose_signature_sign_restart_set_crypto_context(&me->restartable_signer_ctx, &(me->crypto_ctx));
t_cose_sign_sign_init(&me->sign_ctx, T_COSE_OPT_MESSAGE_TYPE_SIGN1);
t_cose_sign_add_signer(&me->sign_ctx, t_cose_signature_sign_from_restart(&me->restartable_signer_ctx));
#if !ATTEST_EL3_TOKEN_SIGN
/*
* Get the reference to `mbedtls_ecp_keypair` and set it to t_cose.
*/
if (attest_get_realm_signing_key(&key_handle) != 0) {
return ATTEST_TOKEN_ERR_SIGNING_KEY;
}
attest_key.key.handle = key_handle;
t_cose_signature_sign_restart_set_signing_key(&me->restartable_signer_ctx, attest_key);
#endif
/* Spin up the CBOR encoder */
QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf);
/*
* This will cause the cose headers to be encoded and written into
* out_buf using me->cbor_enc_ctx
*/
cose_res = t_cose_sign_encode_start(&me->sign_ctx, &me->cbor_enc_ctx);
if (cose_res != T_COSE_SUCCESS) {
return ATTEST_TOKEN_ERR_COSE_ERROR;
}
return ATTEST_TOKEN_ERR_SUCCESS;
}
enum attest_token_err_t
attest_realm_token_sign(struct token_sign_cntxt *me,
size_t *completed_token_len)
{
/* The completed and signed encoded cose_sign1 */
struct q_useful_buf_c completed_token_ub;
enum attest_token_err_t attest_res = ATTEST_TOKEN_ERR_SUCCESS;
QCBORError qcbor_res;
enum t_cose_err_t cose_res;
assert(me != NULL);
assert(completed_token_len != NULL);
if (me->state != ATTEST_TOKEN_SIGN) {
return ATTEST_TOKEN_ERR_INVALID_STATE;
}
/* Enable Data Independent Timing feature */
write_dit(DIT_BIT);
/* Finish up the COSE_Sign1. This is where the signing happens */
SIMD_FPU_ALLOW(
cose_res = t_cose_sign_encode_finish(&me->ctx.sign_ctx,
NULL_Q_USEFUL_BUF_C,
me->ctx.signed_payload,
&me->ctx.cbor_enc_ctx));
/* Disable Data Independent Timing feature */
write_dit(0x0);
if (cose_res == T_COSE_ERR_SIG_IN_PROGRESS) {
/* Token signing has not yet finished */
return ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS;
}
if (cose_res != T_COSE_SUCCESS) {
/* Main errors are invoking the hash or signature */
return ATTEST_TOKEN_ERR_COSE_ERROR;
}
/*
* Finally close off the CBOR formatting and get the pointer and length
* of the resulting COSE_Sign1
*/
qcbor_res = QCBOREncode_Finish(&me->ctx.cbor_enc_ctx,
&completed_token_ub);
switch (qcbor_res) {
case QCBOR_ERR_BUFFER_TOO_SMALL:
attest_res = ATTEST_TOKEN_ERR_TOO_SMALL;
break;
case QCBOR_SUCCESS:
/* coverity[uninit_use:SUPPRESS] */
*completed_token_len = completed_token_ub.len;
/* This was the last signing cycle. Transition to next state */
me->state = ATTEST_TOKEN_CREATE;
break;
default:
/* likely from array not closed, too many closes ... */
attest_res = ATTEST_TOKEN_ERR_CBOR_FORMATTING;
}
return attest_res;
}
enum attest_token_err_t
attest_cca_token_create(struct token_sign_cntxt *me,
void *attest_token_buf,
size_t attest_token_buf_size,
const void *realm_token_buf,
size_t realm_token_len,
size_t *cca_token_len)
{
struct q_useful_buf_c completed_token;
QCBOREncodeContext cbor_enc_ctx;
QCBORError qcbor_res;
struct q_useful_buf_c platform_token;
struct q_useful_buf attest_token_ub = {attest_token_buf, attest_token_buf_size};
struct q_useful_buf_c realm_token_ub = {realm_token_buf, realm_token_len};
int ret;
if (me->state != ATTEST_TOKEN_CREATE) {
return ATTEST_TOKEN_ERR_INVALID_STATE;
}
/* Get the platform token */
ret = attest_get_platform_token(&platform_token.ptr,
&platform_token.len);
if (ret != 0) {
ERROR("Platform token is not ready for retrieval\n");
return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
}
QCBOREncode_Init(&cbor_enc_ctx, attest_token_ub);
QCBOREncode_AddTag(&cbor_enc_ctx, TAG_CCA_TOKEN);
QCBOREncode_OpenMap(&cbor_enc_ctx);
QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
CCA_PLAT_TOKEN,
platform_token);
QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
CCA_REALM_DELEGATED_TOKEN,
realm_token_ub);
QCBOREncode_CloseMap(&cbor_enc_ctx);
qcbor_res = QCBOREncode_Finish(&cbor_enc_ctx, &completed_token);
if (qcbor_res == QCBOR_ERR_BUFFER_TOO_SMALL) {
ERROR("CCA output token buffer too small\n");
return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
} else if (qcbor_res != QCBOR_SUCCESS) {
/* likely from array not closed, too many closes, ... */
ERROR("CBOR Encode finish failed with error 0x%x\n", qcbor_res);
return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
}
/* Transition back to NOT_STARTED */
me->state = ATTEST_TOKEN_NOT_STARTED;
/* coverity[uninit_use:SUPPRESS] */
*cca_token_len = completed_token.len;
return ATTEST_TOKEN_ERR_SUCCESS;
}
/*
* Assemble the Realm Attestation Token in the buffer provided in
* realm_token_buf, except the signature.
*
* As per section A7.2.3.1 of RMM specification, Realm Attestation token is
* composed of:
* - Realm Challenge
* - Realm Personalization Value
* - Realm Hash Algorithm Id
* - Realm Public Key
* - Realm Public Key Hash Algorithm Id
* - Realm Initial Measurement
* - Realm Extensible Measurements
*/
int attest_realm_token_create(enum hash_algo algorithm,
unsigned char measurements[][MAX_MEASUREMENT_SIZE],
unsigned int num_measurements,
const void *rpv_buf,
size_t rpv_len,
const void *challenge_buf,
size_t challenge_len,
struct token_sign_cntxt *ctx,
void *realm_token_buf,
size_t realm_token_buf_size)
{
struct q_useful_buf_c buf;
size_t measurement_size;
enum attest_token_err_t token_ret = ATTEST_TOKEN_ERR_INVALID_STATE;
struct q_useful_buf realm_token_ub = {realm_token_buf, realm_token_buf_size};
struct q_useful_buf_c rpv_ub = {rpv_buf, rpv_len};
int ret;
/* Can only be called in the init state */
if (ctx->state != ATTEST_TOKEN_INIT) {
return (int)token_ret;
}
assert(num_measurements == MEASUREMENT_SLOT_NR);
/*
* Get started creating the token. This sets up the CBOR and COSE
* contexts which causes the COSE headers to be constructed.
*/
token_ret = attest_token_encode_start(&(ctx->ctx),
0, /* option_flags */
0, /* key_select */
T_COSE_ALGORITHM_ES384,
&realm_token_ub);
if (token_ret != ATTEST_TOKEN_ERR_SUCCESS) {
return (int)token_ret;
}
QCBOREncode_BstrWrap(&(ctx->ctx.cbor_enc_ctx));
QCBOREncode_OpenMap(&(ctx->ctx.cbor_enc_ctx));
/* Add challenge value, which is the only input from the caller. */
buf.ptr = challenge_buf;
buf.len = challenge_len;
QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_CHALLENGE,
buf);
QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_PERSONALIZATION_VALUE,
rpv_ub);
ret = attest_get_realm_public_key(&buf);
if (ret != 0) {
return ret;
}
QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_PUB_KEY,
buf);
attest_get_hash_algo_text(algorithm, &buf);
QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_HASH_ALGM_ID,
buf);
attest_get_hash_algo_text(attest_get_realm_public_key_hash_algo_id(),
&buf);
QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_PUB_KEY_HASH_ALGO_ID,
buf);
QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_PROFILE,
UsefulBuf_FromSZ(CCA_REALM_PROFILE_STR));
measurement_size = measurement_get_size(algorithm);
assert(measurement_size <= MAX_MEASUREMENT_SIZE);
/* RIM: 0, REM: 1..4 */
buf.ptr = &measurements[RIM_MEASUREMENT_SLOT];
buf.len = measurement_size;
QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_INITIAL_MEASUREMENT,
buf);
QCBOREncode_OpenArrayInMapN(&(ctx->ctx.cbor_enc_ctx),
CCA_REALM_EXTENSIBLE_MEASUREMENTS);
for (unsigned int i = 1U; i < num_measurements; ++i) {
buf.ptr = &measurements[i];
buf.len = measurement_size;
QCBOREncode_AddBytes(&(ctx->ctx.cbor_enc_ctx), buf);
}
QCBOREncode_CloseArray(&(ctx->ctx.cbor_enc_ctx));
QCBOREncode_CloseMap(&(ctx->ctx.cbor_enc_ctx));
QCBOREncode_CloseBstrWrap2(&(ctx->ctx.cbor_enc_ctx), false,
&(ctx->ctx.signed_payload));
/* Transition to ATTEST_CCA_TOKEN_SIGN state */
ctx->state = ATTEST_TOKEN_SIGN;
return 0;
}
/* This function will only succeed if attestation_init() has succeeded. */
enum attest_token_err_t attest_token_ctx_init(struct token_sign_cntxt *token_ctx,
unsigned char *heap_buf,
unsigned long heap_buf_len,
uintptr_t cookie)
{
(void)cookie;
if (token_ctx->state != ATTEST_TOKEN_INIT) {
int ret;
/* Clear context for signing an attestation token */
(void)memset(token_ctx, 0, sizeof(struct token_sign_cntxt));
ret = attestation_heap_ctx_init(heap_buf,
heap_buf_len);
if (ret != 0) {
return ATTEST_TOKEN_ERR_INVALID_STATE;
}
#if ATTEST_EL3_TOKEN_SIGN
t_cose_crypto_el3_ctx_init(&token_ctx->ctx.crypto_ctx, cookie);
#endif
token_ctx->state = ATTEST_TOKEN_INIT;
}
return ATTEST_TOKEN_ERR_SUCCESS;
}