Skip to content

Commit

Permalink
core/pta/veraison_attestation: integrate Veraison remote attestation PTA
Browse files Browse the repository at this point in the history
Copy remote attestation PTA functionality from the repository:
https://github.com/iisec-suzaki/optee-ra (commit: 80ca8ef), and make
the following adjustments for integration:

- Add build configuration for remote attestation PTA by introducing
  the CFG_VERAISON_ATTESTATION_PTA option to align with the new naming
  convention.
- Replace the custom base64 implementation with the base64 library
  added in PR #7007.
- Update QCBOR integration by removing custom QCBOR files and using
  the standard library, adjusting paths as necessary.
- Apply region validation improvements introduced in PR #6195.
- Update API calls in sign.c to align with libmbedtls changes from
  PR #6151.
- Calculate the required buffer size at runtime to minimize memory
  allocation.
- Refactor code to improve readability and maintainability.
- Add SPDX license identifier (BSD-2-Clause) and copyright notice.

Signed-off-by: Yuichi Sugiyama <[email protected]>
Reviewed-by: Thomas Fossati <[email protected]>
Acked-by: Jerome Forissier <[email protected]>
  • Loading branch information
mmxsrup committed Nov 18, 2024
1 parent bc4dd2a commit b580089
Show file tree
Hide file tree
Showing 13 changed files with 1,010 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/pta/sub.mk
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ subdirs-y += bcm
subdirs-y += stm32mp
subdirs-y += imx
subdirs-y += k3
subdirs-y += veraison_attestation

ifeq ($(CFG_REMOTEPROC_PTA),y)
gensrcs-y += rproc_pub_key
Expand Down
24 changes: 24 additions & 0 deletions core/pta/veraison_attestation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Veraison Attestation PTA

This is a proof of concept for adding attestation capabilities to S-EL0 TAs and a demonstrator of an end-to-end remote attestation protocol [1] using the Veraison verifier [2].

For convenience, this PTA reuses the PSA token format [3]. However, note that PSA semantics do not fully apply, as many relevant properties required by the PSA SM [4] are not met.

Furthermore, the attestation evidence produced by the PTA attests to the memory contents of the calling TA, but there is no way to establish trust in the PTA in the first place.

For these reasons, the PTA should not be regarded as a best practice example for real-world attestation.

Instead, this PTA aims to demonstrate the integration of various libraries and tools to create a trusted application focused on attestation within the OP-TEE environment and to practically explore an end-to-end remote attestation flow using this approach.

## Known Limitations

1. **PSA Semantics Limitations:** Although this PTA reuses the PSA token format, many of the relevant properties required by the PSA Security Model (SM) are not met. This can impact the effectiveness and security assumptions typically expected from PSA-based attestation.

2. **Lack of Trust in the PTA:** The attestation evidence produced by the PTA attests to the memory contents of the calling TA, but there is no mechanism to establish trust in the PTA itself from a lower-level entity, such as the bootloader. Without such anchoring to a platform Root of Trust (RoT), the PTA lacks foundational trust, which weakens the overall chain of trust.

## References

[1] https://datatracker.ietf.org/doc/rfc9334
[2] https://github.com/veraison/services
[3] https://datatracker.ietf.org/doc/draft-tschofenig-rats-psa-token
[4] https://www.psacertified.org/app/uploads/2021/12/JSADEN014_PSA_Certified_SM_V1.1_BET0.pdf
346 changes: 346 additions & 0 deletions core/pta/veraison_attestation/cbor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (C) 2024, Institute of Information Security (IISEC)
*/

#include <compiler.h>
#include <kernel/pseudo_ta.h>
#include <mempool.h>

#include "cbor.h"
#include "sign.h"

struct cbor_evidence_args {
UsefulBufC ubc_eat_profile;
int psa_client_id;
int psa_security_lifecycle;
UsefulBufC ubc_psa_implementation_id;
UsefulBufC ubc_measurement_type;
UsefulBufC ubc_signer_id;
UsefulBufC ubc_psa_instance_id;
UsefulBufC ubc_psa_nonce;
UsefulBufC ubc_measurement_value;
};

