diff --git a/trusted-firmware-m/2.3.0/README b/trusted-firmware-m/2.3.0/README new file mode 100644 index 00000000..c16651cc --- /dev/null +++ b/trusted-firmware-m/2.3.0/README @@ -0,0 +1,6 @@ +This folder contains patches for Trusted Firmware-M to work with wolfSSL. +Patches make it easier to add support for newer versions of a target library. +The format of the patch names is: + trusted-firmware-m--.patch +Instructions for applying each patch are included in the patch commit +message. diff --git a/trusted-firmware-m/2.3.0/trusted-firmware-m-2.3.0-wolfcose.patch b/trusted-firmware-m/2.3.0/trusted-firmware-m-2.3.0-wolfcose.patch new file mode 100644 index 00000000..cab51317 --- /dev/null +++ b/trusted-firmware-m/2.3.0/trusted-firmware-m-2.3.0-wolfcose.patch @@ -0,0 +1,814 @@ +From 12d39ffdbc9bd4cf434da3c0235ba92b7c5646d0 Mon Sep 17 00:00:00 2001 +From: Aidan Garske +Date: Thu, 16 Jul 2026 18:33:44 -0700 +Subject: [PATCH] Use wolfCOSE for initial attestation token signing + +Replaces t_cose with wolfCOSE in the initial attestation partition. The IAK is +not exported: wolfCOSE is given a signature callback that calls psa_sign_hash, +so the key stays in the crypto partition. + +Adds a TFM_COSE_BACKEND selector which defaults to t_cose, so an unselected +build is unchanged. + +Apply to TF-Mv2.3.0: + + git am trusted-firmware-m-2.3.0-wolfcose.patch + +Build, with wolfCOSE and wolfSSL sources checked out: + + cmake -S /tests_reg/spe -B build_spe -GNinja \ + -DTFM_PLATFORM=arm/mps2/an521 \ + -DCONFIG_TFM_SOURCE_PATH= \ + -DTFM_COSE_BACKEND=wolfcose \ + -DWOLFCOSE_PATH= \ + -DWOLFSSL_PATH= \ + -DTEST_NS=ON -DTEST_S=ON + cmake --build build_spe -- install + +wolfCOSE must be built with WOLFCOSE_ENABLE_EXT_SIGN, which the added +lib/ext/wolfcose/CMakeLists.txt sets. +--- + config/config_base.cmake | 1 + + lib/ext/CMakeLists.txt | 1 + + lib/ext/wolfcose/CMakeLists.txt | 69 ++++ + lib/ext/wolfcose/user_settings.h | 64 ++++ + .../initial_attestation/CMakeLists.txt | 17 +- + .../attest_asymmetric_key.c | 13 +- + .../initial_attestation/attest_core.c | 18 +- + .../initial_attestation/attest_token.h | 11 + + .../attest_token_backend.h | 51 +++ + ..._encode.c => attest_token_encode_t_cose.c} | 15 + + .../attest_token_encode_wolfcose.c | 311 ++++++++++++++++++ + 11 files changed, 550 insertions(+), 21 deletions(-) + create mode 100644 lib/ext/wolfcose/CMakeLists.txt + create mode 100644 lib/ext/wolfcose/user_settings.h + create mode 100644 secure_fw/partitions/initial_attestation/attest_token_backend.h + rename secure_fw/partitions/initial_attestation/{attest_token_encode.c => attest_token_encode_t_cose.c} (96%) + create mode 100644 secure_fw/partitions/initial_attestation/attest_token_encode_wolfcose.c + +diff --git a/config/config_base.cmake b/config/config_base.cmake +index dff8a5df1..cf2f638ad 100644 +--- a/config/config_base.cmake ++++ b/config/config_base.cmake +@@ -179,6 +179,7 @@ set(TFM_PARTITION_INITIAL_ATTESTATION OFF CACHE BOOL "Enable Init + set(SYMMETRIC_INITIAL_ATTESTATION OFF CACHE BOOL "Use symmetric crypto for inital attestation") + set(ATTEST_KEY_BITS 256 CACHE STRING "The size of the initial attestation key in bits") + set(PSA_INITIAL_ATTEST_MAX_TOKEN_SIZE 0x250 CACHE STRING "The maximum possible size of a token") ++set(TFM_COSE_BACKEND "t_cose" CACHE STRING "COSE implementation used by initial attestation [t_cose]") + + set(TFM_PARTITION_PLATFORM OFF CACHE BOOL "Enable Platform partition") + +diff --git a/lib/ext/CMakeLists.txt b/lib/ext/CMakeLists.txt +index 06e952ac9..1c9788e39 100644 +--- a/lib/ext/CMakeLists.txt ++++ b/lib/ext/CMakeLists.txt +@@ -9,6 +9,7 @@ set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/lib/ext CACHE STRING "" FORCE) + add_subdirectory(qcbor) + add_subdirectory(tf-psa-crypto) + add_subdirectory(t_cose) ++add_subdirectory(wolfcose) + add_subdirectory(cmsis) + add_subdirectory(efi_soft_crc) + if (${PLATFORM_PSA_ADAC_SECURE_DEBUG}) +diff --git a/lib/ext/wolfcose/CMakeLists.txt b/lib/ext/wolfcose/CMakeLists.txt +new file mode 100644 +index 000000000..d1d03bb85 +--- /dev/null ++++ b/lib/ext/wolfcose/CMakeLists.txt +@@ -0,0 +1,69 @@ ++if (NOT TFM_COSE_BACKEND STREQUAL "wolfcose") ++ return() ++endif() ++ ++cmake_minimum_required(VERSION 3.21) ++ ++if (NOT WOLFCOSE_PATH OR NOT EXISTS ${WOLFCOSE_PATH}) ++ message(FATAL_ERROR "WOLFCOSE_PATH = '${WOLFCOSE_PATH}' is not set or does not exist. " ++ "Provide the path to a wolfCOSE checkout with -DWOLFCOSE_PATH=") ++endif() ++if (NOT WOLFSSL_PATH OR NOT EXISTS ${WOLFSSL_PATH}) ++ message(FATAL_ERROR "WOLFSSL_PATH = '${WOLFSSL_PATH}' is not set or does not exist. " ++ "wolfCOSE needs wolfCrypt headers and SHA-256. Use -DWOLFSSL_PATH=") ++endif() ++ ++add_library(wolfcose_s STATIC EXCLUDE_FROM_ALL) ++ ++target_sources(wolfcose_s ++ PRIVATE ++ ${WOLFCOSE_PATH}/src/wolfcose.c ++ ${WOLFCOSE_PATH}/src/wolfcose_cbor.c ++ # Only what wc_Hash(SHA-256) and the ES256 framing reach. ++ ${WOLFSSL_PATH}/wolfcrypt/src/hash.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/sha256.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/wc_port.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/memory.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/wolfmath.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/ecc.c ++ # wc_ecc_sign_hash pulls DER signature helpers even though the ++ # signature itself is delegated to psa_sign_hash. ++ ${WOLFSSL_PATH}/wolfcrypt/src/asn.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/coding.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/sp_int.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/sp_c32.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/random.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/logging.c ++ ${WOLFSSL_PATH}/wolfcrypt/src/misc.c ++) ++ ++target_include_directories(wolfcose_s ++ PUBLIC ++ ${WOLFCOSE_PATH}/include ++ ${CMAKE_CURRENT_LIST_DIR} ++ ${WOLFSSL_PATH} ++ PRIVATE ++ ${WOLFCOSE_PATH}/src ++) ++ ++target_compile_definitions(wolfcose_s ++ PUBLIC ++ WOLFSSL_USER_SETTINGS ++ WOLFCOSE_ENABLE_EXT_SIGN ++ PRIVATE ++ WOLFSSL_IGNORE_FILE_WARN ++) ++ ++target_compile_options(wolfcose_s ++ PRIVATE ++ ${COMPILER_CP_FLAG} ++ -Wno-unused-parameter ++) ++ ++target_link_libraries(wolfcose_s ++ PRIVATE ++ psa_crypto_config ++ psa_interface ++ platform_s ++ tfm_config ++) +diff --git a/lib/ext/wolfcose/user_settings.h b/lib/ext/wolfcose/user_settings.h +new file mode 100644 +index 000000000..f66dbabd4 +--- /dev/null ++++ b/lib/ext/wolfcose/user_settings.h +@@ -0,0 +1,64 @@ ++#ifndef __WOLFCOSE_TFM_USER_SETTINGS_H__ ++#define __WOLFCOSE_TFM_USER_SETTINGS_H__ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* wolfSSL builds gnu11, where anonymous inline aggregates are on. TF-M's ++ * toolchain files force -std=c99, which turns them off and silently shrinks ++ * WC_RNG. Keep this even though the delegated path links no RNG. */ ++#define HAVE_ANONYMOUS_INLINE_AGGREGATES 1 ++ ++#define WOLFCRYPT_ONLY ++#define SINGLE_THREADED ++#define NO_FILESYSTEM ++#define NO_WRITEV ++#define WOLFSSL_NO_SOCK ++#define NO_ERROR_STRINGS ++#define NO_WOLFSSL_DIR ++ ++/* The attestation partition has no heap. WOLFSSL_SMALL_STACK must stay off: it ++ * moves large buffers to the heap, which is exactly what is missing here. */ ++#define WOLFSSL_NO_MALLOC ++#define NO_WOLFSSL_MEMORY ++ ++/* wolfCOSE pre-hashes the Sig_structure with wc_Hash(SHA-256); the signature ++ * itself is delegated to psa_sign_hash, so no RNG is needed here. */ ++#define WOLFSSL_SHA256 ++#define NO_SHA ++#define NO_MD4 ++#define NO_MD5 ++#define WOLFSSL_NO_SHAKE128 ++#define WOLFSSL_NO_SHAKE256 ++ ++#define NO_DH ++#define NO_DSA ++#define NO_DES3 ++#define NO_RC4 ++#define NO_AES ++#define NO_HMAC ++#define NO_PWDBASED ++#define NO_PKCS12 ++#define NO_PKCS8 ++#define WOLFSSL_ASN_TEMPLATE ++#define NO_CERTS ++#define NO_RSA ++#define NO_SESSION_CACHE ++#define NO_OLD_TLS ++ ++/* ES256 framing only. wolfCOSE gates COSE_Sign1 on HAVE_ECC, so ECC must be ++ * enabled even though psa_sign_hash performs the signature. */ ++/* Key export stays enabled: wc_CoseKey_Encode uses wc_ecc_export_public_raw to ++ * emit the COSE_Key that the Instance ID hashes over. */ ++#define HAVE_ECC ++#define ECC_USER_CURVES ++#define HAVE_ECC256 ++#define ECC_TIMING_RESISTANT ++#define TFM_TIMING_RESISTANT ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __WOLFCOSE_TFM_USER_SETTINGS_H__ */ +diff --git a/secure_fw/partitions/initial_attestation/CMakeLists.txt b/secure_fw/partitions/initial_attestation/CMakeLists.txt +index 7d0f5c9de..676f0343c 100644 +--- a/secure_fw/partitions/initial_attestation/CMakeLists.txt ++++ b/secure_fw/partitions/initial_attestation/CMakeLists.txt +@@ -10,6 +10,12 @@ endif() + + cmake_minimum_required(VERSION 3.21) + ++set(TFM_COSE_BACKEND_SUPPORTED t_cose wolfcose) ++if (NOT TFM_COSE_BACKEND IN_LIST TFM_COSE_BACKEND_SUPPORTED) ++ message(FATAL_ERROR "Unsupported TFM_COSE_BACKEND '${TFM_COSE_BACKEND}'. " ++ "Supported: ${TFM_COSE_BACKEND_SUPPORTED}") ++endif() ++ + configure_file(${CMAKE_SOURCE_DIR}/interface/include/psa/initial_attestation.h.in + ${CMAKE_BINARY_DIR}/generated/interface/include/psa/initial_attestation.h) + +@@ -24,7 +30,8 @@ target_sources(tfm_psa_rot_partition_attestation PRIVATE + attest_boot_data.c + $<$>:attest_asymmetric_key.c> + $<$:attest_symmetric_key.c> +- attest_token_encode.c ++ $<$:attest_token_encode_t_cose.c> ++ $<$:attest_token_encode_wolfcose.c> + ) + + # The generated sources +@@ -43,6 +50,10 @@ target_include_directories(tfm_psa_rot_partition_attestation + . + PRIVATE + ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/initial_attestation ++ # q_useful_buf is QCBOR's type, re-exported by a header-only t_cose ++ # wrapper. Needed for the type alone; no t_cose code links unless ++ # t_cose is the selected backend. ++ ${T_COSE_PATH}/inc + ) + target_include_directories(tfm_partitions + INTERFACE +@@ -53,7 +64,8 @@ target_link_libraries(tfm_psa_rot_partition_attestation + PRIVATE + platform_s + tfm_config +- tfm_t_cose_s ++ $<$:tfm_t_cose_s> ++ $<$:wolfcose_s> + tfm_sprt + tfm_boot_status + qcbor +@@ -65,6 +77,7 @@ target_compile_definitions(tfm_psa_rot_partition_attestation + $<$:SYMMETRIC_INITIAL_ATTESTATION> + $<$>:CLAIM_VALUE_CHECK> + $<$>:ATTEST_KEY_BITS=${ATTEST_KEY_BITS}> ++ $<$:TFM_COSE_BACKEND_WOLFCOSE> + ) + + if(DEFINED ATTEST_BOOT_STATUS_MAX_SIZE) +diff --git a/secure_fw/partitions/initial_attestation/attest_asymmetric_key.c b/secure_fw/partitions/initial_attestation/attest_asymmetric_key.c +index 5630d24c7..d25ba32ff 100644 +--- a/secure_fw/partitions/initial_attestation/attest_asymmetric_key.c ++++ b/secure_fw/partitions/initial_attestation/attest_asymmetric_key.c +@@ -12,8 +12,7 @@ + #include "config_tfm.h" + #include "psa/crypto.h" + #include "t_cose/q_useful_buf.h" +-#include "t_cose/t_cose_common.h" +-#include "t_cose/t_cose_key.h" ++#include "attest_token_backend.h" + #include "tfm_crypto_defs.h" + + /** +@@ -52,17 +51,11 @@ static struct q_useful_buf attest_key_hash = { .ptr = instance_id_buf + 1, + static enum psa_attest_err_t compute_attest_key_hash(void) + { + Q_USEFUL_BUF_MAKE_STACK_UB(public_key_buf, MAX_ENCODED_COSE_KEY_SIZE); +- struct t_cose_key attest_key; + struct q_useful_buf_c cose_key; + psa_status_t psa_res; +- enum t_cose_err_t cose_res; + +- attest_key.key.handle = TFM_BUILTIN_KEY_ID_IAK; +- +- cose_res = t_cose_key_encode(attest_key, +- public_key_buf, +- &cose_key); +- if (cose_res != T_COSE_SUCCESS) { ++ if (attest_encode_cose_key(TFM_BUILTIN_KEY_ID_IAK, public_key_buf, ++ &cose_key) != 0) { + return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE; + } + +diff --git a/secure_fw/partitions/initial_attestation/attest_core.c b/secure_fw/partitions/initial_attestation/attest_core.c +index 92d106326..a59fdc741 100644 +--- a/secure_fw/partitions/initial_attestation/attest_core.c ++++ b/secure_fw/partitions/initial_attestation/attest_core.c +@@ -19,7 +19,7 @@ + #include "tfm_plat_boot_seed.h" + #include "tfm_attest_hal.h" + #include "tfm_attest_iat_defs.h" +-#include "t_cose/t_cose_common.h" ++#include "attest_token_backend.h" + #include "tfm_crypto_defs.h" + #include "tfm_log.h" + #include "tfm_string.h" +@@ -508,7 +508,7 @@ static enum psa_attest_err_t attest_verify_challenge_size(size_t challenge_size) + return PSA_ATTEST_ERR_INVALID_INPUT; + } + +-static enum psa_attest_err_t attest_get_t_cose_algorithm( ++static enum psa_attest_err_t attest_get_cose_algorithm( + int32_t *cose_algorithm_id) + { + psa_status_t status; +@@ -530,13 +530,13 @@ static enum psa_attest_err_t attest_get_t_cose_algorithm( + (PSA_KEY_TYPE_ECC_GET_FAMILY(key_type) == PSA_ECC_FAMILY_SECP_R1)) { + switch (psa_get_key_bits(&attr)) { + case 256: +- *cose_algorithm_id = T_COSE_ALGORITHM_ES256; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_ES256; + break; + case 384: +- *cose_algorithm_id = T_COSE_ALGORITHM_ES384; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_ES384; + break; + case 512: +- *cose_algorithm_id = T_COSE_ALGORITHM_ES512; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_ES512; + break; + default: + return PSA_ATTEST_ERR_GENERAL; +@@ -544,13 +544,13 @@ static enum psa_attest_err_t attest_get_t_cose_algorithm( + } else if (key_type == PSA_KEY_TYPE_HMAC) { + switch (psa_get_key_bits(&attr)) { + case 256: +- *cose_algorithm_id = T_COSE_ALGORITHM_HMAC256; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_HMAC256; + break; + case 384: +- *cose_algorithm_id = T_COSE_ALGORITHM_HMAC384; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_HMAC384; + break; + case 512: +- *cose_algorithm_id = T_COSE_ALGORITHM_HMAC512; ++ *cose_algorithm_id = ATTEST_COSE_ALGORITHM_HMAC512; + break; + default: + return PSA_ATTEST_ERR_GENERAL; +@@ -619,7 +619,7 @@ attest_create_token(struct q_useful_buf_c *challenge, + int i; + int32_t cose_algorithm_id; + +- attest_err = attest_get_t_cose_algorithm(&cose_algorithm_id); ++ attest_err = attest_get_cose_algorithm(&cose_algorithm_id); + if (attest_err != PSA_ATTEST_ERR_SUCCESS) { + return attest_err; + } +diff --git a/secure_fw/partitions/initial_attestation/attest_token.h b/secure_fw/partitions/initial_attestation/attest_token.h +index a7de3c8ac..a59658764 100644 +--- a/secure_fw/partitions/initial_attestation/attest_token.h ++++ b/secure_fw/partitions/initial_attestation/attest_token.h +@@ -15,11 +15,14 @@ + + #include + #include "qcbor/qcbor.h" ++#include "attest_token_backend.h" ++#if !defined(TFM_COSE_BACKEND_WOLFCOSE) + #ifdef SYMMETRIC_INITIAL_ATTESTATION + #include "t_cose/t_cose_mac_compute.h" + #else + #include "t_cose/t_cose_sign1_sign.h" + #endif ++#endif + + #ifdef __cplusplus + extern "C" { +@@ -110,11 +113,19 @@ struct attest_token_encode_ctx { + /* Private data structure */ + QCBOREncodeContext cbor_enc_ctx; + int32_t key_select; ++#if defined(TFM_COSE_BACKEND_WOLFCOSE) ++ WOLFCOSE_KEY cose_key; ++ struct q_useful_buf out_buf; ++ struct q_useful_buf_c kid; ++ int32_t cose_alg_id; ++ psa_key_id_t private_key; ++#else + #ifdef SYMMETRIC_INITIAL_ATTESTATION + struct t_cose_mac_calculate_ctx mac_ctx; + #else + struct t_cose_sign1_sign_ctx signer_ctx; + #endif ++#endif + }; + + /** +diff --git a/secure_fw/partitions/initial_attestation/attest_token_backend.h b/secure_fw/partitions/initial_attestation/attest_token_backend.h +new file mode 100644 +index 000000000..152d5072c +--- /dev/null ++++ b/secure_fw/partitions/initial_attestation/attest_token_backend.h +@@ -0,0 +1,51 @@ ++#ifndef __ATTEST_TOKEN_BACKEND_H__ ++#define __ATTEST_TOKEN_BACKEND_H__ ++ ++#include "qcbor/qcbor.h" ++#include "psa/crypto.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* Encode the public part of key_id as a CBOR COSE_Key. Implemented by the ++ * selected backend. The SHA-256 of the result is the device Instance ID, so ++ * every backend must emit the same 4 entry map {1: 2, -1: crv, -2: x, -3: y} ++ * with no kid and no alg. Returns 0 on success. */ ++int attest_encode_cose_key(psa_key_id_t key_id, ++ struct q_useful_buf buf, ++ struct q_useful_buf_c *cose_key); ++ ++/* t_cose is the default so consumers predating the selector, such as the ++ * tf-m-tests token decoder, keep compiling unchanged. TFM_COSE_BACKEND is ++ * validated at configure time. */ ++#if defined(TFM_COSE_BACKEND_WOLFCOSE) ++ ++#include "psa/crypto.h" ++#include "wolfcose/wolfcose.h" ++ ++#define ATTEST_COSE_ALGORITHM_ES256 WOLFCOSE_ALG_ES256 ++#define ATTEST_COSE_ALGORITHM_ES384 WOLFCOSE_ALG_ES384 ++#define ATTEST_COSE_ALGORITHM_ES512 WOLFCOSE_ALG_ES512 ++#define ATTEST_COSE_ALGORITHM_HMAC256 WOLFCOSE_ALG_HMAC256 ++#define ATTEST_COSE_ALGORITHM_HMAC384 WOLFCOSE_ALG_HMAC384 ++#define ATTEST_COSE_ALGORITHM_HMAC512 WOLFCOSE_ALG_HMAC512 ++ ++#else ++ ++#include "t_cose/t_cose_common.h" ++ ++#define ATTEST_COSE_ALGORITHM_ES256 T_COSE_ALGORITHM_ES256 ++#define ATTEST_COSE_ALGORITHM_ES384 T_COSE_ALGORITHM_ES384 ++#define ATTEST_COSE_ALGORITHM_ES512 T_COSE_ALGORITHM_ES512 ++#define ATTEST_COSE_ALGORITHM_HMAC256 T_COSE_ALGORITHM_HMAC256 ++#define ATTEST_COSE_ALGORITHM_HMAC384 T_COSE_ALGORITHM_HMAC384 ++#define ATTEST_COSE_ALGORITHM_HMAC512 T_COSE_ALGORITHM_HMAC512 ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __ATTEST_TOKEN_BACKEND_H__ */ +diff --git a/secure_fw/partitions/initial_attestation/attest_token_encode.c b/secure_fw/partitions/initial_attestation/attest_token_encode_t_cose.c +similarity index 96% +rename from secure_fw/partitions/initial_attestation/attest_token_encode.c +rename to secure_fw/partitions/initial_attestation/attest_token_encode_t_cose.c +index d72d9880f..5f15781ba 100644 +--- a/secure_fw/partitions/initial_attestation/attest_token_encode.c ++++ b/secure_fw/partitions/initial_attestation/attest_token_encode_t_cose.c +@@ -365,3 +365,18 @@ void attest_token_encode_add_cbor(struct attest_token_encode_ctx *me, + { + QCBOREncode_AddEncodedToMapN(&(me->cbor_enc_ctx), label, *encoded); + } ++ ++#include "t_cose/t_cose_key.h" ++ ++/* Public function. See attest_token_backend.h */ ++int attest_encode_cose_key(psa_key_id_t key_id, ++ struct q_useful_buf buf, ++ struct q_useful_buf_c *cose_key) ++{ ++ struct t_cose_key attest_key; ++ ++ attest_key.key.handle = key_id; ++ ++ return (t_cose_key_encode(attest_key, buf, cose_key) == T_COSE_SUCCESS) ++ ? 0 : -1; ++} +diff --git a/secure_fw/partitions/initial_attestation/attest_token_encode_wolfcose.c b/secure_fw/partitions/initial_attestation/attest_token_encode_wolfcose.c +new file mode 100644 +index 000000000..9cc8e469e +--- /dev/null ++++ b/secure_fw/partitions/initial_attestation/attest_token_encode_wolfcose.c +@@ -0,0 +1,311 @@ ++#include "attest_token.h" ++#include "config_tfm.h" ++#include "qcbor/qcbor.h" ++#include "psa/crypto.h" ++#include "attest_key.h" ++#include "tfm_crypto_defs.h" ++#include ++#include ++ ++#ifdef SYMMETRIC_INITIAL_ATTESTATION ++#error "The wolfCOSE backend does not implement COSE_Mac0 attestation yet." ++#endif ++ ++/* One request is in flight per partition, so the claim set and the ++ * Sig_structure scratch are file scope rather than on the 2 KB partition ++ * stack. */ ++static uint8_t attest_claims_buf[PSA_INITIAL_ATTEST_MAX_TOKEN_SIZE]; ++static uint8_t attest_scratch_buf[PSA_INITIAL_ATTEST_MAX_TOKEN_SIZE + ++ WOLFCOSE_MAX_SCRATCH_SZ]; ++ ++/* psa_initial_attest_get_token_size() asks how large the token would be by ++ * passing a NULL out_buf (see attest_token.h). t_cose answers that from a size ++ * calculation mode; wolfCOSE has no such mode, so the token is built here ++ * instead and only its length reported. */ ++static uint8_t attest_sizing_buf[PSA_INITIAL_ATTEST_MAX_TOKEN_SIZE]; ++ ++static enum attest_token_err_t wolfcose_err_to_attest_err(int err) ++{ ++ switch (err) { ++ case WOLFCOSE_SUCCESS: ++ return ATTEST_TOKEN_ERR_SUCCESS; ++ ++ case WOLFCOSE_E_BUFFER_TOO_SMALL: ++ return ATTEST_TOKEN_ERR_TOO_SMALL; ++ ++ case WOLFCOSE_E_COSE_BAD_ALG: ++ return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG; ++ ++ default: ++ return ATTEST_TOKEN_ERR_GENERAL; ++ } ++} ++ ++/* Delegates the ECDSA to the crypto partition. The IAK is a builtin key id, so ++ * the private key never enters this partition. */ ++static int attest_wolfcose_sign_cb(void* cbCtx, int32_t alg, ++ const uint8_t* tbs, size_t tbsSz, ++ uint8_t* sig, size_t sigSz, size_t* sigLen) ++{ ++ psa_key_id_t key_id = *(psa_key_id_t *)cbCtx; ++ psa_algorithm_t psa_alg; ++ psa_status_t status; ++ size_t out_len = 0; ++ ++ switch (alg) { ++ case WOLFCOSE_ALG_ES256: ++ psa_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); ++ break; ++ case WOLFCOSE_ALG_ES384: ++ psa_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_384); ++ break; ++ case WOLFCOSE_ALG_ES512: ++ psa_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_512); ++ break; ++ default: ++ return -1; ++ } ++ ++ status = psa_sign_hash(key_id, psa_alg, tbs, tbsSz, sig, sigSz, &out_len); ++ if (status != PSA_SUCCESS) { ++ return -1; ++ } ++ ++ *sigLen = out_len; ++ return 0; ++} ++ ++enum attest_token_err_t ++attest_token_encode_start(struct attest_token_encode_ctx *me, ++ int32_t key_select, ++ int32_t cose_alg_id, ++ const struct q_useful_buf *out_buf) ++{ ++ enum psa_attest_err_t attest_ret; ++ struct q_useful_buf_c attest_key_id = NULL_Q_USEFUL_BUF_C; ++ struct q_useful_buf claims_buf; ++ int cose_ret; ++ ++ me->key_select = key_select; ++ me->cose_alg_id = cose_alg_id; ++ me->out_buf = *out_buf; ++ me->private_key = TFM_BUILTIN_KEY_ID_IAK; ++ ++ attest_ret = attest_get_initial_attestation_key_id(&attest_key_id); ++ if (attest_ret != PSA_ATTEST_ERR_SUCCESS) { ++ return ATTEST_TOKEN_ERR_GENERAL; ++ } ++ me->kid = attest_key_id; ++ ++ cose_ret = wc_CoseKey_Init(&me->cose_key); ++ if (cose_ret != WOLFCOSE_SUCCESS) { ++ return wolfcose_err_to_attest_err(cose_ret); ++ } ++ ++ me->cose_key.kty = WOLFCOSE_KTY_EC2; ++ switch (cose_alg_id) { ++ case WOLFCOSE_ALG_ES256: ++ me->cose_key.crv = WOLFCOSE_CRV_P256; ++ break; ++ case WOLFCOSE_ALG_ES384: ++ me->cose_key.crv = WOLFCOSE_CRV_P384; ++ break; ++ case WOLFCOSE_ALG_ES512: ++ me->cose_key.crv = WOLFCOSE_CRV_P521; ++ break; ++ default: ++ return ATTEST_TOKEN_ERR_UNSUPPORTED_SIG_ALG; ++ } ++ ++ cose_ret = wc_CoseKey_SetExtSigner(&me->cose_key, attest_wolfcose_sign_cb, ++ &me->private_key); ++ if (cose_ret != WOLFCOSE_SUCCESS) { ++ return wolfcose_err_to_attest_err(cose_ret); ++ } ++ ++ /* Claims are encoded on their own, then signed in one shot at finish. The ++ * borrowed-context and add_* callers are unaffected by this. */ ++ claims_buf.ptr = attest_claims_buf; ++ claims_buf.len = sizeof(attest_claims_buf); ++ QCBOREncode_Init(&(me->cbor_enc_ctx), claims_buf); ++ QCBOREncode_OpenMap(&(me->cbor_enc_ctx)); ++ ++ return ATTEST_TOKEN_ERR_SUCCESS; ++} ++ ++enum attest_token_err_t ++attest_token_encode_finish(struct attest_token_encode_ctx *me, ++ struct q_useful_buf_c *completed_token) ++{ ++ struct q_useful_buf_c claims; ++ QCBORError qcbor_result; ++ size_t token_len = 0; ++ uint8_t *out_ptr; ++ size_t out_sz; ++ int sizing; ++ int cose_ret; ++ ++ QCBOREncode_CloseMap(&(me->cbor_enc_ctx)); ++ ++ qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx), &claims); ++ if (qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) { ++ return ATTEST_TOKEN_ERR_TOO_SMALL; ++ } ++ else if (qcbor_result != QCBOR_SUCCESS) { ++ return ATTEST_TOKEN_ERR_CBOR_FORMATTING; ++ } ++ ++ sizing = (me->out_buf.ptr == NULL); ++ if (sizing) { ++ out_ptr = attest_sizing_buf; ++ out_sz = sizeof(attest_sizing_buf); ++ } ++ else { ++ out_ptr = (uint8_t *)me->out_buf.ptr; ++ out_sz = me->out_buf.len; ++ } ++ ++ /* rng is NULL: psa_sign_hash owns its randomness. */ ++ cose_ret = wc_CoseSign1_Sign(&me->cose_key, me->cose_alg_id, ++ me->kid.ptr, me->kid.len, ++ claims.ptr, claims.len, ++ NULL, 0, ++ NULL, 0, ++ attest_scratch_buf, sizeof(attest_scratch_buf), ++ out_ptr, out_sz, &token_len, ++ NULL); ++ if (cose_ret != WOLFCOSE_SUCCESS) { ++ return wolfcose_err_to_attest_err(cose_ret); ++ } ++ ++ /* In sizing mode only the length is meaningful; the caller passed no ++ * buffer to hand back. */ ++ completed_token->ptr = sizing ? NULL : me->out_buf.ptr; ++ completed_token->len = token_len; ++ ++ return ATTEST_TOKEN_ERR_SUCCESS; ++} ++ ++QCBOREncodeContext * ++attest_token_encode_borrow_cbor_cntxt(struct attest_token_encode_ctx *me) ++{ ++ return &(me->cbor_enc_ctx); ++} ++ ++void attest_token_encode_add_integer(struct attest_token_encode_ctx *me, ++ int32_t label, ++ int64_t value) ++{ ++ QCBOREncode_AddInt64ToMapN(&(me->cbor_enc_ctx), label, value); ++} ++ ++void attest_token_encode_add_bstr(struct attest_token_encode_ctx *me, ++ int32_t label, ++ const struct q_useful_buf_c *bstr) ++{ ++ QCBOREncode_AddBytesToMapN(&(me->cbor_enc_ctx), label, *bstr); ++} ++ ++void attest_token_encode_add_tstr(struct attest_token_encode_ctx *me, ++ int32_t label, ++ const struct q_useful_buf_c *tstr) ++{ ++ QCBOREncode_AddTextToMapN(&(me->cbor_enc_ctx), label, *tstr); ++} ++ ++void attest_token_encode_add_cbor(struct attest_token_encode_ctx *me, ++ int32_t label, ++ const struct q_useful_buf_c *encoded) ++{ ++ QCBOREncode_AddEncodedToMapN(&(me->cbor_enc_ctx), label, *encoded); ++} ++ ++/* Public function. See attest_token_backend.h */ ++int attest_encode_cose_key(psa_key_id_t key_id, ++ struct q_useful_buf buf, ++ struct q_useful_buf_c *cose_key) ++{ ++ uint8_t pub[1u + (2u * 66u)]; /* SEC1 uncompressed: 0x04 || X || Y */ ++ psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; ++ WOLFCOSE_KEY key; ++ ecc_key ecc; ++ size_t pub_len = 0; ++ size_t coord_len = 0; ++ size_t out_len = 0; ++ int32_t crv = 0; ++ int curve_id = 0; ++ int ecc_inited = 0; ++ int ret = 0; ++ ++ if (psa_get_key_attributes(key_id, &attr) != PSA_SUCCESS) { ++ ret = -1; ++ } ++ ++ if (ret == 0) { ++ switch (psa_get_key_bits(&attr)) { ++ case 256: ++ crv = WOLFCOSE_CRV_P256; coord_len = 32; curve_id = ECC_SECP256R1; ++ break; ++ case 384: ++ crv = WOLFCOSE_CRV_P384; coord_len = 48; curve_id = ECC_SECP384R1; ++ break; ++ case 521: ++ crv = WOLFCOSE_CRV_P521; coord_len = 66; curve_id = ECC_SECP521R1; ++ break; ++ default: ++ ret = -1; ++ break; ++ } ++ } ++ ++ if ((ret == 0) && ++ (psa_export_public_key(key_id, pub, sizeof(pub), &pub_len) ++ != PSA_SUCCESS)) { ++ ret = -1; ++ } ++ ++ /* Only the uncompressed form can be split into X and Y. */ ++ if ((ret == 0) && ++ ((pub_len != (1u + (2u * coord_len))) || (pub[0] != 0x04u))) { ++ ret = -1; ++ } ++ ++ if (ret == 0) { ++ if (wc_ecc_init(&ecc) != 0) { ++ ret = -1; ++ } ++ else { ++ ecc_inited = 1; ++ } ++ } ++ if ((ret == 0) && ++ (wc_ecc_import_unsigned(&ecc, &pub[1], &pub[1 + coord_len], NULL, ++ curve_id) != 0)) { ++ ret = -1; ++ } ++ if ((ret == 0) && (wc_CoseKey_Init(&key) != WOLFCOSE_SUCCESS)) { ++ ret = -1; ++ } ++ /* kid and alg stay unset: either would be inserted between kty and crv and ++ * change the Instance ID. */ ++ if ((ret == 0) && ++ (wc_CoseKey_SetEcc(&key, crv, &ecc) != WOLFCOSE_SUCCESS)) { ++ ret = -1; ++ } ++ if (ret == 0) { ++ key.hasPrivate = 0; ++ if (wc_CoseKey_Encode(&key, (uint8_t *)buf.ptr, buf.len, &out_len) ++ != WOLFCOSE_SUCCESS) { ++ ret = -1; ++ } ++ } ++ if (ret == 0) { ++ cose_key->ptr = buf.ptr; ++ cose_key->len = out_len; ++ } ++ ++ if (ecc_inited != 0) { ++ (void)wc_ecc_free(&ecc); ++ } ++ return ret; ++} +-- +2.49.0 +