Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 107 additions & 82 deletions README-j.md

Large diffs are not rendered by default.

966 changes: 108 additions & 858 deletions README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions attester/.dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
README.md
container-imx/secure-boot-out/
container-imx/yocto/
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ do_configure:append() {
cp ${PTA_SRC}/sign.h ${S}/core/pta/remote_attestation/
cp ${PTA_SRC}/base64.c ${S}/core/pta/remote_attestation/
cp ${PTA_SRC}/base64.h ${S}/core/pta/remote_attestation/
cp ${PTA_SRC}/ocotp.c ${S}/core/pta/remote_attestation/
cp ${PTA_SRC}/ocotp.h ${S}/core/pta/remote_attestation/
cp ${PTA_SRC}/sub.mk ${S}/core/pta/remote_attestation/

# Copy qcbor files
Expand Down
6 changes: 5 additions & 1 deletion attester/container/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ RUN cd ${OPTEE_DIR} \
&& repo sync -j$(nproc) \
&& cd ${OPTEE_DIR}/build \
&& make toolchains -j$(nproc) \
&& make check -j$(nproc)
&& sed -i 's/BR2_PACKAGE_OPTEE_RUST_EXAMPLES_EXT ?= y/BR2_PACKAGE_OPTEE_RUST_EXAMPLES_EXT ?= n/' \
${OPTEE_DIR}/build/common.mk \
&& sed -i 's/OPTEE_RUST_ENABLE ?= y/OPTEE_RUST_ENABLE ?= n/' \
${OPTEE_DIR}/build/common.mk \
&& make -j$(nproc)

RUN cd ${OPTEE_DIR}/optee_os/core/pta \
&& if ! grep -q "^subdirs-y += remote_attestation" sub.mk; then \
Expand Down
2 changes: 1 addition & 1 deletion attester/pta_remote_attestation/pta_remote_attestation.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*
* [in] memref[0] Nonce
* [out] memref[1] Output buffer
* [in] memref[2] Implementation ID
* [in] memref[2] Caller TA UUID (16 bytes)
* [in] memref[3] (optional) Serialized black key for signing
*
* Return codes:
Expand Down
65 changes: 45 additions & 20 deletions attester/pta_remote_attestation/remote_attestation/cbor.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ encode_evidence_to_cbor(const char *eat_profile, const int psa_client_id,
const 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,
const struct psa_sw_component *components,
size_t num_components, 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 mv_len, UsefulBuf cbor_evidence_buffer) {
size_t psa_nonce_len, UsefulBuf cbor_evidence_buffer) {
/* 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, mv_len};

QCBOREncodeContext encode_ctx;
QCBOREncode_Init(&encode_ctx, cbor_evidence_buffer);
Expand All @@ -44,14 +40,26 @@ encode_evidence_to_cbor(const char *eat_profile, const int psa_client_id,

/* Software Components */
QCBOREncode_OpenArrayInMapN(&encode_ctx, PSA_SW_COMPONENTS); /* [ */
QCBOREncode_OpenMap(&encode_ctx); /* { */
QCBOREncode_AddTextToMapN(&encode_ctx, PSA_SW_COMPONENT_MEASUREMENT_TYPE,
ubc_measurement_type);
QCBOREncode_AddBytesToMapN(&encode_ctx, PSA_SW_COMPONENT_MEASUREMENT_VALUE,
ubc_measurement_value);
QCBOREncode_AddBytesToMapN(&encode_ctx, PSA_SW_COMPONENT_SIGNER_ID,
ubc_signer_id);
QCBOREncode_CloseMap(&encode_ctx); /* } */
for (size_t i = 0; i < num_components; i++) {
const struct psa_sw_component *c = &components[i];
UsefulBufC ubc_measurement_value = {c->measurement_value,
c->measurement_value_len};
UsefulBufC ubc_signer_id = {c->signer_id, c->signer_id_len};

QCBOREncode_OpenMap(&encode_ctx); /* { */
QCBOREncode_AddTextToMapN(&encode_ctx,
PSA_SW_COMPONENT_MEASUREMENT_TYPE,
UsefulBuf_FromSZ(c->measurement_type));
QCBOREncode_AddBytesToMapN(&encode_ctx,
PSA_SW_COMPONENT_MEASUREMENT_VALUE,
ubc_measurement_value);
if (c->version)
QCBOREncode_AddTextToMapN(&encode_ctx, PSA_SW_COMPONENT_VERSION,
UsefulBuf_FromSZ(c->version));
QCBOREncode_AddBytesToMapN(&encode_ctx, PSA_SW_COMPONENT_SIGNER_ID,
ubc_signer_id);
QCBOREncode_CloseMap(&encode_ctx); /* } */
}
QCBOREncode_CloseArray(&encode_ctx); /* ] */

/* Nonce */
Expand Down Expand Up @@ -87,12 +95,22 @@ UsefulBufC generate_cose(UsefulBufC ubc_cbor_evidence,
QCBOREncode_AddTag(&cose_context, CBOR_TAG_COSE_SIGN1);
QCBOREncode_OpenArray(&cose_context);

/* Allocate work buffers on heap to avoid PTA stack overflow */
void *heap_prot = malloc(256);
void *heap_tbs = malloc(512);
if (!heap_prot || !heap_tbs) {
free(heap_prot);
free(heap_tbs);
return NULLUsefulBufC;
}

/* Encode protected header */
UsefulBuf_MAKE_STACK_UB(buffer_for_protected_parameter, 256);
UsefulBufC protected_parameter =
encode_protected_parameter(buffer_for_protected_parameter);
UsefulBuf buf_prot = {heap_prot, 256};
UsefulBufC protected_parameter = encode_protected_parameter(buf_prot);
if (UsefulBuf_IsNULLC(protected_parameter)) {
DMSG("Failed to encode protected header payload");
free(heap_prot);
free(heap_tbs);
return NULLUsefulBufC;
}

Expand All @@ -107,11 +125,13 @@ UsefulBufC generate_cose(UsefulBufC ubc_cbor_evidence,
QCBOREncode_AddBytes(&cose_context, ubc_cbor_evidence);

/* Encode "To Be Signed" payload */
UsefulBuf_MAKE_STACK_UB(buffer_for_tbs, 1024);
UsefulBuf buf_tbs = {heap_tbs, 512};
UsefulBufC tbs_payload = create_tbs(protected_parameter, NULLUsefulBufC,
ubc_cbor_evidence, buffer_for_tbs);
ubc_cbor_evidence, buf_tbs);
if (UsefulBuf_IsNULLC(tbs_payload)) {
DMSG("Failed to encode to-be-signed payload");
free(heap_prot);
free(heap_tbs);
return NULLUsefulBufC;
}

Expand All @@ -122,9 +142,14 @@ UsefulBufC generate_cose(UsefulBufC ubc_cbor_evidence,
&signature_len, serialized_black_key,
serialized_black_key_len) != TEE_SUCCESS) {
DMSG("Failed to sign payload");
free(heap_prot);
free(heap_tbs);
return NULLUsefulBufC;
}

free(heap_prot);
free(heap_tbs);

/* Add the signature */
UsefulBufC signature_payload = {signature, signature_len};
QCBOREncode_AddBytes(&cose_context, signature_payload);
Expand Down
17 changes: 13 additions & 4 deletions attester/pta_remote_attestation/remote_attestation/cbor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,25 @@
#define COSE_ALGORITHM_ES256 -7
#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1"

/* One PSA software component (an entry in the Software Components claim). */
struct psa_sw_component {
const char *measurement_type; /* claim 1 (text) */
const uint8_t *measurement_value; /* claim 2 (bytes) */
size_t measurement_value_len;
const char *version; /* claim 4 (text); NULL to omit */
const uint8_t *signer_id; /* claim 5 (bytes) */
size_t signer_id_len;
};

UsefulBufC
encode_evidence_to_cbor(const char *eat_profile, const int psa_client_id,
const 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,
const struct psa_sw_component *components,
size_t num_components, 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 mv_len, UsefulBuf cbor_evidence_buffer);
size_t psa_nonce_len, UsefulBuf cbor_evidence_buffer);

UsefulBufC generate_cose(UsefulBufC ubc_cbor_evidence,
UsefulBuf buffer_for_cose,
Expand Down
66 changes: 66 additions & 0 deletions attester/pta_remote_attestation/remote_attestation/hash.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <config.h>
#include <crypto/crypto.h>
#include <kernel/linker.h>
#include <kernel/user_access.h>
#include <kernel/user_mode_ctx.h>

Expand Down Expand Up @@ -130,3 +132,67 @@ TEE_Result get_hash_ta_memory(uint8_t *out, size_t out_sz)
ts_push_current_session(s);
return res;
}

/*
* Hash the OP-TEE OS (core) immutable memory: code (.text) and read-only
* data (.rodata). This is a runtime measurement of the trusted OS and
* mirrors the official OP-TEE attestation PTA (cmd_hash_tee_memory).
*
* Note: this is a self-measurement; its trustworthiness is ultimately
* rooted in secure boot (HAB/SRK verifying the OP-TEE image at load time).
*/
TEE_Result get_hash_tee_memory(uint8_t *out, size_t out_sz)
{
TEE_Result res = TEE_SUCCESS;
void *ctx = NULL;

if (out_sz < TEE_SHA256_HASH_SIZE)
return TEE_ERROR_SHORT_BUFFER;

res = crypto_hash_alloc_ctx(&ctx, TEE_ALG_SHA256);
if (res)
return res;

res = crypto_hash_init(ctx);
if (res)
goto out;

res = crypto_hash_update(ctx, __text_start,
__text_data_start - __text_start);
if (res)
goto out;
res = crypto_hash_update(ctx, __text_data_end,
__text_end - __text_data_end);
if (res)
goto out;
if (IS_ENABLED(CFG_WITH_PAGER)) {
res = crypto_hash_update(ctx, __text_init_start,
__text_init_end - __text_init_start);
if (res)
goto out;
res = crypto_hash_update(ctx, __text_pageable_start,
__text_pageable_end - __text_pageable_start);
if (res)
goto out;
}
res = crypto_hash_update(ctx, __rodata_start,
__rodata_end - __rodata_start);
if (res)
goto out;
if (IS_ENABLED(CFG_WITH_PAGER)) {
res = crypto_hash_update(ctx, __rodata_init_start,
__rodata_init_end - __rodata_init_start);
if (res)
goto out;
res = crypto_hash_update(ctx, __rodata_pageable_start,
__rodata_pageable_end -
__rodata_pageable_start);
if (res)
goto out;
}

res = crypto_hash_final(ctx, out, TEE_SHA256_HASH_SIZE);
out:
crypto_hash_free_ctx(ctx);
return res;
}
3 changes: 3 additions & 0 deletions attester/pta_remote_attestation/remote_attestation/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@

TEE_Result get_hash_ta_memory(uint8_t *out, size_t out_sz);

/* Hash the OP-TEE OS (core) .text + .rodata into @out (>= 32 bytes). */
TEE_Result get_hash_tee_memory(uint8_t *out, size_t out_sz);

#endif /* PTA_REMOTE_ATTESTATION_TA_HASH_H */
106 changes: 106 additions & 0 deletions attester/pta_remote_attestation/remote_attestation/ocotp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Read SRK hash and security configuration from i.MX8MP OCOTP fuses
* for PSA Attestation Token signer-id and lifecycle claims.
*
* Uses the OP-TEE imx_ocotp driver which properly handles CCM clock
* gating — direct io_read32() on OCOTP registers hangs when the
* OCOTP clock is gated by Linux.
*/

#include "ocotp.h"

#include <drivers/imx_ocotp.h>
#include <string.h>
#include <trace.h>

static bool srk_is_zero(const uint8_t *buf, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
if (buf[i])
return false;
}
return true;
}