struct cose_evidence_args {
UsefulBufC ubc_cbor_evidence;
};

struct tbs_structure_args {
UsefulBufC protected_header;
UsefulBufC aad;
UsefulBufC payload;
};

static void
encode_cbor_evidence(QCBOREncodeContext *context, UsefulBufC ubc_eat_profile,
const int psa_client_id, const int psa_security_lifecycle,
UsefulBufC ubc_psa_implementation_id,
UsefulBufC ubc_measurement_type, UsefulBufC ubc_signer_id,
UsefulBufC ubc_psa_instance_id, UsefulBufC ubc_psa_nonce,
UsefulBufC ubc_measurement_value)
{
QCBOREncode_OpenMap(context);

/* Profile Definition */
QCBOREncode_AddTextToMapN(context, PSA_PROFILE_DEFINITION,
ubc_eat_profile);

/* Client ID */
QCBOREncode_AddInt64ToMapN(context, PSA_CLIENT_ID, psa_client_id);

/* Security Lifecycle */
QCBOREncode_AddInt64ToMapN(context, PSA_SECURITY_LIFECYCLE,
psa_security_lifecycle);

/* Implementation ID */
QCBOREncode_AddBytesToMapN(context, PSA_IMPLEMENTATION_ID,
ubc_psa_implementation_id);

/* Software Components */
QCBOREncode_OpenArrayInMapN(context, PSA_SW_COMPONENTS); /* [ */
QCBOREncode_OpenMap(context); /* { */
QCBOREncode_AddTextToMapN(context, PSA_SW_COMPONENT_MEASUREMENT_TYPE,
ubc_measurement_type);
QCBOREncode_AddBytesToMapN(context, PSA_SW_COMPONENT_MEASUREMENT_VALUE,
ubc_measurement_value);
QCBOREncode_AddBytesToMapN(context, PSA_SW_COMPONENT_SIGNER_ID,
ubc_signer_id);
QCBOREncode_CloseMap(context); /* } */
QCBOREncode_CloseArray(context); /* ] */

/* Nonce */
QCBOREncode_AddBytesToMapN(context, PSA_NONCE, ubc_psa_nonce);

/* Instance ID */
QCBOREncode_AddBytesToMapN(context, PSA_INSTANCE_ID,
ubc_psa_instance_id);

QCBOREncode_CloseMap(context);
}

/* Generic function for encoding and buffer allocation */
static UsefulBufC build_encoded_buffer(void (*encode_func)(QCBOREncodeContext *,
void *),
void *encode_args)
{
QCBOREncodeContext context = { };
uint8_t *buffer = NULL;
size_t required_size = 0;
UsefulBufC encoded_data = { NULL, 0 };

/* First encode: calculate the required length */
QCBOREncode_Init(&context, (UsefulBuf){ NULL, INT32_MAX });
encode_func(&context, encode_args);
if (QCBOREncode_FinishGetSize(&context, &required_size) !=
QCBOR_SUCCESS) {
return NULLUsefulBufC;
}

/* Allocate buffer for encoded data */
buffer = mempool_alloc(mempool_default, required_size);
if (!buffer) {
DMSG("Failed to allocate buffer");
return NULLUsefulBufC;
}

/* Second encode: encode data */
QCBOREncode_Init(&context, (UsefulBuf){ buffer, required_size });
encode_func(&context, encode_args);
if (QCBOREncode_Finish(&context, &encoded_data) != QCBOR_SUCCESS) {
mempool_free(mempool_default, buffer);
return NULLUsefulBufC;
}

/* Verify the length of the encoded data */
if (encoded_data.len != required_size) {
DMSG("Unexpected length of encoded data");
mempool_free(mempool_default, buffer);
return NULLUsefulBufC;
}

return encoded_data;
}

