Add wolfCOSE DICE attestation

pull/850/head
Aidan Garske 2026-08-05 11:16:40 -07:00 committed by Daniele Lacamera
parent 9232a95033
commit db0801b9b4
13 changed files with 742 additions and 409 deletions

View File

@ -89,4 +89,5 @@ jobs:
WOLFBOOT_LIB_WOLFPKCS11="$(realpath ../external-libs/wolfPKCS11)" \
WOLFBOOT_LIB_WOLFPSA="$(realpath ../external-libs/wolfPSA)" \
WOLFBOOT_LIB_WOLFTPM="$(realpath ../external-libs/wolfTPM)" \
WOLFBOOT_LIB_WOLFHSM="$(realpath ../external-libs/wolfHSM)"
WOLFBOOT_LIB_WOLFHSM="$(realpath ../external-libs/wolfHSM)" \
WOLFBOOT_LIB_WOLFCOSE="$(realpath ../external-libs/wolfCOSE)"

View File

@ -101,10 +101,16 @@ jobs:
- name: Clean and build test with DICE attestation (stm32h5)
run: |
set -o pipefail
make clean distclean
cp config/examples/stm32h5-tz-psa.config .config
make
m33mu wolfboot.bin test-app/image_v1_signed.bin:0x60000 --uart-stdout --expect-bkpt 0x7f --timeout 600
m33mu wolfboot.bin test-app/image_v1_signed.bin:0x60000 --uart-stdout --expect-bkpt 0x7f --timeout 600 \
| tee /tmp/m33mu-dice.log
grep -q "IAT size match: challenge=64 token=" /tmp/m33mu-dice.log
grep -q "PSA boot attestation: success" /tmp/m33mu-dice.log
grep -q "\\[BKPT\\] imm=0x7f" /tmp/m33mu-dice.log
grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-dice.log
- name: Clean and build test with fwTPM (stm32h5)
run: |

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "lib/wolfHAL"]
path = lib/wolfHAL
url = https://github.com/wolfSSL/wolfHAL.git
[submodule "lib/wolfCOSE"]
path = lib/wolfCOSE
url = https://github.com/wolfSSL/wolfCOSE.git

View File

@ -566,6 +566,10 @@ endif()
if(DEFINED WOLFCRYPT_TZ_PSA AND NOT WOLFCRYPT_TZ_PSA STREQUAL "0")
list(APPEND WOLFBOOT_SOURCES "src/dice/dice.c")
list(APPEND WOLFBOOT_SOURCES "lib/wolfCOSE/src/wolfcose.c")
list(APPEND WOLFBOOT_SOURCES "lib/wolfCOSE/src/wolfcose_cbor.c")
list(APPEND WOLFBOOT_INCLUDE_DIRS ${WOLFBOOT_ROOT}/lib/wolfCOSE/include)
list(APPEND WOLFBOOT_DEFS WOLFCOSE_LEAN WOLFCOSE_ENABLE_EXT_SIGN)
endif()
# build bin-assemble tool Windows

View File

@ -191,6 +191,7 @@ WOLFBOOT_LIB_WOLFTPM?=lib/wolfTPM
WOLFBOOT_LIB_WOLFPKCS11?=lib/wolfPKCS11
WOLFBOOT_LIB_WOLFPSA?=lib/wolfPSA
WOLFBOOT_LIB_WOLFHSM?=lib/wolfHSM
WOLFBOOT_LIB_WOLFCOSE?=lib/wolfCOSE
# Convert to absolute paths using abspath function
WOLFBOOT_LIB_WOLFSSL:=$(abspath $(WOLFBOOT_LIB_WOLFSSL))
@ -198,6 +199,7 @@ WOLFBOOT_LIB_WOLFTPM:=$(abspath $(WOLFBOOT_LIB_WOLFTPM))
WOLFBOOT_LIB_WOLFPKCS11:=$(abspath $(WOLFBOOT_LIB_WOLFPKCS11))
WOLFBOOT_LIB_WOLFPSA:=$(abspath $(WOLFBOOT_LIB_WOLFPSA))
WOLFBOOT_LIB_WOLFHSM:=$(abspath $(WOLFBOOT_LIB_WOLFHSM))
WOLFBOOT_LIB_WOLFCOSE:=$(abspath $(WOLFBOOT_LIB_WOLFCOSE))
# Export variables so they are available to sub-makefiles
export WOLFBOOT_LIB_WOLFSSL
@ -205,6 +207,7 @@ export WOLFBOOT_LIB_WOLFTPM
export WOLFBOOT_LIB_WOLFPKCS11
export WOLFBOOT_LIB_WOLFPSA
export WOLFBOOT_LIB_WOLFHSM
export WOLFBOOT_LIB_WOLFCOSE
## Architecture/CPU configuration
include arch.mk

View File

@ -22,15 +22,24 @@ an attestation key derived by DICE or supplied as a provisioned IAK.
The implementation lives under `src/dice/` and is shared across targets. The
service is invoked through the PSA Initial Attestation API and builds the
COSE_Sign1 token using a minimal CBOR encoder.
EAT claim set with wolfCOSE's CBOR API. wolfCOSE then wraps and signs the
payload as an untagged COSE_Sign1 object using ES256. Hardware DICE targets use
wolfCOSE's external-signer callback so the attestation private key never leaves
the platform security boundary. Token-size queries use wolfCOSE's prediction
API and do not derive a key, advance the CDI, or invoke a signer.
- Claim construction and COSE_Sign1 encoding: `src/dice/dice.c`.
- PSA Initial Attestation service dispatch: `src/arm_tee_psa_ipc.c`.
- NSC wrappers for the PSA Initial Attestation API: `zephyr/src/arm_tee_attest_api.c`.
Measured boot claims reuse the image hashing pipeline already used by
wolfBoot to validate images. Component claims include a measurement type,
measurement value, and a description string.
wolfBoot to validate images. Each software-component map includes measurement
type (key 1), measurement value (key 2), signer ID (key 5), and measurement
description (key 6). The boot-image signer ID is the authenticated `HDR_PUBKEY`
hash from its wolfBoot image header. The running wolfBoot image has no retained
signing manifest, so its signer ID is the zero-filled unknown-signer value used
by TF-M for the same case. Unsigned `WOLFBOOT_NO_SIGN` boot images also use the
unknown-signer value because no signing authority exists.
## Keying model

View File