/*
* Read SRK hash (8 x 32-bit fuse words = 256 bits) from OCOTP.
* Banks 6-7, words 0-3 each, stored as big-endian byte array.
*/
TEE_Result ocotp_read_srk_hash(uint8_t signer_id[OCOTP_SRK_SIZE])
{
uint32_t bank, word;
size_t idx = 0;

for (bank = OCOTP_SRK_BANK_START; bank <= OCOTP_SRK_BANK_END; bank++) {
for (word = 0; word < 4; word++) {
uint32_t val = 0;
TEE_Result res = imx_ocotp_read(bank, word, &val);

if (res != TEE_SUCCESS) {
EMSG("imx_ocotp_read(bank=%u, word=%u) failed: 0x%x",
bank, word, res);
return res;
}
/* Store as big-endian */
signer_id[idx++] = (uint8_t)(val >> 24);
signer_id[idx++] = (uint8_t)(val >> 16);
signer_id[idx++] = (uint8_t)(val >> 8);
signer_id[idx++] = (uint8_t)(val);
}
}

DMSG("OCOTP SRK hash:");
DHEXDUMP(signer_id, OCOTP_SRK_SIZE);

return TEE_SUCCESS;
}

/*
* Determine PSA lifecycle state from OCOTP fuses:
*
* SRK fuses SEC_CONFIG Lifecycle
* all-zero Open 0x1000 (ASSEMBLY_AND_TEST)
* all-zero Closed 0x1000 (ASSEMBLY_AND_TEST, abnormal)
* written Open 0x2000 (ROT_PROVISIONING)
* written Closed 0x3000 (SECURED)
*/
TEE_Result ocotp_get_lifecycle(uint32_t *lifecycle)
{
uint8_t srk[OCOTP_SRK_SIZE] = {0};
uint32_t sec_cfg = 0;
bool srk_empty;
bool closed;
TEE_Result res;

if (!lifecycle)
return TEE_ERROR_GENERIC;

res = ocotp_read_srk_hash(srk);
if (res != TEE_SUCCESS)
return res;

srk_empty = srk_is_zero(srk, sizeof(srk));

res = imx_ocotp_read(OCOTP_SEC_CONFIG_BANK, OCOTP_SEC_CONFIG_WORD,
&sec_cfg);
if (res != TEE_SUCCESS) {
EMSG("imx_ocotp_read(SEC_CONFIG) failed: 0x%x", res);
return res;
}
closed = (sec_cfg & OCOTP_SEC_CONFIG_BIT) != 0;

DMSG("OCOTP SEC_CONFIG=0x%08" PRIx32 " closed=%d srk_empty=%d",
sec_cfg, closed, srk_empty);

if (srk_empty) {
*lifecycle = PSA_LIFECYCLE_ASSEMBLY_AND_TEST;
} else if (!closed) {
*lifecycle = PSA_LIFECYCLE_ROT_PROVISIONING;
} else {
*lifecycle = PSA_LIFECYCLE_SECURED;
}

return TEE_SUCCESS;
}
23 changes: 23 additions & 0 deletions attester/pta_remote_attestation/remote_attestation/ocotp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: BSD-2-Clause */
#ifndef PTA_REMOTE_ATTESTATION_OCOTP_H
#define PTA_REMOTE_ATTESTATION_OCOTP_H

#include <tee_api_types.h>
#include <stdint.h>

#define OCOTP_SRK_BANK_START 6
#define OCOTP_SRK_BANK_END 7
#define OCOTP_SRK_SIZE 32

#define OCOTP_SEC_CONFIG_BANK 1
#define OCOTP_SEC_CONFIG_WORD 3
#define OCOTP_SEC_CONFIG_BIT (1U << 25)

#define PSA_LIFECYCLE_ASSEMBLY_AND_TEST 0x1000
#define PSA_LIFECYCLE_ROT_PROVISIONING 0x2000
#define PSA_LIFECYCLE_SECURED 0x3000

TEE_Result ocotp_read_srk_hash(uint8_t signer_id[OCOTP_SRK_SIZE]);
TEE_Result ocotp_get_lifecycle(uint32_t *lifecycle);

#endif /* PTA_REMOTE_ATTESTATION_OCOTP_H */
Loading