static void encode_protected_header(QCBOREncodeContext *context)
{
QCBOREncode_OpenMap(context);
QCBOREncode_AddInt64ToMapN(context, COSE_HEADER_PARAM_ALG,
COSE_ALGORITHM_ES256);
QCBOREncode_CloseMap(context);
}

static void encode_protected_header_wrapper(QCBOREncodeContext *context,
void *__unused args)
{
encode_protected_header(context);
}

/*
* Format of to-be-signed bytes. This is defined in COSE (RFC 8152)
* section 4.4. It is the input to the hash.
*
* Sig_structure = [
* context : "Signature1",
* body_protected : empty_or_serialized_map,
* external_aad : bstr,
* payload : bstr
* ]
*
* body_protected refers to the protected parameters from the main
* COSE_Sign1 structure.
*/
static void encode_tbs_structure(QCBOREncodeContext *context,
UsefulBufC protected_header, UsefulBufC aad,
UsefulBufC payload)
{
QCBOREncode_OpenArray(context);
QCBOREncode_AddSZString(context, COSE_SIG_CONTEXT_STRING_SIGNATURE1);
QCBOREncode_AddBytes(context, protected_header);
QCBOREncode_AddBytes(context, aad);
QCBOREncode_AddBytes(context, payload);
QCBOREncode_CloseArray(context);
}

static void encode_tbs_structure_wrapper(QCBOREncodeContext *context,
void *args)
{
struct tbs_structure_args *tbs_args =
(struct tbs_structure_args *)args;

encode_tbs_structure(context, tbs_args->protected_header, tbs_args->aad,
tbs_args->payload);
}

static UsefulBufC build_protected_header(void)
{
return build_encoded_buffer(encode_protected_header_wrapper, NULL);
}

static UsefulBufC build_tbs_structure(UsefulBufC protected_header,
UsefulBufC aad, UsefulBufC payload)
{
struct tbs_structure_args args = {
.protected_header = protected_header,
.aad = aad,
.payload = payload,
};

return build_encoded_buffer(encode_tbs_structure_wrapper, &args);
}

static void encode_cose_evidence(QCBOREncodeContext *context,
UsefulBufC ubc_cbor_evidence)
{
UsefulBufC protected_header = { NULL, 0 };
UsefulBufC tbs_payload = { NULL, 0 };
uint8_t signature[64] = { };
size_t signature_len = 64;
UsefulBufC signature_payload = { signature, signature_len };

/* Add top level array for COSE_Sign1 */
QCBOREncode_AddTag(context, CBOR_TAG_COSE_SIGN1);
QCBOREncode_OpenArray(context);

/* Encode protected header */
protected_header = build_protected_header();
if (UsefulBuf_IsNULLC(protected_header)) {
DMSG("Failed to encode protected header payload");
return;
}

/* Add protected header */
QCBOREncode_AddBytes(context, protected_header);

/* Add unprotected header (empty map) */
QCBOREncode_OpenMap(context);
QCBOREncode_CloseMap(context);

/* Add the payload (evidence CBOR) */
QCBOREncode_AddBytes(context, ubc_cbor_evidence);

/* Encode "To Be Signed" payload */
tbs_payload = build_tbs_structure(protected_header, NULLUsefulBufC,
ubc_cbor_evidence);
if (UsefulBuf_IsNULLC(tbs_payload)) {
DMSG("Failed to encode to-be-signed payload");
mempool_free(mempool_default, (void *)protected_header.ptr);
return;
}

/* Calculate a signature and add the signature to payload */
if (sign_ecdsa_sha256(tbs_payload.ptr, tbs_payload.len, signature,
&signature_len) != TEE_SUCCESS) {
DMSG("Failed to sign payload");
mempool_free(mempool_default, (void *)protected_header.ptr);
mempool_free(mempool_default, (void *)tbs_payload.ptr);
return;
}

/* Add the signature */
QCBOREncode_AddBytes(context, signature_payload);

/* Close top level array for COSE_Sign1 */
QCBOREncode_CloseArray(context);
}