@ -167,10 +167,11 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
return 0;
}
#define STM32H5_BSEC_BASE 0x46009000u
#define STM32H5_BSEC_UID0 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x14))
#define STM32H5_BSEC_UID1 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x18))
#define STM32H5_BSEC_UID2 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x1C))
/* STM32H5 96-bit unique device ID, factory-programmed (RM0481). */
#define STM32H5_UID_BASE 0x08FFF800u
#define STM32H5_UID0 (*(volatile uint32_t *)(STM32H5_UID_BASE + 0x0u))
#define STM32H5_UID1 (*(volatile uint32_t *)(STM32H5_UID_BASE + 0x4u))
#define STM32H5_UID2 (*(volatile uint32_t *)(STM32H5_UID_BASE + 0x8u))
#ifdef WOLFBOOT_UDS_OBKEYS
__attribute__((weak)) int stm32h5_obkeys_read_uds(uint8_t *out, size_t out_len)
@ -205,18 +206,18 @@ static int uds_from_uid(uint8_t *out, size_t out_len)
#endif
size_t copy_len;
uid[0] = (uint8_t)(STM32H5_BSEC_UID0 >> 0);
uid[1] = (uint8_t)(STM32H5_BSEC_UID0 >> 8);
uid[2] = (uint8_t)(STM32H5_BSEC_UID0 >> 16);
uid[3] = (uint8_t)(STM32H5_BSEC_UID0 >> 24);
uid[4] = (uint8_t)(STM32H5_BSEC_UID1 >> 0);
uid[5] = (uint8_t)(STM32H5_BSEC_UID1 >> 8);
uid[6] = (uint8_t)(STM32H5_BSEC_UID1 >> 16);
uid[7] = (uint8_t)(STM32H5_BSEC_UID1 >> 24);
uid[8] = (uint8_t)(STM32H5_BSEC_UID2 >> 0);
uid[9] = (uint8_t)(STM32H5_BSEC_UID2 >> 8);
uid[10] = (uint8_t)(STM32H5_BSEC_UID2 >> 16);
uid[11] = (uint8_t)(STM32H5_BSEC_UID2 >> 24);
uid[0] = (uint8_t)(STM32H5_UID0 >> 0);
uid[1] = (uint8_t)(STM32H5_UID0 >> 8);
uid[2] = (uint8_t)(STM32H5_UID0 >> 16);
uid[3] = (uint8_t)(STM32H5_UID0 >> 24);
uid[4] = (uint8_t)(STM32H5_UID1 >> 0);
uid[5] = (uint8_t)(STM32H5_UID1 >> 8);
uid[6] = (uint8_t)(STM32H5_UID1 >> 16);
uid[7] = (uint8_t)(STM32H5_UID1 >> 24);
uid[8] = (uint8_t)(STM32H5_UID2 >> 0);
uid[9] = (uint8_t)(STM32H5_UID2 >> 8);
uid[10] = (uint8_t)(STM32H5_UID2 >> 16);
uid[11] = (uint8_t)(STM32H5_UID2 >> 24);
#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&hash);

1
lib/wolfCOSE 160000

@ -0,0 +1 @@
Subproject commit 588232e6f2213133b48976f5cf3153b21fc7199c

View File

@ -1139,6 +1139,8 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1)
CFLAGS+=-DWOLFSSL_PSA_ENGINE
CFLAGS+=-DWOLFPSA_CUSTOM_STORE
CFLAGS+=-DNO_DES3 -DNO_DES3_TLS_SUITES
CFLAGS+=-I$(WOLFBOOT_LIB_WOLFCOSE)/include
CFLAGS+=-DWOLFCOSE_LEAN -DWOLFCOSE_ENABLE_EXT_SIGN
WOLFPSA_CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA)
WOLFPSA_CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA)/wolfpsa
ifeq ($(USE_CLANG),1)
@ -1152,6 +1154,8 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1)
WOLFCRYPT_OBJS+=src/psa_store.o
WOLFCRYPT_OBJS+=src/arm_tee_psa_veneer.o
WOLFCRYPT_OBJS+=src/arm_tee_psa_ipc.o
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFCOSE)/src/wolfcose.o
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFCOSE)/src/wolfcose_cbor.o
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/pwdbased.o
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/dh.o

View File