static void encode_cbor_evidence_wrapper(QCBOREncodeContext *context,
void *args)
{
struct cbor_evidence_args *evidence_args =
(struct cbor_evidence_args *)args;

encode_cbor_evidence(context, evidence_args->ubc_eat_profile,
evidence_args->psa_client_id,
evidence_args->psa_security_lifecycle,
evidence_args->ubc_psa_implementation_id,
evidence_args->ubc_measurement_type,
evidence_args->ubc_signer_id,
evidence_args->ubc_psa_instance_id,
evidence_args->ubc_psa_nonce,
evidence_args->ubc_measurement_value);
}

static void encode_cose_evidence_wrapper(QCBOREncodeContext *context,
void *args)
{
struct cose_evidence_args *cose_args =
(struct cose_evidence_args *)args;

encode_cose_evidence(context, cose_args->ubc_cbor_evidence);
}

static UsefulBufC
build_cbor_evidence(UsefulBufC ubc_eat_profile, int psa_client_id,
int psa_security_lifecycle,
UsefulBufC ubc_psa_implementation_id,
UsefulBufC ubc_measurement_type, UsefulBufC ubc_signer_id,
UsefulBufC ubc_psa_instance_id, UsefulBufC ubc_psa_nonce,
UsefulBufC ubc_measurement_value)
{
struct cbor_evidence_args args = {
.ubc_eat_profile = ubc_eat_profile,
.psa_client_id = psa_client_id,
.psa_security_lifecycle = psa_security_lifecycle,
.ubc_psa_implementation_id = ubc_psa_implementation_id,
.ubc_measurement_type = ubc_measurement_type,
.ubc_signer_id = ubc_signer_id,
.ubc_psa_instance_id = ubc_psa_instance_id,
.ubc_psa_nonce = ubc_psa_nonce,
.ubc_measurement_value = ubc_measurement_value,
};

return build_encoded_buffer(encode_cbor_evidence_wrapper, &args);
}

static UsefulBufC build_cose_evidence(UsefulBufC ubc_cbor_evidence)
{
struct cose_evidence_args args = {
.ubc_cbor_evidence = ubc_cbor_evidence,
};

return build_encoded_buffer(encode_cose_evidence_wrapper, &args);
}

UsefulBufC generate_cbor_evidence(const char *eat_profile,
int psa_client_id,
int psa_security_lifecycle,
const uint8_t *psa_implementation_id,
size_t psa_implementation_id_len,
const char *measurement_type,
const uint8_t *signer_id,
size_t signer_id_len,
const uint8_t *psa_instance_id,
size_t psa_instance_id_len,
const uint8_t *psa_nonce,
size_t psa_nonce_len,
const uint8_t *measurement_value,
size_t measurement_value_len)
{
/* prepare usefulbufs because qcbor only accepts them */
UsefulBufC ubc_eat_profile = UsefulBuf_FromSZ(eat_profile);
UsefulBufC ubc_psa_implementation_id = { psa_implementation_id,
psa_implementation_id_len };
UsefulBufC ubc_measurement_type = UsefulBuf_FromSZ(measurement_type);
UsefulBufC ubc_signer_id = { signer_id, signer_id_len };
UsefulBufC ubc_psa_instance_id = { psa_instance_id,
psa_instance_id_len };
UsefulBufC ubc_psa_nonce = { psa_nonce, psa_nonce_len };
UsefulBufC ubc_measurement_value = { measurement_value,
measurement_value_len };

return build_cbor_evidence(ubc_eat_profile,
psa_client_id,
psa_security_lifecycle,
ubc_psa_implementation_id,
ubc_measurement_type,
ubc_signer_id,
ubc_psa_instance_id,
ubc_psa_nonce,
ubc_measurement_value);
}

UsefulBufC generate_cose_evidence(UsefulBufC ubc_cbor_evidence)
{
return build_cose_evidence(ubc_cbor_evidence);
}
Loading

0 comments on commit b580089

Please sign in to comment.