@ -42,6 +42,8 @@
#include <wolfssl/wolfcrypt/sha3.h>
#endif
#include <wolfcose/wolfcose.h>
#ifndef PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32
#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 (32u)
#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 (48u)
@ -59,7 +61,6 @@
#define WOLFBOOT_DICE_CDI_LEN 32
#define WOLFBOOT_DICE_KEY_LEN 32
#define WOLFBOOT_DICE_UEID_LEN 33
#define WOLFBOOT_DICE_SIG_LEN 64
#define WOLFBOOT_DICE_SUCCESS 0
#define WOLFBOOT_DICE_ERR_INVALID_ARGUMENT -1
@ -76,19 +77,17 @@ static NOINLINEFUNCTION void wolfboot_dice_zeroize(void *ptr, size_t len)
}
}
#define COSE_LABEL_ALG 1
#define COSE_ALG_ES256 (-7)
#define EAT_CLAIM_NONCE 10
#define EAT_CLAIM_UEID 256
#define PSA_IAT_CLAIM_IMPLEMENTATION_ID 2396
#define PSA_IAT_CLAIM_LIFECYCLE 2398
#define PSA_IAT_CLAIM_LIFECYCLE 2395
#define PSA_IAT_CLAIM_SW_COMPONENTS 2399
#define PSA_SW_COMPONENT_MEASUREMENT_TYPE 1
#define PSA_SW_COMPONENT_MEASUREMENT_VALUE 2
#define PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION 5
#define PSA_SW_COMPONENT_SIGNER_ID 5
#define PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION 6
#define WOLFBOOT_UEID_TYPE_RANDOM 0x01
@ -115,6 +114,8 @@ struct wolfboot_dice_component {
size_t measurement_desc_len;
uint8_t measurement[WOLFBOOT_SHA_DIGEST_SIZE];
size_t measurement_len;
uint8_t signer_id[WOLFBOOT_SHA_DIGEST_SIZE];
size_t signer_id_len;
};
struct wolfboot_dice_claims {
@ -130,145 +131,6 @@ struct wolfboot_dice_claims {
size_t component_count;
};
struct wolfboot_cbor_writer {
uint8_t *buf;
size_t size;
size_t offset;
int error;
};
static void wolfboot_cbor_init(struct wolfboot_cbor_writer *w,
uint8_t *buf,
size_t size)
{
w->buf = buf;
w->size = size;
w->offset = 0;
w->error = 0;
}
static void wolfboot_cbor_reserve(struct wolfboot_cbor_writer *w, size_t len)
{
if (w->error != 0) {
return;
}
if (w->buf == NULL || w->size == 0) {
w->offset += len;
return;
}
if (w->offset + len > w->size) {
w->error = WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL;
return;
}
w->offset += len;
}
static void wolfboot_cbor_put_type_val(struct wolfboot_cbor_writer *w,
uint8_t major,
uint64_t val)
{
uint8_t tmp[9];
size_t len = 0;
if (val <= 23) {
tmp[len++] = (uint8_t)((major << 5) | (uint8_t)val);
}
else if (val <= 0xFF) {
tmp[len++] = (uint8_t)((major << 5) | 24);
tmp[len++] = (uint8_t)val;
}
else if (val <= 0xFFFF) {
tmp[len++] = (uint8_t)((major << 5) | 25);
tmp[len++] = (uint8_t)(val >> 8);
tmp[len++] = (uint8_t)(val & 0xFF);
}
else if (val <= 0xFFFFFFFFu) {
tmp[len++] = (uint8_t)((major << 5) | 26);
tmp[len++] = (uint8_t)(val >> 24);
tmp[len++] = (uint8_t)(val >> 16);
tmp[len++] = (uint8_t)(val >> 8);
tmp[len++] = (uint8_t)(val & 0xFF);
}
else {
tmp[len++] = (uint8_t)((major << 5) | 27);
tmp[len++] = (uint8_t)(val >> 56);
tmp[len++] = (uint8_t)(val >> 48);
tmp[len++] = (uint8_t)(val >> 40);
tmp[len++] = (uint8_t)(val >> 32);
tmp[len++] = (uint8_t)(val >> 24);
tmp[len++] = (uint8_t)(val >> 16);
tmp[len++] = (uint8_t)(val >> 8);
tmp[len++] = (uint8_t)(val & 0xFF);
}
wolfboot_cbor_reserve(w, len);
if (w->error != 0) {
return;
}
if (w->buf == NULL || w->size == 0) {
return;
}
XMEMCPY(w->buf + (w->offset - len), tmp, len);
}
static void wolfboot_cbor_put_uint(struct wolfboot_cbor_writer *w, uint64_t val)
{
wolfboot_cbor_put_type_val(w, 0, val);
}
static void wolfboot_cbor_put_int(struct wolfboot_cbor_writer *w, int64_t val)
{
if (val >= 0) {
wolfboot_cbor_put_uint(w, (uint64_t)val);
}
else {
uint64_t n = (uint64_t)(-1 - val);
wolfboot_cbor_put_type_val(w, 1, n);
}
}
static void wolfboot_cbor_put_bstr(struct wolfboot_cbor_writer *w,
const uint8_t *data,
size_t len)
{
wolfboot_cbor_put_type_val(w, 2, len);
wolfboot_cbor_reserve(w, len);
if (w->error != 0) {
return;
}
if (w->buf == NULL || w->size == 0) {
return;
}
XMEMCPY(w->buf + (w->offset - len), data, len);
}
static void wolfboot_cbor_put_tstr(struct wolfboot_cbor_writer *w,
const char *data,
size_t len)
{
wolfboot_cbor_put_type_val(w, 3, len);
wolfboot_cbor_reserve(w, len);
if (w->error != 0) {
return;
}
if (w->buf == NULL || w->size == 0) {
return;
}
XMEMCPY(w->buf + (w->offset - len), data, len);
}
static void wolfboot_cbor_put_array_start(struct wolfboot_cbor_writer *w,
size_t count)
{
wolfboot_cbor_put_type_val(w, 4, count);
}
static void wolfboot_cbor_put_map_start(struct wolfboot_cbor_writer *w,
size_t count)
{
wolfboot_cbor_put_type_val(w, 5, count);
}
static int wolfboot_hash_region(uintptr_t address, uint32_t size, uint8_t *out)
{
#if defined(WOLFBOOT_HASH_SHA256)
@ -336,13 +198,25 @@ static int wolfboot_hash_region(uintptr_t address, uint32_t size, uint8_t *out)
return ret;
}
static int wolfboot_get_boot_image_hash(uint8_t *out, size_t *out_len)
static int wolfboot_get_boot_image_claims(uint8_t *measurement,
size_t *measurement_len,
uint8_t *signer_id,
size_t *signer_id_len)
{
struct wolfBoot_image img;
uint8_t *hash_ptr = NULL;
#ifndef WOLFBOOT_NO_SIGN
uint8_t *signer_ptr = NULL;
#endif
uint16_t hash_len = 0;
#ifndef WOLFBOOT_NO_SIGN
uint16_t signer_len = 0;
#endif
if (out == NULL || out_len == NULL || *out_len < WOLFBOOT_SHA_DIGEST_SIZE) {
if (measurement == NULL || measurement_len == NULL || signer_id == NULL ||
signer_id_len == NULL ||
*measurement_len < WOLFBOOT_SHA_DIGEST_SIZE ||
*signer_id_len < WOLFBOOT_SHA_DIGEST_SIZE) {
return -1;
}
@ -355,8 +229,19 @@ static int wolfboot_get_boot_image_hash(uint8_t *out, size_t *out_len)
return -1;
}
XMEMCPY(out, hash_ptr, hash_len);
*out_len = hash_len;
XMEMCPY(measurement, hash_ptr, hash_len);
*measurement_len = hash_len;
#ifdef WOLFBOOT_NO_SIGN
XMEMSET(signer_id, 0, WOLFBOOT_SHA_DIGEST_SIZE);
*signer_id_len = WOLFBOOT_SHA_DIGEST_SIZE;
#else
signer_len = wolfBoot_get_header(&img, HDR_PUBKEY, &signer_ptr);
if (signer_len != WOLFBOOT_SHA_DIGEST_SIZE || signer_ptr == NULL) {
return -1;
}
XMEMCPY(signer_id, signer_ptr, signer_len);
*signer_id_len = signer_len;
#endif
return 0;
}
@ -527,6 +412,8 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims)
size_t wb_hash_len = sizeof(wb_hash);
uint8_t boot_hash[WOLFBOOT_SHA_DIGEST_SIZE];
size_t boot_hash_len = sizeof(boot_hash);
uint8_t boot_signer_id[WOLFBOOT_SHA_DIGEST_SIZE];
size_t boot_signer_id_len = sizeof(boot_signer_id);
XMEMSET(claims, 0, sizeof(*claims));
@ -569,35 +456,53 @@ static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims)
claims->has_lifecycle = 1;
}
if (wolfboot_get_wolfboot_hash(wb_hash, &wb_hash_len) == 0) {
claims->components[claims->component_count].measurement_type =
WOLFBOOT_MEASUREMENT_HASH_NAME;
claims->components[claims->component_count].measurement_type_len =
XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME);
claims->components[claims->component_count].measurement_desc =
WOLFBOOT_DICE_COMPONENT_WOLFBOOT;
claims->components[claims->component_count].measurement_desc_len =
XSTRLEN(WOLFBOOT_DICE_COMPONENT_WOLFBOOT);
XMEMCPY(claims->components[claims->component_count].measurement,
wb_hash, wb_hash_len);
claims->components[claims->component_count].measurement_len = wb_hash_len;
claims->component_count++;
/* A measurement that silently vanishes leaves a token a verifier cannot
* tell from one for a device with nothing to measure. */
if (wolfboot_get_wolfboot_hash(wb_hash, &wb_hash_len) != 0) {
#ifndef WOLFBOOT_DICE_HW
wc_ForceZero(uds, sizeof(uds));
#endif
return WOLFBOOT_DICE_ERR_HW;
}
claims->components[claims->component_count].measurement_type =
WOLFBOOT_MEASUREMENT_HASH_NAME;
claims->components[claims->component_count].measurement_type_len =
XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME);
claims->components[claims->component_count].measurement_desc =
WOLFBOOT_DICE_COMPONENT_WOLFBOOT;
claims->components[claims->component_count].measurement_desc_len =
XSTRLEN(WOLFBOOT_DICE_COMPONENT_WOLFBOOT);
XMEMCPY(claims->components[claims->component_count].measurement,
wb_hash, wb_hash_len);
claims->components[claims->component_count].measurement_len = wb_hash_len;
claims->components[claims->component_count].signer_id_len =
WOLFBOOT_SHA_DIGEST_SIZE;
claims->component_count++;
if (wolfboot_get_boot_image_hash(boot_hash, &boot_hash_len) == 0) {
claims->components[claims->component_count].measurement_type =
WOLFBOOT_MEASUREMENT_HASH_NAME;
claims->components[claims->component_count].measurement_type_len =
XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME);
claims->components[claims->component_count].measurement_desc =
WOLFBOOT_DICE_COMPONENT_BOOTIMAGE;
claims->components[claims->component_count].measurement_desc_len =
XSTRLEN(WOLFBOOT_DICE_COMPONENT_BOOTIMAGE);
XMEMCPY(claims->components[claims->component_count].measurement,
boot_hash, boot_hash_len);
claims->components[claims->component_count].measurement_len = boot_hash_len;
claims->component_count++;
if (wolfboot_get_boot_image_claims(boot_hash, &boot_hash_len,
boot_signer_id,
&boot_signer_id_len) != 0) {
#ifndef WOLFBOOT_DICE_HW
wc_ForceZero(uds, sizeof(uds));
#endif
return WOLFBOOT_DICE_ERR_HW;
}
claims->components[claims->component_count].measurement_type =
WOLFBOOT_MEASUREMENT_HASH_NAME;
claims->components[claims->component_count].measurement_type_len =
XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME);
claims->components[claims->component_count].measurement_desc =
WOLFBOOT_DICE_COMPONENT_BOOTIMAGE;
claims->components[claims->component_count].measurement_desc_len =
XSTRLEN(WOLFBOOT_DICE_COMPONENT_BOOTIMAGE);
XMEMCPY(claims->components[claims->component_count].measurement,
boot_hash, boot_hash_len);
claims->components[claims->component_count].measurement_len = boot_hash_len;
XMEMCPY(claims->components[claims->component_count].signer_id,
boot_signer_id, boot_signer_id_len);
claims->components[claims->component_count].signer_id_len =
boot_signer_id_len;
claims->component_count++;
#ifndef WOLFBOOT_DICE_HW
wc_ForceZero(uds, sizeof(uds));
#endif
@ -750,6 +655,11 @@ static int wolfboot_attest_get_private_key(ecc_key *key,
key, ECC_SECP256R1) != 0) {
goto cleanup;
}
/* Import leaves the key ECC_PRIVATEKEY_ONLY; wolfCOSE treats a key as
* sign-capable only once the public point is present. */
if (wc_ecc_make_pub(key, NULL) != 0) {
goto cleanup;
}
ret = 0;
cleanup:
@ -773,9 +683,10 @@ static int wolfboot_dice_encode_payload(uint8_t *buf,
const struct wolfboot_dice_claims *claims,
size_t *payload_len)
{
struct wolfboot_cbor_writer w;
WOLFCOSE_CBOR_CTX ctx;
size_t map_count = 2;
size_t i;
int ret;
if (claims->implementation_id_len > 0) {
map_count++;
@ -787,195 +698,163 @@ static int wolfboot_dice_encode_payload(uint8_t *buf,
map_count++;
}
wolfboot_cbor_init(&w, buf, buf_len);
wolfboot_cbor_put_map_start(&w, map_count);
XMEMSET(&ctx, 0, sizeof(ctx));
ctx.buf = buf;
ctx.bufSz = buf_len;
wolfboot_cbor_put_int(&w, EAT_CLAIM_NONCE);
wolfboot_cbor_put_bstr(&w, claims->challenge, claims->challenge_len);
wolfboot_cbor_put_int(&w, EAT_CLAIM_UEID);
wolfboot_cbor_put_bstr(&w, claims->ueid, claims->ueid_len);
if (claims->implementation_id_len > 0) {
wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_IMPLEMENTATION_ID);
wolfboot_cbor_put_bstr(&w,
claims->implementation_id,
claims->implementation_id_len);
ret = wc_CBOR_EncodeMapStart(&ctx, map_count);
if (ret == 0) {
ret = wc_CBOR_EncodeInt(&ctx, EAT_CLAIM_NONCE);
}
if (ret == 0) {
ret = wc_CBOR_EncodeBstr(&ctx, claims->challenge, claims->challenge_len);
}
if (ret == 0) {
ret = wc_CBOR_EncodeInt(&ctx, EAT_CLAIM_UEID);
}
if (ret == 0) {
ret = wc_CBOR_EncodeBstr(&ctx, claims->ueid, claims->ueid_len);
}
if (claims->has_lifecycle) {
wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_LIFECYCLE);
wolfboot_cbor_put_uint(&w, claims->lifecycle);
}
if (claims->component_count > 0) {
wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_SW_COMPONENTS);
wolfboot_cbor_put_array_start(&w, claims->component_count);
for (i = 0; i < claims->component_count; i++) {
wolfboot_cbor_put_map_start(&w, 3);
wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_TYPE);
wolfboot_cbor_put_tstr(&w,
claims->components[i].measurement_type,
claims->components[i].measurement_type_len);
wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_VALUE);
wolfboot_cbor_put_bstr(&w,
claims->components[i].measurement,
claims->components[i].measurement_len);
wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION);
wolfboot_cbor_put_tstr(&w,
claims->components[i].measurement_desc,
claims->components[i].measurement_desc_len);
if ((ret == 0) && (claims->implementation_id_len > 0)) {
ret = wc_CBOR_EncodeInt(&ctx, PSA_IAT_CLAIM_IMPLEMENTATION_ID);
if (ret == 0) {
ret = wc_CBOR_EncodeBstr(&ctx, claims->implementation_id,
claims->implementation_id_len);
}
}
if (w.error != 0) {
return w.error;
if ((ret == 0) && claims->has_lifecycle) {
ret = wc_CBOR_EncodeInt(&ctx, PSA_IAT_CLAIM_LIFECYCLE);
if (ret == 0) {
ret = wc_CBOR_EncodeUint(&ctx, claims->lifecycle);
}
}
*payload_len = w.offset;
if ((ret == 0) && (claims->component_count > 0)) {
ret = wc_CBOR_EncodeInt(&ctx, PSA_IAT_CLAIM_SW_COMPONENTS);
if (ret == 0) {
ret = wc_CBOR_EncodeArrayStart(&ctx, claims->component_count);
}
for (i = 0; (ret == 0) && (i < claims->component_count); i++) {
ret = wc_CBOR_EncodeMapStart(&ctx, 4);
if (ret == 0) {
ret = wc_CBOR_EncodeUint(&ctx,
PSA_SW_COMPONENT_MEASUREMENT_TYPE);
}
if (ret == 0) {
ret = wc_CBOR_EncodeTstr(&ctx,
(const uint8_t *)claims->components[i].measurement_type,
claims->components[i].measurement_type_len);
}
if (ret == 0) {
ret = wc_CBOR_EncodeUint(&ctx,
PSA_SW_COMPONENT_MEASUREMENT_VALUE);
}
if (ret == 0) {
ret = wc_CBOR_EncodeBstr(&ctx,
claims->components[i].measurement,
claims->components[i].measurement_len);
}
if (ret == 0) {
ret = wc_CBOR_EncodeUint(&ctx, PSA_SW_COMPONENT_SIGNER_ID);
}
if (ret == 0) {
ret = wc_CBOR_EncodeBstr(&ctx,
claims->components[i].signer_id,
claims->components[i].signer_id_len);
}
if (ret == 0) {
ret = wc_CBOR_EncodeUint(&ctx,
PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION);
}
if (ret == 0) {
ret = wc_CBOR_EncodeTstr(&ctx,
(const uint8_t *)claims->components[i].measurement_desc,
claims->components[i].measurement_desc_len);
}
}
}
if (ret != 0) {
return WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL;
}
*payload_len = ctx.idx;
return 0;
}
static int wolfboot_dice_encode_protected(uint8_t *buf,
size_t buf_len,
size_t *prot_len)
{
struct wolfboot_cbor_writer w;
wolfboot_cbor_init(&w, buf, buf_len);
wolfboot_cbor_put_map_start(&w, 1);
wolfboot_cbor_put_uint(&w, COSE_LABEL_ALG);
wolfboot_cbor_put_int(&w, COSE_ALG_ES256);
if (w.error != 0) {
return w.error;
}
*prot_len = w.offset;
return 0;
}
static int wolfboot_dice_build_sig_structure(uint8_t *buf,
size_t buf_len,
const uint8_t *prot,
size_t prot_len,
const uint8_t *payload,
size_t payload_len,
size_t *tbs_len)
{
struct wolfboot_cbor_writer w;
wolfboot_cbor_init(&w, buf, buf_len);
wolfboot_cbor_put_array_start(&w, 4);
wolfboot_cbor_put_tstr(&w, "Signature1", 10);
wolfboot_cbor_put_bstr(&w, prot, prot_len);
wolfboot_cbor_put_bstr(&w, (const uint8_t *)"", 0);
wolfboot_cbor_put_bstr(&w, payload, payload_len);
if (w.error != 0) {
return w.error;
}
*tbs_len = w.offset;
return 0;
}
static int wolfboot_dice_sign_tbs(const uint8_t *tbs,
size_t tbs_len,
uint8_t *sig,
size_t *sig_len,
const struct wolfboot_dice_claims *claims)
{
int ret = WOLFBOOT_DICE_ERR_CRYPTO;
uint8_t hash[SHA256_DIGEST_SIZE];
#ifndef WOLFBOOT_DICE_HW
ecc_key key;
int key_inited = 0;
WC_RNG rng;
int wc_ret;
int rng_inited = 0;
uint8_t der_sig[128];
word32 der_sig_len = sizeof(der_sig);
uint8_t r[WOLFBOOT_DICE_SIG_LEN / 2];
uint8_t s[WOLFBOOT_DICE_SIG_LEN / 2];
word32 r_len = sizeof(r);
word32 s_len = sizeof(s);
#endif /* !WOLFBOOT_DICE_HW */
if (sig == NULL || sig_len == NULL || *sig_len < WOLFBOOT_DICE_SIG_LEN) {
return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT;
}
#ifdef WOLFBOOT_DICE_HW
if (wolfboot_attest_get_private_key_hw(claims) != 0) {
ret = WOLFBOOT_DICE_ERR_HW;
goto cleanup;
static int wolfboot_dice_hw_sign_cb(void *cbCtx, int32_t alg,
const uint8_t *tbs, size_t tbs_len,
uint8_t *sig, size_t sig_sz,
size_t *sig_len)
{
int *sign_failed = (int *)cbCtx;
size_t out_len = sig_sz;
(void)alg;
/* wolfCOSE pre-hashes the Sig_structure for ES256, so tbs is the 32-byte
* digest. hal_dice_sign_hash() outputs 64-byte raw R||S and keeps the
* private key inside the platform boundary. */
if (hal_dice_sign_hash(tbs, tbs_len, sig, &out_len) != 0) {
if (sign_failed != NULL) {
*sign_failed = 1;
}
return -1;
}
#else
*sig_len = out_len;
return 0;
}
#endif /* WOLFBOOT_DICE_HW */
#ifndef WOLFBOOT_DICE_HW
/* Kept out of line so the secure-world sign path does not widen the caller's
* frame, which the size query also pays for. */
static int NOINLINEFUNCTION wolfboot_dice_sign_payload(
WOLFCOSE_KEY *cose_key,
struct wolfboot_dice_claims *claims,
const uint8_t *payload,
size_t payload_len,
uint8_t *scratch,
size_t scratch_len,
uint8_t *token_buf,
size_t token_buf_size,
size_t *out_len)
{
ecc_key key;
WC_RNG rng;
int key_inited = 0;
int rng_inited = 0;
int ret;
wc_ecc_init(&key);
key_inited = 1;
if (wolfboot_attest_get_private_key(&key, claims) != 0) {
ret = WOLFBOOT_DICE_ERR_HW;
goto cleanup;
}
#endif
#ifndef WOLFBOOT_DICE_HW
(void)wc_ecc_set_deterministic(&key, 1);
if (wc_InitRng(&rng) != 0) {
ret = WOLFBOOT_DICE_ERR_HW;
goto cleanup;
}
rng_inited = 1;
#endif /* !WOLFBOOT_DICE_HW */
{
wc_Sha256 sha;
ret = wc_InitSha256(&sha);
if (ret == 0) {
ret = wc_Sha256Update(&sha, tbs, (word32)tbs_len);
if (ret == 0) {
ret = wc_Sha256Final(&sha, hash);
}
wc_Sha256Free(&sha);
}
if (ret != 0) {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
}
#ifdef WOLFBOOT_DICE_HW
/* Platform attestation key is ready. Sign pre-computed hash via HAL.
* hal_dice_sign_hash() MUST output 64-byte raw R||S (big-endian), NOT DER.
* This matches WOLFBOOT_DICE_SIG_LEN and the COSE_Sign1 signature field directly,
* bypassing the wc_ecc_sig_to_rs DER->raw conversion that the software path needs. */
ret = hal_dice_sign_hash(hash, sizeof(hash), sig, sig_len);
if (ret != 0)
ret = WOLFBOOT_DICE_ERR_HW;
#else /* !WOLFBOOT_DICE_HW */
wc_ret = wc_ecc_sign_hash(hash, sizeof(hash), der_sig, &der_sig_len, &rng, &key);
if (wc_ret != 0) {
ret = wc_CoseKey_SetEcc(cose_key, WOLFCOSE_CRV_P256, &key);
if (ret != 0) {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
wc_ret = wc_ecc_sig_to_rs(der_sig, der_sig_len, r, &r_len, s, &s_len);
if (wc_ret != 0 || r_len > sizeof(r) || s_len > sizeof(s)) {
ret = wc_CoseSign1_Sign_ex(cose_key, WOLFCOSE_ALG_ES256, NULL, 0,
payload, payload_len, NULL, 0, NULL, 0,
scratch, scratch_len, token_buf, token_buf_size,
out_len, &rng, WOLFCOSE_SIGN1_UNTAGGED);
if (ret != 0) {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
XMEMSET(sig, 0, WOLFBOOT_DICE_SIG_LEN);
XMEMCPY(sig + (sizeof(r) - r_len), r, r_len);
XMEMCPY(sig + sizeof(r) + (sizeof(s) - s_len), s, s_len);
*sig_len = WOLFBOOT_DICE_SIG_LEN;
ret = WOLFBOOT_DICE_SUCCESS;
#endif /* !WOLFBOOT_DICE_HW */
cleanup:
#ifndef WOLFBOOT_DICE_HW
if (key_inited) {
wc_ecc_free(&key);
wolfboot_dice_zeroize(&key, sizeof(key));
@ -983,11 +862,9 @@ cleanup:
if (rng_inited) {
wc_FreeRng(&rng);
}
wolfboot_dice_zeroize(der_sig, sizeof(der_sig));
#endif /* !WOLFBOOT_DICE_HW */
wolfboot_dice_zeroize(hash, sizeof(hash));
return ret;
}
#endif /* !WOLFBOOT_DICE_HW */
static int wolfboot_dice_build_token(uint8_t *token_buf,
size_t token_buf_size,
@ -998,13 +875,13 @@ static int wolfboot_dice_build_token(uint8_t *token_buf,
struct wolfboot_dice_claims claims;
uint8_t payload[WOLFBOOT_DICE_MAX_PAYLOAD];
size_t payload_len = 0;
uint8_t protected_hdr[32];
size_t protected_len = 0;
uint8_t tbs[WOLFBOOT_DICE_MAX_TBS];
size_t tbs_len = 0;
uint8_t sig[WOLFBOOT_DICE_SIG_LEN];
size_t sig_len = sizeof(sig);
struct wolfboot_cbor_writer w;
uint8_t scratch[WOLFBOOT_DICE_MAX_TBS];
WOLFCOSE_KEY cose_key;
int cose_key_inited = 0;
#ifdef WOLFBOOT_DICE_HW
int hw_sign_failed = 0;
#endif
size_t out_len = 0;
int ret;
ret = wolfboot_dice_collect_claims(&claims);
@ -1018,48 +895,71 @@ static int wolfboot_dice_build_token(uint8_t *token_buf,
ret = wolfboot_dice_encode_payload(payload, sizeof(payload), &claims,
&payload_len);
if (ret != 0) {
return ret;
goto cleanup;
}
ret = wolfboot_dice_encode_protected(protected_hdr, sizeof(protected_hdr),
&protected_len);
if (ret != 0) {
return ret;
}
ret = wolfboot_dice_build_sig_structure(tbs, sizeof(tbs),
protected_hdr, protected_len,
payload, payload_len, &tbs_len);
if (ret != 0) {
return ret;
}
if (token_buf != NULL) {
ret = wolfboot_dice_sign_tbs(tbs, tbs_len, sig, &sig_len, &claims);
if (ret != 0) {
return ret;
/* Size query returns the exact untagged COSE_Sign1 length without signing:
* the HW DICE engine must not run and the CDI must not advance while sizing. */
if (token_buf == NULL) {
ret = wc_CoseSign1_SignSize_ex(NULL, WOLFCOSE_ALG_ES256, 0,
payload_len, 0,
WOLFCOSE_SIGN1_UNTAGGED, &out_len);
if (ret == 0) {
*token_len = out_len;
}
else {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
}
goto cleanup;
}
wolfboot_cbor_init(&w, token_buf, token_buf_size);
wolfboot_cbor_put_array_start(&w, 4);
wolfboot_cbor_put_bstr(&w, protected_hdr, protected_len);
wolfboot_cbor_put_map_start(&w, 0);
wolfboot_cbor_put_bstr(&w, payload, payload_len);
if (token_buf != NULL) {
wolfboot_cbor_put_bstr(&w, sig, sig_len);
}
else {
wolfboot_cbor_put_type_val(&w, 2, WOLFBOOT_DICE_SIG_LEN);
wolfboot_cbor_reserve(&w, WOLFBOOT_DICE_SIG_LEN);
ret = wc_CoseKey_Init(&cose_key);
if (ret != 0) {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
cose_key_inited = 1;
if (w.error != 0) {
return w.error;
#ifdef WOLFBOOT_DICE_HW
if (wolfboot_attest_get_private_key_hw(&claims) != 0) {
ret = WOLFBOOT_DICE_ERR_HW;
goto cleanup;
}
ret = wc_CoseKey_SetExtSigner(&cose_key, wolfboot_dice_hw_sign_cb,
&hw_sign_failed);
if (ret != 0) {
ret = WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
ret = wc_CoseSign1_Sign_ex(&cose_key, WOLFCOSE_ALG_ES256, NULL, 0,
payload, payload_len, NULL, 0, NULL, 0,
scratch, sizeof(scratch), token_buf,
token_buf_size, &out_len, NULL,
WOLFCOSE_SIGN1_UNTAGGED);
if (ret != 0) {
ret = hw_sign_failed ? WOLFBOOT_DICE_ERR_HW :
WOLFBOOT_DICE_ERR_CRYPTO;
goto cleanup;
}
#else
ret = wolfboot_dice_sign_payload(&cose_key, &claims, payload, payload_len,
scratch, sizeof(scratch),
token_buf, token_buf_size, &out_len);
if (ret != 0) {
goto cleanup;
}
#endif /* WOLFBOOT_DICE_HW */
*token_len = w.offset;
return WOLFBOOT_DICE_SUCCESS;
*token_len = out_len;
ret = WOLFBOOT_DICE_SUCCESS;
cleanup:
if (cose_key_inited) {
wc_CoseKey_Free(&cose_key);
}
wolfboot_dice_zeroize(payload, sizeof(payload));
wolfboot_dice_zeroize(scratch, sizeof(scratch));
return ret;
}
int wolfBoot_dice_get_token(const uint8_t *challenge,

View File

@ -754,7 +754,8 @@ static int run_psa_boot_attestation(void)
#if (APP_HASH_SIZE > 0u)
uint8_t hash_buf[APP_HASH_SIZE];
#endif
size_t token_size = 0;
size_t queried_token_size;
size_t emitted_token_size;
int ret = 0;
size_t i;
@ -802,30 +803,39 @@ static int run_psa_boot_attestation(void)
}
printf(" step 5: request IAT token size\r\n");
status = psa_initial_attest_get_token_size(sizeof(challenge), &token_size);
queried_token_size = 0;
status = psa_initial_attest_get_token_size(sizeof(challenge),
&queried_token_size);
if (status != PSA_SUCCESS) {
printf(" step 5: token size failed (%ld)\r\n", (long)status);
ret = -1;
} else {
printf(" step 5: token size %lu bytes\r\n",
(unsigned long)token_size);
(unsigned long)queried_token_size);
}
printf(" step 6: request IAT token\r\n");
if (ret == 0 && token_size <= sizeof(token)) {
printf(" step 6: request exact-size IAT token\r\n");
if (ret == 0 && queried_token_size <= sizeof(token)) {
emitted_token_size = 0;
status = psa_initial_attest_get_token(challenge, sizeof(challenge),
token, sizeof(token), &token_size);
if (status != PSA_SUCCESS) {
printf(" step 6: token failed (%ld)\r\n", (long)status);
token, queried_token_size,
&emitted_token_size);
if (status != PSA_SUCCESS ||
emitted_token_size != queried_token_size) {
printf(" step 6: size mismatch (status=%ld size=%lu "
"expected=%lu)\r\n", (long)status,
(unsigned long)emitted_token_size,
(unsigned long)queried_token_size);
ret = -1;
} else {
printf(" step 6: token received (%lu bytes)\r\n",
(unsigned long)token_size);
print_hex(token, (uint32_t)token_size, 1);
printf(" IAT size match: challenge=64 token=%lu\r\n",
(unsigned long)emitted_token_size);
print_hex(token, (uint32_t)emitted_token_size, 1);
}
} else if (ret == 0) {
printf(" step 6: token buffer too small (%lu > %lu)\r\n",
(unsigned long)token_size, (unsigned long)sizeof(token));
(unsigned long)queried_token_size,
(unsigned long)sizeof(token));
ret = -1;
}

View File

@ -11,6 +11,7 @@ WOLFBOOT_LIB_WOLFPKCS11?=../../lib/wolfPKCS11
WOLFBOOT_LIB_WOLFPSA?=../../lib/wolfPSA
WOLFBOOT_LIB_WOLFTPM?=../../lib/wolfTPM
WOLFBOOT_LIB_WOLFHSM?=../../lib/wolfHSM
WOLFBOOT_LIB_WOLFCOSE?=../../lib/wolfCOSE
ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFPSA)),)
WOLFBOOT_LIB_WOLFPSA=../../../external-libs/wolfPSA
@ -21,6 +22,9 @@ endif
ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFHSM)),)
WOLFBOOT_LIB_WOLFHSM=../../../external-libs/wolfHSM
endif
ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFCOSE)),)
WOLFBOOT_LIB_WOLFCOSE=../../../external-libs/wolfCOSE
endif
# Convert to absolute paths for standalone usage
WOLFBOOT_LIB_WOLFSSL:=$(abspath $(WOLFBOOT_LIB_WOLFSSL))
@ -28,6 +32,7 @@ WOLFBOOT_LIB_WOLFPKCS11:=$(abspath $(WOLFBOOT_LIB_WOLFPKCS11))
WOLFBOOT_LIB_WOLFPSA:=$(abspath $(WOLFBOOT_LIB_WOLFPSA))
WOLFBOOT_LIB_WOLFTPM:=$(abspath $(WOLFBOOT_LIB_WOLFTPM))
WOLFBOOT_LIB_WOLFHSM:=$(abspath $(WOLFBOOT_LIB_WOLFHSM))
WOLFBOOT_LIB_WOLFCOSE:=$(abspath $(WOLFBOOT_LIB_WOLFCOSE))
CFLAGS=-I. -I../../src -I../../include -I$(WOLFBOOT_LIB_WOLFSSL)
CFLAGS+=-g -ggdb
@ -88,6 +93,8 @@ TESTS+=unit-fwtpm-nv-oob
TESTS+=unit-elf-bss-guard
TESTS+=unit-image-elf-scatter
TESTS+=unit-arm-tee-psa-ipc
TESTS+=unit-dice-token-size
TESTS+=unit-dice-token-nosign
TESTS+=unit-va416x0-fram
TESTS+=unit-flash-write-mcxa
TESTS+=unit-flash-write-samr21
@ -376,6 +383,24 @@ unit-arm-tee-psa-ipc: ../../include/target.h unit-arm-tee-psa-ipc.c ../../src/ar
-ffunction-sections -fdata-sections \
$(LDFLAGS) -Wl,--gc-sections
DICE_TOKEN_TEST_CFLAGS=-I$(WOLFBOOT_LIB_WOLFCOSE)/include \
-I$(WOLFBOOT_LIB_WOLFSSL) -DUNIT_TEST_AUTH -DWOLFBOOT_SIGN_ECC256 \
-DWOLFCRYPT_SECURE_MODE -DHAVE_HKDF -DWOLFBOOT_NO_PARTITIONS \
-DWOLFCOSE_LEAN -DWOLFCOSE_ENABLE_EXT_SIGN \
-ffunction-sections -fdata-sections
unit-dice-token-size:CFLAGS+=$(DICE_TOKEN_TEST_CFLAGS)
unit-dice-token-nosign:CFLAGS+=$(DICE_TOKEN_TEST_CFLAGS) -DWOLFBOOT_NO_SIGN
DICE_TOKEN_TEST_SRC=unit-dice-token-size.c \
$(WOLFBOOT_LIB_WOLFCOSE)/src/wolfcose.c \
$(WOLFBOOT_LIB_WOLFCOSE)/src/wolfcose_cbor.c \
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c \
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hash.c \
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c
unit-dice-token-size unit-dice-token-nosign: ../../include/target.h \
$(DICE_TOKEN_TEST_SRC)
gcc -o $@ $(DICE_TOKEN_TEST_SRC) $(CFLAGS) $(LDFLAGS) \
-Wl,--gc-sections
unit-va416x0-fram: unit-va416x0-fram.c ../../hal/va416x0.c
gcc -o $@ unit-va416x0-fram.c $(CFLAGS) $(LDFLAGS)

View File

@ -0,0 +1,366 @@
/* unit-dice-token-size.c
*
* DICE COSE_Sign1 token sizing tests.
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
#include <check.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
static uint8_t test_wolfboot_region[64];
#define ARCH_FLASH_OFFSET 0x1000u
#define WOLFBOOT_PARTITION_BOOT_ADDRESS 0x1040u
#define EXT_FLASH
#define NO_XIP
#define WOLFBOOT_DICE_HW
#define WOLFBOOT_HASH_SHA256
#include "../../src/dice/dice.c"
static uint8_t test_boot_hash[WOLFBOOT_SHA_DIGEST_SIZE];
static uint8_t test_boot_signer_id[WOLFBOOT_SHA_DIGEST_SIZE];
static unsigned int test_cdi_update_calls;
static unsigned int test_key_create_calls;
static unsigned int test_sign_calls;
static int test_sign_result;
/* Local ECC signing must never be reached: this test exercises the delegated
* hardware-signing path. These stubs keep that failure mode link-visible. */
int wc_ecc_sign_hash(const byte *in, word32 in_len, byte *out,
word32 *out_len, WC_RNG *rng, ecc_key *key)
{
(void)in;
(void)in_len;
(void)out;
(void)out_len;
(void)rng;
(void)key;
return -1;
}
int wc_ecc_sig_to_rs(const byte *sig, word32 sig_len, byte *r,
word32 *r_len, byte *s, word32 *s_len)
{
(void)sig;
(void)sig_len;
(void)r;
(void)r_len;
(void)s;
(void)s_len;
return -1;
}
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
{
size_t offset;
if (address < ARCH_FLASH_OFFSET || data == NULL || len < 0) {
return -1;
}
offset = (size_t)(address - ARCH_FLASH_OFFSET);
if (offset > sizeof(test_wolfboot_region) ||
(size_t)len > sizeof(test_wolfboot_region) - offset) {
return -1;
}
memcpy(data, test_wolfboot_region + offset, (size_t)len);
return len;
}
int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part)
{
(void)img;
return part == PART_BOOT ? 0 : -1;
}
uint16_t wolfBoot_get_header(struct wolfBoot_image *img, uint16_t type,
uint8_t **ptr)
{
(void)img;
if (ptr == NULL) {
return 0;
}
if (type == HDR_HASH) {
*ptr = test_boot_hash;
}
else if (type == HDR_PUBKEY) {
*ptr = test_boot_signer_id;
}
else {
return 0;
}
return (uint16_t)sizeof(test_boot_hash);
}
int hal_attestation_get_ueid(uint8_t *buf, size_t *len)
{
size_t i;
if (buf == NULL || len == NULL || *len < WOLFBOOT_DICE_UEID_LEN) {
return -1;
}
for (i = 0; i < WOLFBOOT_DICE_UEID_LEN; i++) {
buf[i] = (uint8_t)(i + 1u);
}
*len = WOLFBOOT_DICE_UEID_LEN;
return 0;
}
int hal_attestation_get_implementation_id(uint8_t *buf, size_t *len)
{
if (buf == NULL || len == NULL || *len < WOLFBOOT_SHA_DIGEST_SIZE) {
return -1;
}
memset(buf, 0xA5, WOLFBOOT_SHA_DIGEST_SIZE);
*len = WOLFBOOT_SHA_DIGEST_SIZE;
return 0;
}
int hal_attestation_get_lifecycle(uint32_t *lifecycle)
{
if (lifecycle == NULL) {
return -1;
}
*lifecycle = 0x3000u;
return 0;
}
int hal_dice_update_cdi(const uint8_t *measurement, size_t meas_len,
const char *measurement_desc,
size_t measurement_desc_len)
{
test_cdi_update_calls++;
return measurement != NULL && meas_len > 0u &&
measurement_desc != NULL && measurement_desc_len > 0u ? 0 : -1;
}
int hal_dice_create_attest_key(void)
{
test_key_create_calls++;
return 0;
}
int hal_dice_sign_hash(const uint8_t *hash, size_t hash_len,
uint8_t *sig, size_t *sig_len)
{
test_sign_calls++;
if (hash == NULL || hash_len != SHA256_DIGEST_SIZE || sig == NULL ||
sig_len == NULL || *sig_len < 64u) {
return -1;
}
if (test_sign_result != 0) {
return test_sign_result;
}
memset(sig, 0x5A, 64u);
*sig_len = 64u;
return 0;
}
int hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len)
{
(void)buf;
(void)len;
return -1;
}
static void check_component_signer_ids(const uint8_t *token, size_t token_size)
{
WOLFCOSE_CBOR_CTX token_ctx;
WOLFCOSE_CBOR_CTX payload_ctx;
const uint8_t *payload;
const uint8_t *value;
size_t array_count;
size_t map_count;
size_t payload_len;
size_t value_len;
size_t i;
size_t j;
uint8_t unknown_signer[WOLFBOOT_SHA_DIGEST_SIZE];
int64_t label;
int found_components = 0;
int found_signer;
memset(&token_ctx, 0, sizeof(token_ctx));
memset(unknown_signer, 0, sizeof(unknown_signer));
token_ctx.cbuf = token;
token_ctx.bufSz = token_size;
ck_assert_int_eq(wc_CBOR_DecodeArrayStart(&token_ctx, &array_count), 0);
ck_assert_uint_eq(array_count, 4u);
ck_assert_int_eq(wc_CBOR_DecodeBstr(&token_ctx, &value, &value_len), 0);
ck_assert_int_eq(wc_CBOR_DecodeMapStart(&token_ctx, &map_count), 0);
ck_assert_uint_eq(map_count, 0u);
ck_assert_int_eq(wc_CBOR_DecodeBstr(&token_ctx, &payload, &payload_len), 0);
ck_assert_int_eq(wc_CBOR_DecodeBstr(&token_ctx, &value, &value_len), 0);
ck_assert_uint_eq(token_ctx.idx, token_ctx.bufSz);
memset(&payload_ctx, 0, sizeof(payload_ctx));
payload_ctx.cbuf = payload;
payload_ctx.bufSz = payload_len;
ck_assert_int_eq(wc_CBOR_DecodeMapStart(&payload_ctx, &map_count), 0);
for (i = 0; i < map_count; i++) {
ck_assert_int_eq(wc_CBOR_DecodeInt(&payload_ctx, &label), 0);
if (label != PSA_IAT_CLAIM_SW_COMPONENTS) {
ck_assert_int_eq(wc_CBOR_Skip(&payload_ctx), 0);
continue;
}
found_components = 1;
ck_assert_int_eq(wc_CBOR_DecodeArrayStart(&payload_ctx,
&array_count), 0);
ck_assert_uint_eq(array_count, 2u);
for (j = 0; j < array_count; j++) {
size_t component_fields;
size_t k;
found_signer = 0;
ck_assert_int_eq(wc_CBOR_DecodeMapStart(&payload_ctx,
&component_fields), 0);
ck_assert_uint_eq(component_fields, 4u);
for (k = 0; k < component_fields; k++) {
ck_assert_int_eq(wc_CBOR_DecodeInt(&payload_ctx, &label), 0);
if (label != PSA_SW_COMPONENT_SIGNER_ID) {
ck_assert_int_eq(wc_CBOR_Skip(&payload_ctx), 0);
continue;
}
ck_assert_int_eq(wc_CBOR_DecodeBstr(&payload_ctx, &value,
&value_len), 0);
ck_assert_uint_eq(value_len, WOLFBOOT_SHA_DIGEST_SIZE);
if (j == 0u) {
ck_assert_mem_eq(value, unknown_signer,
sizeof(unknown_signer));
}
else {
#ifdef WOLFBOOT_NO_SIGN
ck_assert_mem_eq(value, unknown_signer,
sizeof(unknown_signer));
#else
ck_assert_mem_eq(value, test_boot_signer_id,
sizeof(test_boot_signer_id));
#endif
}
found_signer = 1;
}
ck_assert_int_eq(found_signer, 1);
}
}
ck_assert_int_eq(found_components, 1);
ck_assert_uint_eq(payload_ctx.idx, payload_ctx.bufSz);
}
static void check_token_size(size_t challenge_size)
{
uint8_t challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64];
uint8_t token[WOLFBOOT_DICE_MAX_PAYLOAD + 128u];
size_t queried_size = 0;
size_t actual_size = 0;
size_t short_size = 0;
int ret;
memset(challenge, 0x3C, sizeof(challenge));
memset(test_boot_signer_id, 0xA7, sizeof(test_boot_signer_id));
test_cdi_update_calls = 0;
test_key_create_calls = 0;
test_sign_calls = 0;
test_sign_result = 0;
ret = wolfBoot_dice_get_token_size(challenge_size, &queried_size);
ck_assert_int_eq(ret, WOLFBOOT_DICE_SUCCESS);
ck_assert_uint_gt(queried_size, 0u);
ck_assert_uint_le(queried_size, sizeof(token));
ck_assert_uint_eq(test_cdi_update_calls, 0u);
ck_assert_uint_eq(test_key_create_calls, 0u);
ck_assert_uint_eq(test_sign_calls, 0u);
ret = wolfBoot_dice_get_token(challenge, challenge_size, token,
queried_size - 1u, &short_size);
ck_assert_int_eq(ret, WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL);
ck_assert_uint_eq(short_size, queried_size);
ck_assert_uint_eq(test_cdi_update_calls, 0u);
ck_assert_uint_eq(test_key_create_calls, 0u);
ck_assert_uint_eq(test_sign_calls, 0u);
ret = wolfBoot_dice_get_token(challenge, challenge_size, token,
queried_size, &actual_size);
ck_assert_int_eq(ret, WOLFBOOT_DICE_SUCCESS);
ck_assert_uint_eq(actual_size, queried_size);
ck_assert_uint_eq(test_cdi_update_calls, 2u);
ck_assert_uint_eq(test_key_create_calls, 1u);
ck_assert_uint_eq(test_sign_calls, 1u);
check_component_signer_ids(token, actual_size);
}
START_TEST(test_dice_token_size_32)
{
check_token_size(PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32);
}
END_TEST
START_TEST(test_dice_hw_sign_failure)
{
uint8_t challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32];
uint8_t token[WOLFBOOT_DICE_MAX_PAYLOAD + 128u];
size_t token_size = 0;
int ret;
memset(challenge, 0x3C, sizeof(challenge));
memset(test_boot_signer_id, 0xA7, sizeof(test_boot_signer_id));
test_cdi_update_calls = 0;
test_key_create_calls = 0;
test_sign_calls = 0;
test_sign_result = -1;
ret = wolfBoot_dice_get_token(challenge, sizeof(challenge), token,
sizeof(token), &token_size);
ck_assert_int_eq(ret, WOLFBOOT_DICE_ERR_HW);
ck_assert_uint_eq(test_sign_calls, 1u);
}
END_TEST
START_TEST(test_dice_token_size_48)
{
check_token_size(PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48);
}
END_TEST
START_TEST(test_dice_token_size_64)
{
check_token_size(PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64);
}
END_TEST
static Suite *dice_token_size_suite(void)
{
Suite *suite = suite_create("dice-token-size");
TCase *test_case = tcase_create("size-consistency");
tcase_add_test(test_case, test_dice_token_size_32);
tcase_add_test(test_case, test_dice_token_size_48);
tcase_add_test(test_case, test_dice_token_size_64);
tcase_add_test(test_case, test_dice_hw_sign_failure);
suite_add_tcase(suite, test_case);
return suite;
}
int main(void)
{
int failures;
Suite *suite = dice_token_size_suite();
SRunner *runner = srunner_create(suite);
srunner_run_all(runner, CK_NORMAL);
failures = srunner_ntests_failed(runner);
srunner_free(runner);
return failures;
}