From b656c6b6b49555f441f8228449a34c293d5c3d76 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Tue, 1 Sep 2026 13:23:59 +0100 Subject: [PATCH 1/4] Fix SE05x API, rotation, and attestation handling Add guarded lifecycle and locking APIs, policy-aware object insertion, SCP03 key rotation, session access, and nonce-bound attestation verification. Keep host-side key wrapping in software and preserve middleware chunking and object setup behavior. --- wolfcrypt/src/port/nxp/README_SE050.md | 299 ++- wolfcrypt/src/port/nxp/se050_port.c | 2696 +++++++++++++++++++---- wolfcrypt/src/wc_port.c | 23 +- wolfssl/wolfcrypt/port/nxp/se050_port.h | 162 ++ wolfssl/wolfcrypt/settings.h | 8 + 5 files changed, 2769 insertions(+), 419 deletions(-) diff --git a/wolfcrypt/src/port/nxp/README_SE050.md b/wolfcrypt/src/port/nxp/README_SE050.md index cc04349818f..d708d6b3458 100644 --- a/wolfcrypt/src/port/nxp/README_SE050.md +++ b/wolfcrypt/src/port/nxp/README_SE050.md @@ -24,7 +24,7 @@ wolfSSL uses the "EdgeLock SE05x Plug & Trust Middleware" to interface with SE050. This can be downloaded from the NXP website [here](https://www.nxp.com/products/security-and-authentication/authentication/edgelock-se050-plug-trust-secure-element-family-enhanced-iot-security-with-high-flexibility:SE050#design-resources). An free NXP account is required to download the middleware. -wolfSSL last tested with SE05x middleware version 04.02.00. +wolfSSL last tested with SE05x middleware version 04.07.01. Instructions for building will vary on target platform and host operating system. A Raspberry Pi with an NXP EdgeLock SE050 Development Kit can be used @@ -246,6 +246,18 @@ defined, wolfCrypt will instead fall back to using `/dev/random` and Disables using the SE050 for RSA, useful for the SE050E which does not have RSA support. +**`WOLFSSL_SE050_NO_ATTEST`** + +Removes the SE05x object-attestation and host-verification helpers. Define this +for small builds that do not provision or validate attested objects. + +**`WOLFSSL_SE050_SCP03_ROTATE`** + +Enables the destructive Platform SCP03 key-rotation APIs. This is deliberately +opt-in. The middleware must enable Platform SCP03 and HostCrypto, and wolfSSL +must be built with `WOLFSSL_SE050_INIT`. The seed-based helper additionally +requires HKDF (`--enable-hkdf` / `HAVE_HKDF`). + **`WOLFSSL_SE050_NO_ECDHE`** Disables offloading ECDH key generation and shared secret operations to the @@ -355,6 +367,36 @@ wolfSSL_Debugging_ON(); wolfCrypt_Cleanup(); ``` +An application using runtime Platform-SCP03 keys may need to authenticate to +the SE05x before global wolfCrypt initialization. In that case, +`wolfCrypt_Init()` detects and retains the already configured session instead +of attempting a second connection with the middleware's compiled-in keys. +`wolfCrypt_Cleanup()` closes sessions owned by wolfSSL, including sessions +opened by `wc_se050_init_ex()` before global initialization. For example: + +```c +wc_se050_scp03_keys activeKeys; +int ret; + +/* Recover activeKeys from protected storage, or derive them from a protected + * seed as shown in the Platform SCP03 section below. */ +ret = wc_se050_init_ex(NULL, &activeKeys); +/* Securely erase the temporary activeKeys copy here. */ +if (ret == 0) + ret = wolfCrypt_Init(); +if (ret != 0) { + (void)wc_se050_close(); + return ret; +} + +/* Use wolfCrypt and the authenticated SE05x session. */ + +return wolfCrypt_Cleanup(); +``` + +Sessions supplied by `wc_se050_set_config()` remain caller-owned and are not +closed by `wolfCrypt_Cleanup()`. + If `WOLFSSL_SE050_INIT` has not been defined when compiling wolfSSL, the following API can be called after wolfSSL library initialization to pass the correct pre-initialized `sss_session_t` and `sss_key_store_t` structure @@ -369,6 +411,39 @@ int wc_se050_set_config( sss_key_store_t *pKeyStore); ``` +### Accessing the wolfSSL SE05x Session + +Applications that need an SSS operation not wrapped by wolfSSL can retrieve +the configured objects with: + +```c +sss_session_t* wc_se050_get_session(void); +pSe05xSession_t wc_se050_get_se05x_session(void); +int wc_se050_get_config(sss_session_t** session, + sss_key_store_t** hostKeyStore, sss_key_store_t** keyStore); +``` + +When `WOLFSSL_SE050_INIT` is enabled, `wc_se050_close()` safely closes a +session opened by `wc_se050_init()` or `wc_se050_init_ex()` and clears the +configured pointers. It returns `BAD_STATE_E` when wolfSSL does not own the +active session. A second initialization attempt while any SE05x session is +configured also returns `BAD_STATE_E` instead of replacing or leaking it. + +The SE05x transport is shared with wolfCrypt. Threaded SE05x builds enable the +wolfCrypt hardware mutex by default. Every direct middleware call +through one of these pointers must be serialized with the same lock: + +```c +int ret = wc_se050_lock(); +if (ret == 0) { + /* Direct SSS or Se05x_API_* call. */ + wc_se050_unlock(); +} +``` + +Do not call another `wc_se050_*` or wolfCrypt hardware operation while holding +this lock; those functions acquire it internally. + ### wolfSSL SE050 Key Generation wolfSSL includes APIs for key generation when `WOLFSSL_KEY_GEN` has been @@ -416,6 +491,103 @@ These APIs will all return 0 on success or a negative error code on failure. The input to all these functions is a DER-encoded key and the size of that DER array in bytes. +### Provisioning and Generating Objects with Policies + +The `_ex` insertion variants accept permission flags and an authentication +object ID. They are available for ECC public/private keys, RSA public/private +keys, and binary objects. For example: + +```c +int ret = wc_se050_ecc_insert_private_key_ex(keyId, der, derSz, + WC_SE050_POLICY_ALLOW_READ | + WC_SE050_POLICY_ALLOW_SIGN | + WC_SE050_POLICY_ALLOW_ATTEST, + 0); /* auth object 0 grants the permissions to every authenticated user */ +``` + +Available flags are `WC_SE050_POLICY_ALLOW_DELETE`, `ALLOW_WRITE`, +`ALLOW_READ`, `ALLOW_SIGN`, `ALLOW_VERIFY`, `ALLOW_ENCRYPT`, `ALLOW_DECRYPT`, +`ALLOW_KA`, `ALLOW_KD`, `ALLOW_GEN`, `ALLOW_IMPORT_EXPORT`, `ALLOW_ATTEST`, +and `REQUIRE_SM`. Flags that do not apply to the object type are rejected. +`ALLOW_KD` grants HKDF on applet 7.2 and the general KDF permission on older +applets. The flag front end writes one applet policy record so common and +key-specific permissions remain combined across middleware versions, then +uses the normal middleware object writer (including binary chunking, RSA +component sequencing, and EC curve creation). +The corresponding `_policy` variants accept a complete middleware +`sss_policy_t` when the flag front end is not expressive enough. + +Applications can also generate persistent ECC and RSA key pairs entirely +inside the SE05x while attaching the policy at creation time: + +```c +int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize, + int curveId, word32 policyFlags, word32 authObjId); +int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize, + int curveId, const sss_policy_t* policy); + +int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e, + word32 policyFlags, word32 authObjId); +int wc_se050_rsa_generate_key_policy(word32 keyId, int size, long e, + const sss_policy_t* policy); +``` + +For ECC, `keySize` is in bytes and `curveId` is a wolfCrypt curve ID such as +`ECC_SECP256R1`. For RSA, `size` is in bits and the SE05x requires `e` to be +65537. Generation requires an unused provisioning ID below +`SE050_KEYID_START`; it never replaces an existing object. The private key is +generated on-chip and is not returned to the host. + +For example, generate a persistent P-256 signing key and then bind a wolfCrypt +key structure to it: + +```c +ecc_key key; +word32 keyId = 0x20; +int ret; + +ret = wc_se050_ecc_generate_key_ex(keyId, 32, ECC_SECP256R1, + WC_SE050_POLICY_ALLOW_DELETE | + WC_SE050_POLICY_ALLOW_READ | + WC_SE050_POLICY_ALLOW_SIGN | + WC_SE050_POLICY_ALLOW_VERIFY, 0); +if (ret == 0) + ret = wc_ecc_init(&key); +if (ret == 0) + ret = wc_ecc_use_key_id(&key, keyId, 0); + +/* Use key, then call wc_ecc_free(&key). The SE05x object remains persistent. */ +``` + +Include `ALLOW_READ` when the application will bind the object with +`wc_ecc_use_key_id()` or `wc_RsaUseKeyId()`, because those functions read the +public component. Include `ALLOW_DELETE` only when the provisioning lifecycle +must permit deletion. `ALLOW_GEN` grants the applet's regenerate permission, +but these wolfSSL helpers still require a new ID to prevent an accidental +replacement; use the direct middleware under `wc_se050_lock()` for an +intentional in-place regeneration allowed by a custom lifecycle. + +A zero flag value attaches no policy and preserves the old applet-default +behavior. Any nonzero policy is default-deny: every permission not granted is +denied. Policies are immutable after object creation; replacing one requires +deleting and recreating the object. Every `_ex` insertion, including a zero +flag value, requires an unused object ID and refuses to overwrite an existing +object. In particular: + +- Omitting `ALLOW_WRITE` prevents replacement of the value. +- Omitting `ALLOW_DELETE` makes normal deletion fail. The object remains until + an applet factory reset. Test no-delete policies on a development part first, + because an incorrect policy can permanently consume NV storage. +- `ALLOW_READ` does not make AES/symmetric secret values readable; SE05x + applets reject those reads regardless of policy. + +On applet 7.2 and later, `wc_se050_get_object_attributes()` returns the raw +applet attribute bytes, +including policy entries and origin, so provisioning code can verify what was +stored. It returns `NOT_COMPILED_IN` with older middleware configurations. +`wc_se050_erase_object()` returns `WC_HW_E` when deletion is denied; +the failure is the expected result for a no-delete object. + ### wolfSSL SE050 Certificate Insertion and Retrieval Applications can insert or retrieve certificates or binary data into an SE050 @@ -450,6 +622,130 @@ function `wc_se050_erase_object(int keyId)`. This function is available through ``, and should be passed the key ID to be deleted. +### Platform SCP03 Runtime Keys and Rotation + +For a middleware build configured with Platform SCP03, `wc_se050_init_ex()` +opens the wolfSSL-owned session using caller-supplied 16-byte ENC, MAC, and DEK +keys instead of the middleware's compiled defaults: + +```c +wc_se050_scp03_keys keys; +/* Load all three fields from protected, durable storage. */ +int ret = wc_se050_init_ex(NULL, &keys); +``` + +The port uses the configured middleware transport when its transport macro is +visible. Otherwise it uses T=1 over I2C, which is the documented/default SE05x +port configuration. + +When `HAVE_HKDF` is enabled, the active keys can be regenerated +deterministically from a protected seed without an open SE05x session: + +```c +int wc_se050_scp03_derive_keys_seed(const byte* seed, word32 seedSz, + wc_se050_scp03_keys* derivedOut); +``` + +This is the normal consecutive-power-cycle flow for seed-provisioned keys: + +```c +#define SCP03_SEED_SIZE 32 + +wc_se050_scp03_keys activeKeys; +byte seed[SCP03_SEED_SIZE]; +int ret; + +/* Load the same protected seed that was committed before key rotation. */ +ret = load_protected_scp03_seed(seed, sizeof(seed)); +if (ret == 0) + ret = wc_se050_scp03_derive_keys_seed(seed, sizeof(seed), &activeKeys); +/* Securely erase seed here. */ +if (ret == 0) + ret = wc_se050_init_ex(NULL, &activeKeys); +/* Securely erase activeKeys here. */ +if (ret == 0) + ret = wolfCrypt_Init(); +if (ret != 0) { + (void)wc_se050_close(); + return ret; +} + +/* Use wolfCrypt and SE05x. */ + +return wolfCrypt_Cleanup(); +``` + +The derive-only API does not change the SE05x and does not require +`WOLFSSL_SE050_SCP03_ROTATE`. It uses HKDF-SHA256 with an empty salt and the +three info strings listed below. The same seed therefore regenerates the same +ENC, MAC, and DEK values after every power cycle. The key-version byte is part +of the destructive PUT KEY operation, not this derivation or session setup. + +Key rotation is only compiled when `WOLFSSL_SE050_SCP03_ROTATE` is defined: + +```c +int wc_se050_scp03_rotate_keys(const wc_se050_scp03_keys* newKeys, + byte keyVersion); +int wc_se050_scp03_rotate_keys_seed(const byte* seed, word32 seedSz, + byte keyVersion, wc_se050_scp03_keys* derivedOut); +``` + +The direct API wraps all three new keys with the current DEK, closes the IoT +applet session, authenticates to the Supplementary Security Domain, sends one +secured GlobalPlatform PUT KEY command, and verifies the three returned KCVs. +It then returns with a fresh IoT applet session authenticated by the new keys. +The host AES block operation stages its input and output through 16-byte-aligned +buffers, which is required by strict-alignment hardware AES backends such as +STM32H7. +`WC_HW_E` means the PUT KEY command was rejected; `AES_GCM_AUTH_E` means the +returned KCVs did not match. The seed API uses this exact interoperable KDF: + +- HKDF-SHA256 with an empty salt and `seed` as IKM. +- Three 16-byte outputs using info strings `SE050 SCP03 ENC`, + `SE050 SCP03 MAC`, and `SE050 SCP03 DEK` respectively. + +SCP03 key loss makes the part inaccessible through Platform SCP03. Persist +directly supplied keys before calling the direct API. For the seed API, persist +the seed before calling; the derive-only API can recover the same key set on +every subsequent boot, so storing the derived keys is optional. After a +successful rotation, verify an operation on the new session before retiring +the old provisioning record. `wc_se050_close()` closes that new session +normally. Never test rotation on a production part. + +### Object Attestation and Provisioning Validation + +`wc_se050_attest_object()` performs an attested read using a caller-provisioned +attestation key. The attestation key's object policy must grant +`WC_SE050_POLICY_ALLOW_ATTEST`. Freshness is exactly 16 bytes; when `random` is +`NULL`, the SE05x TRNG supplies it unless that path was compiled out. + +```c +wc_se050_attst_result result; +int valid; + +ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, + freshness, sizeof(freshness), &result); +ret = wc_se050_verify_attestation(&result, attestPublicDer, + attestPublicDerSz, freshness, sizeof(freshness), &valid); +``` + +The verifier supports the pre-7.2 and 7.2+ signed-data formats and ECC or RSA +attestation public keys, including X25519 and Ed25519 object values. It requires +the independently retained 16-byte challenge and compares it with both the +host-side result metadata and every signed attestation component; a recorded +response therefore cannot be accepted for a new challenge. `result` contains +the returned object value, the challenge used for the request, parsed +origin/authentication ID/policy flags, object metadata, and the raw middleware +attestation records for remote verification. A valid signature proves the +response was signed by the corresponding attestation key; it does not establish +trust in that key. The application must validate the attestation key's +certificate/provisioning chain separately. + +`wc_se050_validate_provisioned_key()` combines a SHA-256 attested read, +signature verification, and exact DER public-key comparison. The attestation +key and certificate chain are customer-provisioned; reserved NXP credentials +are variant-specific and are not selected automatically. + ### wolfSSL SE050 Factory Reset If wolfSSL is compiled with `WOLFSSL_SE050_FACTORY_RESET` defined, when @@ -618,4 +914,3 @@ Once the build has finished, the `wolfcrypt_test` executable can be run with: $ cd /home/pi/se_mw/simw-top_build/raspbian_native_se050_t1oi2c/bin $ ./wolfcrypt_test ``` - diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index c1e057e5638..33ffe2a0cd2 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -37,6 +37,15 @@ #include #include +#ifdef HAVE_HKDF + #include +#endif + +#ifndef WOLFSSL_SE050_NO_ATTEST + #include + #include +#endif + #ifdef NO_INLINE #include #else @@ -52,6 +61,10 @@ #endif #include "ex_sss_boot.h" + #if defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 + #include "ex_sss_auth.h" + #endif #endif #ifdef HAVE_ECC @@ -61,7 +74,8 @@ #define SE050_ECC_DER_MAX 256 #endif #endif -#if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) +#if !defined(NO_RSA) && (!defined(WOLFSSL_SE050_NO_RSA) || \ + !defined(WOLFSSL_SE050_NO_ATTEST)) #include struct RsaKey; #endif @@ -80,10 +94,31 @@ static sss_session_t *cfg_se050_i2c_pi; static sss_key_store_t *gHostKeyStore; static sss_key_store_t *gKeyStore; +#ifdef WOLFSSL_SE050_INIT +static ex_sss_boot_ctx_t gBootCtx; +#endif + +#if defined(WOLFSSL_SE050_INIT) && \ + defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 + #define SE050_RUNTIME_SCP03 +#endif int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, sss_key_store_t *pKeyStore) { + int ret; + + if ((pSession == NULL) || (pKeyStore == NULL)) { + return BAD_FUNC_ARG; + } + ret = wolfSSL_CryptHwMutexInit(); + if (ret != 0) { + return ret; + } + WOLFSSL_MSG("Setting SE050 session configuration"); cfg_se050_i2c_pi = pSession; @@ -93,586 +128,2148 @@ int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, return 0; } -#ifdef WOLFSSL_SE050_INIT -int wc_se050_init(const char* portName) +int wc_se050_get_config(sss_session_t **pSession, + sss_key_store_t **pHostKeyStore, sss_key_store_t **pKeyStore) { - int ret; - sss_status_t status; - static ex_sss_boot_ctx_t pCtx; - - if (portName == NULL) { - portName = SE050_DEFAULT_PORT; + if ((cfg_se050_i2c_pi == NULL) || (gKeyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - status = ex_sss_boot_open(&pCtx, portName); - if (status == kStatus_SSS_Success) { - ret = wc_se050_set_config(&pCtx.session, - #if SSS_HAVE_HOSTCRYPTO_ANY - &pCtx.host_ks, - #else - NULL, - #endif - &pCtx.ks); - - #ifdef WOLFSSL_SE050_FACTORY_RESET - ex_sss_boot_factory_reset(&pCtx); - #endif + if (pSession != NULL) { + *pSession = cfg_se050_i2c_pi; } - else { - WOLFSSL_MSG("Failed to open SE050 context"); - ret = WC_HW_E; + if (pHostKeyStore != NULL) { + *pHostKeyStore = gHostKeyStore; } - return ret; + if (pKeyStore != NULL) { + *pKeyStore = gKeyStore; + } + + return 0; } -#endif -/** - * Erase and free an object stored in SE050. - * - * keyId ID of object to erase - * - * Returns 0 on success, negative on error. - */ -int wc_se050_erase_object(word32 id) +sss_session_t* wc_se050_get_session(void) { - int ret = 0; - sss_object_t object; - sss_key_store_t host_keystore; - sss_status_t status = kStatus_SSS_Success; + return cfg_se050_i2c_pi; +} -#ifdef SE050_DEBUG - printf("wc_se050_erase_object: id %d\n", id); +pSe05xSession_t wc_se050_get_se05x_session(void) +{ +#if SSS_HAVE_APPLET_SE05X_IOT + if ((cfg_se050_i2c_pi != NULL) && + (cfg_se050_i2c_pi->subsystem == kType_SSS_SE_SE05x)) { + return &((sss_se05x_session_t*)cfg_se050_i2c_pi)->s_ctx; + } #endif + return NULL; +} - if (cfg_se050_i2c_pi == NULL) { +int wc_se050_lock(void) +{ + return wolfSSL_CryptHwMutexLock(); +} + +void wc_se050_unlock(void) +{ + wolfSSL_CryptHwMutexUnLock(); +} + +enum se050_policy_object_type { + SE050_POLICY_OBJECT_ASYM, + SE050_POLICY_OBJECT_FILE +}; + +#define SE050_POLICY_MAX_ENTRIES 3U + +typedef struct se050_policy_set { + sss_policy_u entries[SE050_POLICY_MAX_ENTRIES]; + sss_policy_t policy; +} se050_policy_set; + +#define SE050_POLICY_COMMON_FLAGS (WC_SE050_POLICY_ALLOW_DELETE | \ + WC_SE050_POLICY_ALLOW_WRITE | WC_SE050_POLICY_ALLOW_READ | \ + WC_SE050_POLICY_REQUIRE_SM) +#define SE050_POLICY_ASYM_FLAGS (SE050_POLICY_COMMON_FLAGS | \ + WC_SE050_POLICY_ALLOW_SIGN | WC_SE050_POLICY_ALLOW_VERIFY | \ + WC_SE050_POLICY_ALLOW_ENCRYPT | WC_SE050_POLICY_ALLOW_DECRYPT | \ + WC_SE050_POLICY_ALLOW_KA | WC_SE050_POLICY_ALLOW_KD | \ + WC_SE050_POLICY_ALLOW_GEN | WC_SE050_POLICY_ALLOW_IMPORT_EXPORT | \ + WC_SE050_POLICY_ALLOW_ATTEST) + +static int se050_build_policy_set( + enum se050_policy_object_type objectType, word32 flags, + word32 authObjId, se050_policy_set* policySet) +{ + sss_policy_u* objectPolicy; + sss_policy_u* commonPolicy; + word32 allowedFlags; + word32 count = 0U; + + if (policySet == NULL) { return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + allowedFlags = (objectType == SE050_POLICY_OBJECT_ASYM) ? + SE050_POLICY_ASYM_FLAGS : SE050_POLICY_COMMON_FLAGS; + if ((flags & ~allowedFlags) != 0U) { + return BAD_FUNC_ARG; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, - SE050_KEYSTOREID_GENERIC); - } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&object, &host_keystore); + XMEMSET(policySet, 0, sizeof(*policySet)); + if (flags == 0U) { + return 0; + } + + objectPolicy = &policySet->entries[count++]; + objectPolicy->auth_obj_id = authObjId; + if (objectType == SE050_POLICY_OBJECT_ASYM) { + objectPolicy->type = KPolicy_Asym_Key; + objectPolicy->policy.asymmkey.can_Sign = + (flags & WC_SE050_POLICY_ALLOW_SIGN) != 0U; + objectPolicy->policy.asymmkey.can_Verify = + (flags & WC_SE050_POLICY_ALLOW_VERIFY) != 0U; + objectPolicy->policy.asymmkey.can_Encrypt = + (flags & WC_SE050_POLICY_ALLOW_ENCRYPT) != 0U; + objectPolicy->policy.asymmkey.can_Decrypt = + (flags & WC_SE050_POLICY_ALLOW_DECRYPT) != 0U; + objectPolicy->policy.asymmkey.can_KA = + (flags & WC_SE050_POLICY_ALLOW_KA) != 0U; + objectPolicy->policy.asymmkey.can_Gen = + (flags & WC_SE050_POLICY_ALLOW_GEN) != 0U; + objectPolicy->policy.asymmkey.can_Import_Export = + (flags & WC_SE050_POLICY_ALLOW_IMPORT_EXPORT) != 0U; + objectPolicy->policy.asymmkey.can_Attest = + (flags & WC_SE050_POLICY_ALLOW_ATTEST) != 0U; + #if !defined(SSS_HAVE_SE05X_VER_GTE_07_02) || \ + !SSS_HAVE_SE05X_VER_GTE_07_02 + objectPolicy->policy.asymmkey.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + objectPolicy->policy.asymmkey.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; + objectPolicy->policy.asymmkey.can_KD = + (flags & WC_SE050_POLICY_ALLOW_KD) != 0U; + #else + if ((flags & WC_SE050_POLICY_ALLOW_KD) != 0U) { + sss_policy_u* derivePolicy = &policySet->entries[count++]; + + derivePolicy->type = KPolicy_Sym_Key; + derivePolicy->auth_obj_id = authObjId; + derivePolicy->policy.symmkey.can_HKDF = 1; + } + #endif } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&object, id); + else { + objectPolicy->type = KPolicy_File; + objectPolicy->policy.file.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + objectPolicy->policy.file.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; + } + + commonPolicy = &policySet->entries[count++]; + commonPolicy->type = KPolicy_Common; + commonPolicy->auth_obj_id = authObjId; + commonPolicy->policy.common.can_Delete = + (flags & WC_SE050_POLICY_ALLOW_DELETE) != 0U; + commonPolicy->policy.common.req_Sm = + (flags & WC_SE050_POLICY_REQUIRE_SM) != 0U; +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + if (objectType == SE050_POLICY_OBJECT_ASYM) { + commonPolicy->policy.common.can_Read = + (flags & WC_SE050_POLICY_ALLOW_READ) != 0U; + commonPolicy->policy.common.can_Write = + (flags & WC_SE050_POLICY_ALLOW_WRITE) != 0U; } - if (status == kStatus_SSS_Success) { - sss_key_store_erase_key(&host_keystore, &object); - sss_key_object_free(&object); +#endif + + policySet->policy.nPolicies = count; + for (count = 0U; count < policySet->policy.nPolicies; count++) { + policySet->policy.policies[count] = &policySet->entries[count]; } - wolfSSL_CryptHwMutexUnLock(); - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; + return 0; +} + +/* Called only while the shared transport mutex is held. */ +static sss_status_t se050_require_new_object(word32 keyId) +{ + pSe05xSession_t session = wc_se050_get_se05x_session(); + SE05x_Result_t exists = kSE05x_Result_NA; + smStatus_t status; + + if (session == NULL) { + return kStatus_SSS_Fail; } + status = Se05x_API_CheckObjectExists(session, keyId, &exists); + if ((status != SM_OK) || (exists != kSE05x_Result_FAILURE)) { + return kStatus_SSS_Fail; + } + return kStatus_SSS_Success; +} - return ret; +#ifdef WOLFSSL_SE050_INIT +static int se050_boot_context_is_open(void) +{ + return (cfg_se050_i2c_pi != NULL) || + (gBootCtx.session.subsystem != kType_SSS_SubSystem_NONE); } -word32 se050_allocate_key(int keyType) +static sss_key_store_t* se050_boot_host_key_store(void) { - word32 keyId = 0; - static word32 keyId_allocator = SE050_KEYID_START; - switch (keyType) { - case SE050_AES_KEY: - case SE050_ECC_KEY: - case SE050_RSA_KEY: - case SE050_ED25519_KEY: - case SE050_CURVE25519_KEY: - case SE050_ANY_KEY: - keyId = keyId_allocator++; - break; +#if defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY + if (gBootCtx.host_ks.session != NULL) { + return &gBootCtx.host_ks; } -#ifdef SE050_DEBUG - printf("se050_allocate_key: keyId %d\n", keyId); #endif - return keyId; + return NULL; } -#if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) -int se050_get_random_number(uint32_t count, uint8_t* rand_out) +#ifdef SE050_RUNTIME_SCP03 +static int se050_set_scp03_static_keys(const wc_se050_scp03_keys* keys) { - int ret = 0; + NXSCP03_StaticCtx_t* staticCtx; sss_status_t status; - sss_rng_context_t rng; - -#ifdef SE050_DEBUG - printf("se050_get_random_number: %p (%d)\n", rand_out, count); -#endif - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if (keys == NULL) { + return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Enc.keyStore == NULL) || + (staticCtx->Mac.keyStore == NULL) || + (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - status = sss_rng_context_init(&rng, cfg_se050_i2c_pi); + + status = sss_host_key_store_set_key(&gBootCtx.host_ks, &staticCtx->Enc, + keys->enc, sizeof(keys->enc), sizeof(keys->enc) * 8U, NULL, 0); if (status == kStatus_SSS_Success) { - status = sss_rng_get_random(&rng, rand_out, count); + status = sss_host_key_store_set_key(&gBootCtx.host_ks, + &staticCtx->Mac, keys->mac, sizeof(keys->mac), + sizeof(keys->mac) * 8U, NULL, 0); } if (status == kStatus_SSS_Success) { - status = sss_rng_context_free(&rng); + status = sss_host_key_store_set_key(&gBootCtx.host_ks, + &staticCtx->Dek, keys->dek, sizeof(keys->dek), + sizeof(keys->dek) * 8U, NULL, 0); } if (status != kStatus_SSS_Success) { - ret = RNG_FAILURE_E; + return WC_HW_E; } - wolfSSL_CryptHwMutexUnLock(); - - return ret; -} -#endif /* !WC_NO_RNG && !WOLFSSL_SE050_NO_TRNG */ - -#ifdef WOLFSSL_SE050_HASH - -/* Used for sha/sha224/sha384/sha512 */ -int se050_hash_init(SE050_HASH_Context* se050Ctx, void* heap) -{ - se050Ctx->heap = heap; - se050Ctx->len = 0; - se050Ctx->used = 0; - se050Ctx->msg = NULL; + staticCtx->key_len = (int)sizeof(keys->enc); return 0; } -int se050_hash_copy(SE050_HASH_Context* src, SE050_HASH_Context* dst) +#ifdef WOLFSSL_SE050_SCP03_ROTATE +static int se050_set_scp03_dek(const byte* dek, word32 dekSz) { - if (src == NULL || dst == NULL || (src->used != dst->used)) { + NXSCP03_StaticCtx_t* staticCtx; + sss_status_t status; + + if ((dek == NULL) || + (dekSz != sizeof(((wc_se050_scp03_keys*)0)->dek))) { return BAD_FUNC_ARG; } - - if (src->used > 0) { - /* dst->msg points to same buffer as src->msg, needs to be allocated - * and dep copied over instead of plain pointer copy */ - dst->msg = (byte*)XMALLOC(src->used, dst->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (dst->msg == NULL) { - PRINTF("Tried to allocate %d bytes\n", dst->used); - return MEMORY_E; - } - XMEMSET(dst->msg, 0, dst->used); - XMEMCPY(dst->msg, src->msg, src->used); - dst->used = src->used; - dst->len = src->used; - } else { - dst->msg = NULL; - dst->len = 0; - dst->used = 0; + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - - return 0; + status = sss_host_key_store_set_key(&gBootCtx.host_ks, &staticCtx->Dek, + dek, dekSz, dekSz * 8U, NULL, 0); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; } +#endif -int se050_hash_update(SE050_HASH_Context* se050Ctx, const byte* data, word32 len) +static sss_status_t se050_open_scp03(const char* portName, + const wc_se050_scp03_keys* keys, int skipSelectApplet) { - byte* tmp = NULL; - word32 usedSz = 0; + SE_Connect_Ctx_t* connectCtx = &gBootCtx.se05x_open_ctx; + sss_status_t status; + int ret; - if (se050Ctx == NULL || (len > 0 && data == NULL) || (len == 0) || - !WC_SAFE_SUM_WORD32(se050Ctx->used, len, usedSz)) { - return BAD_FUNC_ARG; + status = ex_sss_se05x_prepare_host(&gBootCtx.host_session, + &gBootCtx.host_ks, connectCtx, &gBootCtx.ex_se05x_auth, + kSSS_AuthType_SCP03); + if (status != kStatus_SSS_Success) { + return status; } - if (se050Ctx->len < usedSz) { - if (se050Ctx->msg == NULL) { - se050Ctx->msg = (byte*)XMALLOC(usedSz, - se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (se050Ctx->msg == NULL) { - return MEMORY_E; - } - XMEMSET(se050Ctx->msg, 0, usedSz); - } - else { - tmp = (byte*)XMALLOC(usedSz, se050Ctx->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (tmp == NULL) { - return MEMORY_E; - } - XMEMSET(tmp, 0, usedSz); - XMEMCPY(tmp, se050Ctx->msg, se050Ctx->used); - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - se050Ctx->msg = tmp; - } - se050Ctx->len = usedSz; + /* PUT KEY is a Security Domain operation. NXP's reference rotation + * application opens SCP03 with applet selection skipped, which makes the + * transport select the SSD before INITIALIZE UPDATE. Normal wolfCrypt + * operations must continue to select the IoT applet. */ + connectCtx->skip_select_applet = (skipSelectApplet != 0); + +#if defined(SMCOM_JRCP_V1) + if (ex_sss_boot_isSocketPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_JRCP_V1; + connectCtx->portName = portName; + } +#endif +#if defined(SMCOM_JRCP_V2) + if (ex_sss_boot_isSocketPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_JRCP_V2; + connectCtx->portName = portName; + } +#endif +#if defined(RJCT_VCOM) + if (ex_sss_boot_isSerialPortName(portName)) { + connectCtx->connType = kType_SE_Conn_Type_VCOM; + connectCtx->portName = portName; } +#endif +#if defined(SCI2C) + #error "SCI2C is not a valid SE05x connection for runtime SCP03 keys" +#endif +#if defined(T1oI2C) + connectCtx->connType = kType_SE_Conn_Type_T1oI2C; + connectCtx->portName = portName; +#endif +#if defined(SMCOM_PCSC) + connectCtx->connType = kType_SE_Conn_Type_PCSC; + connectCtx->portName = portName; +#endif +#if defined(SMCOM_PN7150) + connectCtx->connType = kType_SE_Conn_Type_NFC; + connectCtx->portName = NULL; +#endif +#if !defined(SMCOM_JRCP_V1) && !defined(SMCOM_JRCP_V2) && \ + !defined(RJCT_VCOM) && !defined(T1oI2C) && !defined(SMCOM_PCSC) && \ + !defined(SMCOM_PN7150) + /* wolfSSL's --with-se050 build consumes an already-configured + * middleware and therefore does not inherit its private transport + * define. T=1 over I2C is the port's documented/default transport. */ + connectCtx->connType = kType_SE_Conn_Type_T1oI2C; + connectCtx->portName = portName; +#endif - XMEMCPY(se050Ctx->msg + se050Ctx->used, data, len); - se050Ctx->used += len; + ret = se050_set_scp03_static_keys(keys); + if (ret != 0) { + return kStatus_SSS_Fail; + } - return 0; + return sss_session_open(&gBootCtx.session, kType_SSS_SE_SE05x, 0, + kSSS_ConnectionType_Encrypted, connectCtx); } +#endif /* SE050_RUNTIME_SCP03 */ -int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, - sss_algorithm_t algo) +int wc_se050_init(const char* portName) { - int ret; + int ret; sss_status_t status; - sss_digest_t digest_ctx; - const byte* data = se050Ctx->msg; - int size = (se050Ctx->used) / SSS_BLOCK_SIZE; - int leftover = (se050Ctx->used) % SSS_BLOCK_SIZE; - const byte* blocks = data; - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if (se050_boot_context_is_open()) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + if (portName == NULL) { + portName = SE050_DEFAULT_PORT; } - status = sss_digest_context_init(&digest_ctx, cfg_se050_i2c_pi, algo, - kMode_SSS_Digest); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + status = ex_sss_boot_open(&gBootCtx, portName); if (status == kStatus_SSS_Success) { - status = sss_digest_init(&digest_ctx); + status = ex_sss_key_store_and_object_init(&gBootCtx); } if (status == kStatus_SSS_Success) { - /* used to send chunks of size 512 */ - while (status == kStatus_SSS_Success && size--) { - status = sss_digest_update(&digest_ctx, blocks, SSS_BLOCK_SIZE); - blocks += SSS_BLOCK_SIZE; - } - if (status == kStatus_SSS_Success && leftover) { - status = sss_digest_update(&digest_ctx, blocks, leftover); +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && defined(SE050_RUNTIME_SCP03) + if (gBootCtx.se05x_open_ctx.auth.authType == kSSS_AuthType_SCP03) { + byte defaultDek[] = EX_SSS_AUTH_SE05X_KEY_DEK; + + ret = se050_set_scp03_dek(defaultDek, + (word32)sizeof(defaultDek)); + ForceZero(defaultDek, sizeof(defaultDek)); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + return ret; + } } - if (status == kStatus_SSS_Success) { - status = sss_digest_finish(&digest_ctx, hash, &digestLen); +#endif + ret = wc_se050_set_config(&gBootCtx.session, + se050_boot_host_key_store(), &gBootCtx.ks); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); } - sss_digest_context_free(&digest_ctx); - } - if (status == kStatus_SSS_Success) { - /* reset state */ - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - ret = se050_hash_init(se050Ctx, se050Ctx->heap); - } else { + #ifdef WOLFSSL_SE050_FACTORY_RESET + if (ret == 0) { + ex_sss_boot_factory_reset(&gBootCtx); + } + #endif + } + else { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + WOLFSSL_MSG("Failed to open SE050 context"); ret = WC_HW_E; } + return ret; +} - wolfSSL_CryptHwMutexUnLock(); +#ifdef SE050_RUNTIME_SCP03 +static int se050_init_scp03_mode(const char* portName, + const wc_se050_scp03_keys* keys, int skipSelectApplet) +{ + sss_status_t status; + int ret; + + if (keys == NULL) { + return BAD_FUNC_ARG; + } + if (se050_boot_context_is_open()) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + if (portName == NULL) { + portName = SE050_DEFAULT_PORT; + } + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + status = se050_open_scp03(portName, keys, skipSelectApplet); + if (status == kStatus_SSS_Success) { + status = ex_sss_key_store_and_object_init(&gBootCtx); + } + if (status == kStatus_SSS_Success) { + ret = wc_se050_set_config(&gBootCtx.session, + se050_boot_host_key_store(), &gBootCtx.ks); + if (ret != 0) { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + } + } + else { + ex_sss_session_close(&gBootCtx); + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + WOLFSSL_MSG("Failed to open SE050 runtime SCP03 context"); + ret = WC_HW_E; + } return ret; } -void se050_hash_free(SE050_HASH_Context* se050Ctx) +int wc_se050_init_ex(const char* portName, const wc_se050_scp03_keys* keys) { - XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); - se050Ctx->msg = NULL; - se050Ctx->len = 0; - se050Ctx->used = 0; + return se050_init_scp03_mode(portName, keys, 0); } +#endif -#endif /* WOLFSSL_SE050_HASH */ +int wc_se050_close(void) +{ + int ret; -#if defined(WOLFSSL_SE050_CRYPT) && !defined(NO_AES) + if (cfg_se050_i2c_pi != &gBootCtx.session) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + ex_sss_session_close(&gBootCtx); + cfg_se050_i2c_pi = NULL; + gHostKeyStore = NULL; + gKeyStore = NULL; + XMEMSET(&gBootCtx, 0, sizeof(gBootCtx)); + wolfSSL_CryptHwMutexUnLock(); + return 0; +} +#endif -int se050_aes_set_key(Aes* aes, const byte* key, word32 keylen, - const byte* iv, int dir) +#ifdef HAVE_HKDF +int wc_se050_scp03_derive_keys_seed(const byte* seed, word32 seedSz, + wc_se050_scp03_keys* derivedOut) { - int ret = 0; - sss_status_t status; - sss_object_t newKey; - sss_key_store_t host_keystore; - word32 keyId; - int keyCreated = 0; + static const byte encInfo[] = "SE050 SCP03 ENC"; + static const byte macInfo[] = "SE050 SCP03 MAC"; + static const byte dekInfo[] = "SE050 SCP03 DEK"; + int ret; - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + if ((seed == NULL) || (seedSz == 0U) || (derivedOut == NULL)) { + return BAD_FUNC_ARG; } - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + XMEMSET(derivedOut, 0, sizeof(*derivedOut)); + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, encInfo, + (word32)sizeof(encInfo) - 1U, derivedOut->enc, + sizeof(derivedOut->enc)); + if (ret == 0) { + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, macInfo, + (word32)sizeof(macInfo) - 1U, derivedOut->mac, + sizeof(derivedOut->mac)); + } + if (ret == 0) { + ret = wc_HKDF(WC_SHA256, seed, seedSz, NULL, 0, dekInfo, + (word32)sizeof(dekInfo) - 1U, derivedOut->dek, + sizeof(derivedOut->dek)); + } + if (ret != 0) { + ForceZero(derivedOut, sizeof(*derivedOut)); } + return ret; +} +#endif /* HAVE_HKDF */ + +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + defined(SE050_RUNTIME_SCP03) +#define SE050_SCP03_KEY_SZ 16U +#define SE050_SCP03_KCV_SZ 3U +#define SE050_SCP03_KEY_BLOCK_SZ 23U +#define SE050_SCP03_PUT_KEY_SZ \ + (1U + (3U * SE050_SCP03_KEY_BLOCK_SZ)) +#define SE050_SCP03_CMD_CAPACITY \ + (SE050_SCP03_PUT_KEY_SZ + AES_BLOCK_SIZE) + +static int se050_scp03_get_static_key(sss_object_t* object, byte* key, + size_t keySz) +{ + size_t outSz = keySz; + size_t outBits = keySz * 8U; + sss_status_t status; - (void)dir; - (void)iv; + if ((object == NULL) || (object->keyStore == NULL) || (key == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + status = sss_host_key_store_get_key(&gBootCtx.host_ks, object, key, + &outSz, &outBits); + if ((status != kStatus_SSS_Success) || (outSz != keySz)) { + ForceZero(key, keySz); + return WC_HW_E; + } + return 0; +} - aes->rounds = keylen/4 + 6; +static int se050_scp03_get_static_keys(wc_se050_scp03_keys* keys) +{ + NXSCP03_StaticCtx_t* staticCtx; + int ret; - /* free existing key in slot first before storing new one */ - ret = wc_se050_erase_object(aes->keyId); - if (ret != 0) { - wolfSSL_CryptHwMutexUnLock(); - return ret; + if (keys == NULL) { + return BAD_FUNC_ARG; } - aes->keyIdSet = 0; - - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if (staticCtx == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&newKey, &host_keystore); + ret = se050_scp03_get_static_key(&staticCtx->Enc, keys->enc, + sizeof(keys->enc)); + if (ret == 0) { + ret = se050_scp03_get_static_key(&staticCtx->Mac, keys->mac, + sizeof(keys->mac)); } - if (status == kStatus_SSS_Success) { - keyId = se050_allocate_key(SE050_AES_KEY); - status = sss_key_object_allocate_handle(&newKey, keyId, - kSSS_KeyPart_Default, kSSS_CipherType_AES, keylen, - kKeyObject_Mode_Transient); + if (ret == 0) { + ret = se050_scp03_get_static_key(&staticCtx->Dek, keys->dek, + sizeof(keys->dek)); } - if (status == kStatus_SSS_Success) { - status = sss_key_store_set_key(&host_keystore, &newKey, key, keylen, - keylen * 8, NULL, 0); + if (ret != 0) { + ForceZero(keys, sizeof(*keys)); } + return ret; +} - if (status == kStatus_SSS_Success) { - keyCreated = 1; - aes->keyId = keyId; - aes->keyIdSet = 1; - ret = 0; - } - else { - if (keyCreated) { - sss_key_store_erase_key(&host_keystore, &newKey); - sss_key_object_free(&newKey); +static int se050_scp03_encrypt_block(const byte* key, const byte* in, + byte* out) +{ + Aes aes; + int ret; + /* in/out point into byte-packed APDU command buffers and are not + * guaranteed to be 4-byte aligned. HW-accelerated AES backends (e.g. + * STM32_CRYPTO) cast these pointers to uint32_t* internally, so an + * unaligned buffer here faults. Stage through aligned local buffers. */ + ALIGN16 byte alignedIn[SE050_SCP03_KEY_SZ]; + ALIGN16 byte alignedOut[SE050_SCP03_KEY_SZ]; + + XMEMSET(&aes, 0, sizeof(aes)); +#if defined(WOLFSSL_SE050_CRYPT) && defined(HAVE_AESGCM) + /* SCP03 static keys are host secrets. Never route DEK wrapping or KCV + * generation back through the secure element. */ + aes.useSWCrypt = 1; +#endif + XMEMCPY(alignedIn, in, sizeof(alignedIn)); + ret = wc_AesSetKey(&aes, key, SE050_SCP03_KEY_SZ, NULL, AES_ENCRYPTION); + if (ret == 0) { + ret = wc_AesEncryptDirect(&aes, alignedOut, alignedIn); + if (ret == 0) { + XMEMCPY(out, alignedOut, sizeof(alignedOut)); } - ret = WC_HW_E; } - - wolfSSL_CryptHwMutexUnLock(); - + ForceZero(alignedIn, sizeof(alignedIn)); + ForceZero(alignedOut, sizeof(alignedOut)); + wc_AesFree(&aes); return ret; } -int se050_aes_crypt(Aes* aes, const byte* in, byte* out, word32 sz, int dir, - sss_algorithm_t algorithm) +static int se050_scp03_make_key_block(const byte* newKey, + const byte* currentDek, byte* block, byte* kcv) { - int ret = 0; - sss_status_t status; - sss_object_t keyObject; - sss_key_store_t host_keystore; + byte checkInput[SE050_SCP03_KEY_SZ]; + int ret; - if (cfg_se050_i2c_pi == NULL) { - return WC_HW_E; + block[0] = 0x88; /* GlobalPlatform AES key type. */ + block[1] = SE050_SCP03_KEY_SZ + 1U; + block[2] = SE050_SCP03_KEY_SZ; + ret = se050_scp03_encrypt_block(currentDek, newKey, &block[3]); + if (ret == 0) { + XMEMSET(checkInput, 1, sizeof(checkInput)); + ret = se050_scp03_encrypt_block(newKey, checkInput, kcv); } - if (aes->keyIdSet == 0) { - return BAD_FUNC_ARG; + if (ret == 0) { + block[3U + SE050_SCP03_KEY_SZ] = SE050_SCP03_KCV_SZ; + XMEMCPY(&block[4U + SE050_SCP03_KEY_SZ], kcv, + SE050_SCP03_KCV_SZ); } + ForceZero(checkInput, sizeof(checkInput)); + return ret; +} - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; - } +static int se050_scp03_put_keys(const wc_se050_scp03_keys* newKeys, + byte keyVersion, int* keysChanged) +{ + byte cmd[SE050_SCP03_CMD_CAPACITY]; + byte expected[1U + (3U * SE050_SCP03_KCV_SZ)]; + byte response[64]; + byte currentDek[SE050_SCP03_KEY_SZ]; + size_t responseSz = sizeof(response); + size_t currentDekSz = sizeof(currentDek); + size_t currentDekBits = sizeof(currentDek) * 8U; + NXSCP03_StaticCtx_t* staticCtx; + sss_se05x_session_t* session; + tlvHeader_t header = {{0x80, 0xD8, 0, 0x81}}; + sss_status_t status; + smStatus_t smStatus; + word32 i; + int ret; - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + if ((newKeys == NULL) || (keysChanged == NULL)) { + return BAD_FUNC_ARG; } - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&keyObject, &host_keystore); + *keysChanged = 0; + if ((cfg_se050_i2c_pi != &gBootCtx.session) || + (gBootCtx.session.subsystem != kType_SSS_SE_SE05x) || + (gBootCtx.se05x_open_ctx.auth.authType != kSSS_AuthType_SCP03) || + (gBootCtx.se05x_open_ctx.skip_select_applet != 1)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&keyObject, aes->keyId); + + staticCtx = gBootCtx.se05x_open_ctx.auth.ctx.scp03.pStatic_ctx; + if ((staticCtx == NULL) || (staticCtx->Dek.keyStore == NULL)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - /* The first call to this function needs an initialization call, - * subsequent calls just need to call update */ - if (status == kStatus_SSS_Success && aes->ctxInitDone == 0) { - sss_mode_t mode; + status = sss_host_key_store_get_key(&gBootCtx.host_ks, + &staticCtx->Dek, currentDek, ¤tDekSz, ¤tDekBits); + if ((status != kStatus_SSS_Success) || + (currentDekSz != sizeof(currentDek))) { + ForceZero(currentDek, sizeof(currentDek)); + return WC_HW_E; + } - XMEMSET(&mode, 0, sizeof(mode)); - if (dir == AES_DECRYPTION) - mode = kMode_SSS_Decrypt; - else if (dir == AES_ENCRYPTION) - mode = kMode_SSS_Encrypt; + cmd[0] = keyVersion; + expected[0] = keyVersion; + for (i = 0; i < 3U; i++) { + const byte* key = (i == 0U) ? newKeys->enc : + ((i == 1U) ? newKeys->mac : newKeys->dek); + byte kcv[SE050_SCP03_KEY_SZ]; - if (status == kStatus_SSS_Success) { - status = sss_symmetric_context_init(&aes->aes_ctx, - cfg_se050_i2c_pi, &keyObject, algorithm, mode); - } - if (status == kStatus_SSS_Success) { - aes->ctxInitDone = 1; - status = sss_cipher_init(&aes->aes_ctx, (uint8_t*)aes->reg, - sizeof(aes->reg)); + ret = se050_scp03_make_key_block(key, currentDek, + &cmd[1U + (i * SE050_SCP03_KEY_BLOCK_SZ)], kcv); + if (ret != 0) { + ForceZero(kcv, sizeof(kcv)); + ForceZero(currentDek, sizeof(currentDek)); + ForceZero(cmd, sizeof(cmd)); + return ret; } + XMEMCPY(&expected[1U + (i * SE050_SCP03_KCV_SZ)], kcv, + SE050_SCP03_KCV_SZ); + ForceZero(kcv, sizeof(kcv)); } - if (status == kStatus_SSS_Success) { - size_t outSz = (size_t)sz; - status = sss_cipher_update(&aes->aes_ctx, in, sz, out, &outSz); - } + ForceZero(currentDek, sizeof(currentDek)); - ret = (status == kStatus_SSS_Success) ? 0 : WC_HW_E; + header.hdr[2] = keyVersion; + session = (sss_se05x_session_t*)&gBootCtx.session; + ret = wc_se050_lock(); + if (ret == 0) { + smStatus = DoAPDUTxRx_s_Case4(&session->s_ctx, &header, cmd, + SE050_SCP03_PUT_KEY_SZ, response, &responseSz); + wc_se050_unlock(); + } + else { + smStatus = SM_NOT_OK; + } + ForceZero(cmd, sizeof(cmd)); - wolfSSL_CryptHwMutexUnLock(); + if ((ret != 0) || (smStatus != SM_OK) || (responseSz < 2U) || + ((((word32)response[responseSz - 2U] << 8) | + response[responseSz - 1U]) != SM_OK)) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return WC_HW_E; + } + /* A successful status means the Security Domain has committed the key + * set, even if the response below is malformed or its KCV echo does not + * match. The caller must reconnect with newKeys in that case. */ + *keysChanged = 1; + if (responseSz < (sizeof(expected) + 2U)) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return WC_HW_E; + } + if (ConstantCompare(response, expected, sizeof(expected)) != 0) { + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); + return AES_GCM_AUTH_E; + } + ForceZero(expected, sizeof(expected)); + ForceZero(response, sizeof(response)); - return ret; + return 0; } -void se050_aes_free(Aes* aes) +int wc_se050_scp03_rotate_keys(const wc_se050_scp03_keys* newKeys, + byte keyVersion) { - if (aes == NULL) { - return; + wc_se050_scp03_keys currentKeys; + const wc_se050_scp03_keys* reopenKeys; + const char* portName; + int keysChanged = 0; + int closeRet; + int reopenRet; + int ret; + + if (newKeys == NULL) { + return BAD_FUNC_ARG; + } + if ((cfg_se050_i2c_pi != &gBootCtx.session) || + (gBootCtx.session.subsystem != kType_SSS_SE_SE05x) || + (gBootCtx.se05x_open_ctx.auth.authType != kSSS_AuthType_SCP03) || + (gBootCtx.se05x_open_ctx.skip_select_applet != 0)) { + return WC_NO_ERR_TRACE(BAD_STATE_E); } - if (aes->ctxInitDone) { - sss_symmetric_context_free(&aes->aes_ctx); + portName = gBootCtx.se05x_open_ctx.portName; + ret = se050_scp03_get_static_keys(¤tKeys); + if (ret != 0) { + return ret; + } - /* sets back to zero to indicate that a free has been called */ - aes->ctxInitDone = 0; + /* Platform SCP03 protects both the IoT applet and its Security Domain, + * but PUT KEY is accepted only by the latter. Reopen against the SSD for + * the update, then always return to a fresh IoT applet session. */ + ret = wc_se050_close(); + if (ret == 0) { + ret = se050_init_scp03_mode(portName, ¤tKeys, 1); + } + if (ret == 0) { + ret = se050_scp03_put_keys(newKeys, keyVersion, &keysChanged); + closeRet = wc_se050_close(); + if (closeRet != 0) { + ret = closeRet; + } } - aes->keyId = 0; - aes->keyIdSet = 0; + if (gBootCtx.session.subsystem == kType_SSS_SubSystem_NONE) { + reopenKeys = (keysChanged != 0) ? newKeys : ¤tKeys; + reopenRet = se050_init_scp03_mode(portName, reopenKeys, 0); + if (reopenRet != 0) { + ret = reopenRet; + } + } + ForceZero(¤tKeys, sizeof(currentKeys)); + return ret; } -#endif /* WOLFSSL_SE050_CRYPT && !NO_AES */ - -/** - * Get size of a SE05X secure object at specified object ID. - * - * keystore SE050 keystore associated with object - * keyId SE050 key ID in which object is stored - * - * Size returned depends on object type: - * ECC key: curve size - * RSA/AES/DES/HMAC key: key size - * Binary file: file size - * - * Return size or negative on error - */ -static int se050_get_object_size(sss_key_store_t* keystore, word32 keyId) +#ifdef HAVE_HKDF +int wc_se050_scp03_rotate_keys_seed(const byte* seed, word32 seedSz, + byte keyVersion, wc_se050_scp03_keys* derivedOut) { - uint16_t size = 0; - smStatus_t status = SM_NOT_OK; - sss_se05x_key_store_t* se05x_keystore = NULL; + wc_se050_scp03_keys keys; + int ret; - if (keystore == NULL) { - return BAD_FUNC_ARG; + ret = wc_se050_scp03_derive_keys_seed(seed, seedSz, &keys); + if ((ret == 0) && (derivedOut != NULL)) { + XMEMCPY(derivedOut, &keys, sizeof(keys)); } - - se05x_keystore = (sss_se05x_key_store_t*)keystore; - status = Se05x_API_ReadSize(&se05x_keystore->session->s_ctx, - keyId, &size); - if (status != SM_OK) { - return WC_HW_E; + if (ret == 0) { + ret = wc_se050_scp03_rotate_keys(&keys, keyVersion); } - - return (int)size; + ForceZero(&keys, sizeof(keys)); + return ret; } +#endif /* HAVE_HKDF */ +#endif /* WOLFSSL_SE050_SCP03_ROTATE && SE050_RUNTIME_SCP03 */ /** - * Insert binary object into SE050 as persistent object. + * Erase and free an object stored in SE050. * - * keyId SE050 key ID to store object in - * object binary object data - * objectSz size of binary object, bytes + * keyId ID of object to erase * - * Returns 0 on success, negative on error + * Returns 0 on success, negative on error. */ -int wc_se050_insert_binary_object(word32 keyId, const byte* object, - word32 objectSz) +int wc_se050_erase_object(word32 id) { - int ret = 0; - sss_object_t newObj; + int ret = 0; + sss_object_t object; sss_key_store_t host_keystore; sss_status_t status = kStatus_SSS_Success; - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; +#ifdef SE050_DEBUG + printf("wc_se050_erase_object: id %d\n", id); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return BAD_FUNC_ARG; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_GENERIC); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&object, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&object, id); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_erase_key(&host_keystore, &object); + sss_key_object_free(&object); + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } + + return ret; +} + +word32 se050_allocate_key(int keyType) +{ + word32 keyId = 0; + static word32 keyId_allocator = SE050_KEYID_START; + switch (keyType) { + case SE050_AES_KEY: + case SE050_ECC_KEY: + case SE050_RSA_KEY: + case SE050_ED25519_KEY: + case SE050_CURVE25519_KEY: + case SE050_ANY_KEY: + keyId = keyId_allocator++; + break; + } +#ifdef SE050_DEBUG + printf("se050_allocate_key: keyId %d\n", keyId); +#endif + return keyId; +} + +#if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) +int se050_get_random_number(uint32_t count, uint8_t* rand_out) +{ + int ret = 0; + sss_status_t status; + sss_rng_context_t rng; + +#ifdef SE050_DEBUG + printf("se050_get_random_number: %p (%d)\n", rand_out, count); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + status = sss_rng_context_init(&rng, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_rng_get_random(&rng, rand_out, count); + } + if (status == kStatus_SSS_Success) { + status = sss_rng_context_free(&rng); + } + if (status != kStatus_SSS_Success) { + ret = RNG_FAILURE_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} +#endif /* !WC_NO_RNG && !WOLFSSL_SE050_NO_TRNG */ + +#ifdef WOLFSSL_SE050_HASH + +/* Used for sha/sha224/sha384/sha512 */ +int se050_hash_init(SE050_HASH_Context* se050Ctx, void* heap) +{ + se050Ctx->heap = heap; + se050Ctx->len = 0; + se050Ctx->used = 0; + se050Ctx->msg = NULL; + return 0; +} + +int se050_hash_copy(SE050_HASH_Context* src, SE050_HASH_Context* dst) +{ + if (src == NULL || dst == NULL || (src->used != dst->used)) { + return BAD_FUNC_ARG; + } + + if (src->used > 0) { + /* dst->msg points to same buffer as src->msg, needs to be allocated + * and dep copied over instead of plain pointer copy */ + dst->msg = (byte*)XMALLOC(src->used, dst->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (dst->msg == NULL) { + PRINTF("Tried to allocate %d bytes\n", dst->used); + return MEMORY_E; + } + XMEMSET(dst->msg, 0, dst->used); + XMEMCPY(dst->msg, src->msg, src->used); + dst->used = src->used; + dst->len = src->used; + } else { + dst->msg = NULL; + dst->len = 0; + dst->used = 0; + } + + return 0; +} + +int se050_hash_update(SE050_HASH_Context* se050Ctx, const byte* data, word32 len) +{ + byte* tmp = NULL; + word32 usedSz = 0; + + if (se050Ctx == NULL || (len > 0 && data == NULL) || (len == 0) || + !WC_SAFE_SUM_WORD32(se050Ctx->used, len, usedSz)) { + return BAD_FUNC_ARG; + } + + if (se050Ctx->len < usedSz) { + if (se050Ctx->msg == NULL) { + se050Ctx->msg = (byte*)XMALLOC(usedSz, + se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (se050Ctx->msg == NULL) { + return MEMORY_E; + } + XMEMSET(se050Ctx->msg, 0, usedSz); + } + else { + tmp = (byte*)XMALLOC(usedSz, se050Ctx->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) { + return MEMORY_E; + } + XMEMSET(tmp, 0, usedSz); + XMEMCPY(tmp, se050Ctx->msg, se050Ctx->used); + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + se050Ctx->msg = tmp; + } + se050Ctx->len = usedSz; + } + + XMEMCPY(se050Ctx->msg + se050Ctx->used, data, len); + se050Ctx->used += len; + + return 0; +} + +int se050_hash_final(SE050_HASH_Context* se050Ctx, byte* hash, size_t digestLen, + sss_algorithm_t algo) +{ + int ret; + sss_status_t status; + sss_digest_t digest_ctx; + const byte* data = se050Ctx->msg; + int size = (se050Ctx->used) / SSS_BLOCK_SIZE; + int leftover = (se050Ctx->used) % SSS_BLOCK_SIZE; + const byte* blocks = data; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_digest_context_init(&digest_ctx, cfg_se050_i2c_pi, algo, + kMode_SSS_Digest); + if (status == kStatus_SSS_Success) { + status = sss_digest_init(&digest_ctx); + } + if (status == kStatus_SSS_Success) { + /* used to send chunks of size 512 */ + while (status == kStatus_SSS_Success && size--) { + status = sss_digest_update(&digest_ctx, blocks, SSS_BLOCK_SIZE); + blocks += SSS_BLOCK_SIZE; + } + if (status == kStatus_SSS_Success && leftover) { + status = sss_digest_update(&digest_ctx, blocks, leftover); + } + if (status == kStatus_SSS_Success) { + status = sss_digest_finish(&digest_ctx, hash, &digestLen); + } + sss_digest_context_free(&digest_ctx); + } + + if (status == kStatus_SSS_Success) { + /* reset state */ + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + ret = se050_hash_init(se050Ctx, se050Ctx->heap); + } else { + ret = WC_HW_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +void se050_hash_free(SE050_HASH_Context* se050Ctx) +{ + XFREE(se050Ctx->msg, se050Ctx->heap, DYNAMIC_TYPE_TMP_BUFFER); + se050Ctx->msg = NULL; + se050Ctx->len = 0; + se050Ctx->used = 0; +} + +#endif /* WOLFSSL_SE050_HASH */ + +#if defined(WOLFSSL_SE050_CRYPT) && !defined(NO_AES) + +int se050_aes_set_key(Aes* aes, const byte* key, word32 keylen, + const byte* iv, int dir) +{ + int ret = 0; + sss_status_t status; + sss_object_t newKey; + sss_key_store_t host_keystore; + word32 keyId; + int keyCreated = 0; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + (void)dir; + (void)iv; + + aes->rounds = keylen/4 + 6; + + /* free existing key in slot first before storing new one */ + ret = wc_se050_erase_object(aes->keyId); + if (ret != 0) { + wolfSSL_CryptHwMutexUnLock(); + return ret; + } + aes->keyIdSet = 0; + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&newKey, &host_keystore); + } + if (status == kStatus_SSS_Success) { + keyId = se050_allocate_key(SE050_AES_KEY); + status = sss_key_object_allocate_handle(&newKey, keyId, + kSSS_KeyPart_Default, kSSS_CipherType_AES, keylen, + kKeyObject_Mode_Transient); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_set_key(&host_keystore, &newKey, key, keylen, + keylen * 8, NULL, 0); + } + + if (status == kStatus_SSS_Success) { + keyCreated = 1; + aes->keyId = keyId; + aes->keyIdSet = 1; + ret = 0; + } + else { + if (keyCreated) { + sss_key_store_erase_key(&host_keystore, &newKey); + sss_key_object_free(&newKey); + } + ret = WC_HW_E; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +int se050_aes_crypt(Aes* aes, const byte* in, byte* out, word32 sz, int dir, + sss_algorithm_t algorithm) +{ + int ret = 0; + sss_status_t status; + sss_object_t keyObject; + sss_key_store_t host_keystore; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if (aes->keyIdSet == 0) { + return BAD_FUNC_ARG; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, SE050_KEYSTOREID_AES); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyObject, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&keyObject, aes->keyId); + } + + /* The first call to this function needs an initialization call, + * subsequent calls just need to call update */ + if (status == kStatus_SSS_Success && aes->ctxInitDone == 0) { + sss_mode_t mode; + + XMEMSET(&mode, 0, sizeof(mode)); + if (dir == AES_DECRYPTION) + mode = kMode_SSS_Decrypt; + else if (dir == AES_ENCRYPTION) + mode = kMode_SSS_Encrypt; + + if (status == kStatus_SSS_Success) { + status = sss_symmetric_context_init(&aes->aes_ctx, + cfg_se050_i2c_pi, &keyObject, algorithm, mode); + } + if (status == kStatus_SSS_Success) { + aes->ctxInitDone = 1; + status = sss_cipher_init(&aes->aes_ctx, (uint8_t*)aes->reg, + sizeof(aes->reg)); + } + } + if (status == kStatus_SSS_Success) { + size_t outSz = (size_t)sz; + status = sss_cipher_update(&aes->aes_ctx, in, sz, out, &outSz); + } + + ret = (status == kStatus_SSS_Success) ? 0 : WC_HW_E; + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +void se050_aes_free(Aes* aes) +{ + if (aes == NULL) { + return; + } + + if (aes->ctxInitDone) { + sss_symmetric_context_free(&aes->aes_ctx); + + /* sets back to zero to indicate that a free has been called */ + aes->ctxInitDone = 0; + } + + aes->keyId = 0; + aes->keyIdSet = 0; +} + +#endif /* WOLFSSL_SE050_CRYPT && !NO_AES */ + +/** + * Get size of a SE05X secure object at specified object ID. + * + * keystore SE050 keystore associated with object + * keyId SE050 key ID in which object is stored + * + * Size returned depends on object type: + * ECC key: curve size + * RSA/AES/DES/HMAC key: key size + * Binary file: file size + * + * Return size or negative on error + */ +static int se050_get_object_size(sss_key_store_t* keystore, word32 keyId) +{ + uint16_t size = 0; + smStatus_t status = SM_NOT_OK; + sss_se05x_key_store_t* se05x_keystore = NULL; + + if (keystore == NULL) { + return BAD_FUNC_ARG; + } + + se05x_keystore = (sss_se05x_key_store_t*)keystore; + status = Se05x_API_ReadSize(&se05x_keystore->session->s_ctx, + keyId, &size); + if (status != SM_OK) { + return WC_HW_E; + } + + return (int)size; +} + +/** + * Insert binary object into SE050 as persistent object. + * + * keyId SE050 key ID to store object in + * object binary object data + * objectSz size of binary object, bytes + * + * Returns 0 on success, negative on error + */ +static int se050_insert_binary_object(word32 keyId, const byte* object, + word32 objectSz, const sss_policy_t* policy, int requireNew) +{ + int ret = 0; + sss_object_t newObj; + sss_key_store_t host_keystore; + sss_status_t status = kStatus_SSS_Success; + + if ((cfg_se050_i2c_pi == NULL) || (object == NULL) || (objectSz == 0U)) { + return BAD_FUNC_ARG; + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + /* Avoid key ID conflicts with temporary key storage */ + if (keyId >= SE050_KEYID_START) { + wolfSSL_CryptHwMutexUnLock(); + return BAD_FUNC_ARG; + } + + if (requireNew) { + status = se050_require_new_object(keyId); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&newObj, &host_keystore); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&newObj, keyId, + kSSS_KeyPart_Default, kSSS_CipherType_Binary, objectSz, + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_set_key(&host_keystore, &newObj, object, + objectSz, (objectSz * 8), (void*)policy, 0); + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } + + return ret; +} + +int wc_se050_insert_binary_object_policy(word32 keyId, const byte* object, + word32 objectSz, const sss_policy_t* policy) +{ + return se050_insert_binary_object(keyId, object, objectSz, policy, 0); +} + +int wc_se050_insert_binary_object_ex(word32 keyId, const byte* object, + word32 objectSz, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_FILE, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_insert_binary_object(keyId, object, objectSz, policy, 1); +} + +int wc_se050_insert_binary_object(word32 keyId, const byte* object, + word32 objectSz) +{ + return wc_se050_insert_binary_object_policy(keyId, object, objectSz, + NULL); +} + +/** + * Get binary object from SE050 from specified key ID. + * + * keyId SE050 key ID to get binary object from + * out output buffer to place binary object + * outSz size of output buffer on input, size of written object on output + * + * Returns 0 on success, LENGTH_ONLY_E if out is NULL with outSz set to + * required buffer size, and other negative on error. + */ +int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz) +{ + int ret = 0; + sss_object_t object; + sss_key_store_t host_keystore; + sss_status_t status = kStatus_SSS_Success; + size_t outBitSz = 0; + + /* If out is NULL, outSz set to required size and LENGTH_ONLY_E returned */ + if (outSz == NULL) { + return BAD_FUNC_ARG; } - /* Avoid key ID conflicts with temporary key storage */ - if (keyId >= SE050_KEYID_START) { + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&object, &host_keystore); + } + if (status == kStatus_SSS_Success) { + ret = se050_get_object_size(&host_keystore, keyId); + if (ret < 0) { + status = kStatus_SSS_Fail; + } + else { + if (out == NULL) { + *outSz = ret; + wolfSSL_CryptHwMutexUnLock(); + return WC_NO_ERR_TRACE(LENGTH_ONLY_E); + } + if ((word32)ret > *outSz) { + WOLFSSL_MSG("Output buffer not large enough for object"); + wolfSSL_CryptHwMutexUnLock(); + return BAD_LENGTH_E; + } + ret = 0; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_get_handle(&object, keyId); + } + if (status == kStatus_SSS_Success) { + outBitSz = (*outSz) * 8; + status = sss_key_store_get_key(&host_keystore, &object, out, + (size_t*)outSz, &outBitSz); + } + wolfSSL_CryptHwMutexUnLock(); + + if (status != kStatus_SSS_Success) { + ret = WC_HW_E; + } + + return ret; +} + +int wc_se050_get_object_attributes(word32 keyId, byte* attr, word32* attrSz) +{ +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + pSe05xSession_t session; + smStatus_t status; + size_t size; + + if ((attr == NULL) || (attrSz == NULL) || (*attrSz == 0U)) { + return BAD_FUNC_ARG; + } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + session = wc_se050_get_se05x_session(); + if (session == NULL) { wolfSSL_CryptHwMutexUnLock(); + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + size = *attrSz; + status = Se05x_API_ReadObjectAttributes(session, keyId, attr, &size); + *attrSz = (word32)size; + wolfSSL_CryptHwMutexUnLock(); + + return (status == SM_OK) ? 0 : WC_HW_E; +#else + (void)keyId; + (void)attr; + (void)attrSz; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#endif +} + +#ifndef WOLFSSL_SE050_NO_ATTEST + +#define SE050_ATTEST_RANDOM_SIZE 16U +#define SE050_ATTR_FIXED_SIZE 14U +#define SE050_ATTR_POLICY_MIN 8U +#define SE050_TLV_OVERHEAD 4U + +static word32 se050_get_u32(const byte* in) +{ + return ((word32)in[0] << 24) | ((word32)in[1] << 16) | + ((word32)in[2] << 8) | (word32)in[3]; +} + +static int se050_attest_algorithm(enum wc_HashType hashAlgo, + word32 cipherType, sss_algorithm_t* algorithm) +{ + int isRsa; + + if (algorithm == NULL) { return BAD_FUNC_ARG; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); + isRsa = (cipherType == (word32)kSSS_CipherType_RSA) || + (cipherType == (word32)kSSS_CipherType_RSA_CRT); + + if (hashAlgo == WC_HASH_TYPE_SHA) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA1 : + kAlgorithm_SSS_ECDSA_SHA1; + } + else if (hashAlgo == WC_HASH_TYPE_SHA224) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA224 : + kAlgorithm_SSS_ECDSA_SHA224; + } + else if (hashAlgo == WC_HASH_TYPE_SHA256) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA256 : + kAlgorithm_SSS_ECDSA_SHA256; + } + else if (hashAlgo == WC_HASH_TYPE_SHA384) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA384 : + kAlgorithm_SSS_ECDSA_SHA384; + } + else if (hashAlgo == WC_HASH_TYPE_SHA512) { + *algorithm = isRsa ? kAlgorithm_SSS_RSASSA_PKCS1_V1_5_SHA512 : + kAlgorithm_SSS_ECDSA_SHA512; + } + else { + return BAD_FUNC_ARG; + } + + if (!isRsa && + (cipherType != (word32)kSSS_CipherType_EC_NIST_P) && + (cipherType != (word32)kSSS_CipherType_EC_NIST_K) && + (cipherType != (word32)kSSS_CipherType_EC_BRAINPOOL)) { + return BAD_FUNC_ARG; + } + + return 0; +} + +static void se050_parse_attested_attributes(const byte* attr, word32 attrSz, + wc_se050_attst_result* result) +{ + word32 i; + word32 authId = 0U; + word32 header; + word32 flags = 0U; + word32 entryLen; + int haveAuthId = 0; + + if ((attr == NULL) || (result == NULL) || + (attrSz < (SE050_ATTR_FIXED_SIZE + 1U))) { + return; + } + + i = SE050_ATTR_FIXED_SIZE; + while ((i < attrSz) && (attr[i] >= SE050_ATTR_POLICY_MIN)) { + entryLen = attr[i]; + if ((entryLen > (attrSz - i - 1U)) || (entryLen < 8U)) { + return; + } + if (!haveAuthId) { + authId = se050_get_u32(attr + i + 1U); + haveAuthId = 1; + } + header = se050_get_u32(attr + i + 5U); + if ((header & 0x00040000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_DELETE; + if ((header & 0x00100000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_WRITE; + if ((header & 0x00200000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_READ; + if ((header & 0x10000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_SIGN; + if ((header & 0x08000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_VERIFY; + if ((header & 0x02000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_ENCRYPT; + if ((header & 0x01000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_DECRYPT; + if ((header & 0x04000000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_KA; + if ((header & 0x00800000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_KD; + if ((header & 0x00080000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_GEN; + if ((header & 0x00001000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_IMPORT_EXPORT; + if ((header & 0x00008000U) != 0U) + flags |= WC_SE050_POLICY_ALLOW_ATTEST; + if ((header & 0x00020000U) != 0U) + flags |= WC_SE050_POLICY_REQUIRE_SM; + i += entryLen + 1U; + } + + if (i < attrSz) { + result->origin = attr[i]; + } + result->authObjId = authId; + result->policyFlags = flags; +} + +int wc_se050_attest_object(word32 keyId, word32 attestKeyId, + enum wc_HashType hashAlgo, const byte* random, word32 randomSz, + wc_se050_attst_result* result) +{ + int ret = 0; + int i; + byte generatedRandom[SE050_ATTEST_RANDOM_SIZE]; + const byte* freshness = random; + word32 freshnessSz = randomSz; + size_t valueSz; + size_t valueBitSz = 0; + sss_algorithm_t algorithm = kAlgorithm_None; + sss_key_store_t keyStore; + sss_object_t object; + sss_object_t attestObject; + sss_se05x_object_t* seObject; + sss_se05x_object_t* seAttestObject; + sss_status_t status = kStatus_SSS_Fail; + + if ((result == NULL) || (keyId == attestKeyId) || + ((random == NULL) && (randomSz != 0U)) || + ((random != NULL) && (randomSz != SE050_ATTEST_RANDOM_SIZE))) { + return BAD_FUNC_ARG; + } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + if (freshness == NULL) { + #if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) + ret = se050_get_random_number((word32)sizeof(generatedRandom), + generatedRandom); + if (ret != 0) { + return ret; + } + freshness = generatedRandom; + freshnessSz = (word32)sizeof(generatedRandom); + #else + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); + #endif + } + + XMEMSET(result, 0, sizeof(*result)); + result->hashAlgo = hashAlgo; + XMEMCPY(result->freshness, freshness, SE050_ATTEST_RANDOM_SIZE); + for (i = 0; i < SE05X_MAX_ATTST_DATA; i++) { + result->raw.data[i].attributeLen = + sizeof(result->raw.data[i].attribute); + result->raw.data[i].chipIdLen = sizeof(result->raw.data[i].chipId); + result->raw.data[i].signatureLen = + sizeof(result->raw.data[i].signature); + result->raw.data[i].timeStampLen = + sizeof(result->raw.data[i].timeStamp); + #if SSS_HAVE_SE05X_VER_GTE_07_02 + result->raw.data[i].cmdLen = sizeof(result->raw.data[i].cmd); + result->raw.data[i].objSizeLen = + sizeof(result->raw.data[i].objSize); + #else + result->raw.data[i].outrandomLen = + sizeof(result->raw.data[i].outrandom); + #endif + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = sss_key_store_context_init(&keyStore, cfg_se050_i2c_pi); + if (status == kStatus_SSS_Success) + status = sss_key_object_init(&object, &keyStore); + if (status == kStatus_SSS_Success) + status = sss_key_object_get_handle(&object, keyId); + if (status == kStatus_SSS_Success) + status = sss_key_object_init(&attestObject, &keyStore); + if (status == kStatus_SSS_Success) + status = sss_key_object_get_handle(&attestObject, attestKeyId); + + seObject = (sss_se05x_object_t*)&object; + seAttestObject = (sss_se05x_object_t*)&attestObject; if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&newObj, &host_keystore); + ret = se050_attest_algorithm(hashAlgo, seAttestObject->cipherType, + &algorithm); + if (ret != 0) + status = kStatus_SSS_Fail; } if (status == kStatus_SSS_Success) { - status = sss_key_object_allocate_handle(&newObj, keyId, - kSSS_KeyPart_Default, kSSS_CipherType_Binary, objectSz, - kKeyObject_Mode_Persistent); + result->cipherType = seObject->cipherType; + result->objectType = seObject->objectType; + result->curveId = seObject->curve_id; + valueSz = sizeof(result->value); + status = sss_se05x_key_store_get_key_attst( + (sss_se05x_key_store_t*)&keyStore, seObject, result->value, + &valueSz, &valueBitSz, seAttestObject, algorithm, + (byte*)freshness, freshnessSz, &result->raw); + if (status == kStatus_SSS_Success) { + result->valueSz = (word32)valueSz; + } + } + wolfSSL_CryptHwMutexUnLock(); + + ForceZero(generatedRandom, sizeof(generatedRandom)); + if (status != kStatus_SSS_Success) { + return (ret != 0) ? ret : WC_HW_E; + } + if ((result->raw.valid_number == 0U) || + (result->raw.valid_number > SE05X_MAX_ATTST_DATA)) { + return WC_HW_E; + } + + se050_parse_attested_attributes(result->raw.data[0].attribute, + (word32)result->raw.data[0].attributeLen, result); + return 0; +} + +static int se050_attested_component(const wc_se050_attst_result* result, + word32 componentIndex, byte* component, word32* componentSz) +{ + int ret; + + if ((result == NULL) || (component == NULL) || (componentSz == NULL) || + (componentIndex >= result->raw.valid_number)) { + return BAD_FUNC_ARG; + } + + if ((result->cipherType == (word32)kSSS_CipherType_RSA) || + (result->cipherType == (word32)kSSS_CipherType_RSA_CRT)) { + #ifndef NO_RSA + RsaKey key; + word32 idx = 0; + byte exponent[8]; + word32 exponentSz = sizeof(exponent); + word32 modulusSz = *componentSz; + + ret = wc_InitRsaKey(&key, NULL); + if (ret == 0) + ret = wc_RsaPublicKeyDecode(result->value, &idx, &key, + result->valueSz); + if (ret == 0) { + ret = wc_RsaFlattenPublicKey(&key, exponent, &exponentSz, + component, &modulusSz); + } + if (ret == 0) { + if (componentIndex == 0U) { + *componentSz = modulusSz; + } + else if (*componentSz >= exponentSz) { + XMEMCPY(component, exponent, exponentSz); + *componentSz = exponentSz; + } + else { + ret = BUFFER_E; + } + } + wc_FreeRsaKey(&key); + ForceZero(exponent, sizeof(exponent)); + return ret; + #else + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); + #endif + } + + if ((result->cipherType == (word32)kSSS_CipherType_EC_NIST_P) || + (result->cipherType == (word32)kSSS_CipherType_EC_NIST_K) || + (result->cipherType == (word32)kSSS_CipherType_EC_BRAINPOOL)) { + #ifdef HAVE_ECC + ecc_key key; + word32 idx = 0; + + ret = wc_ecc_init(&key); + if (ret == 0) + ret = wc_EccPublicKeyDecode(result->value, &idx, &key, + result->valueSz); + if (ret == 0) + ret = wc_ecc_export_x963(&key, component, componentSz); + wc_ecc_free(&key); + return ret; + #else + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); + #endif + } + + if ((result->cipherType == + (word32)kSSS_CipherType_EC_MONTGOMERY) || + (result->cipherType == + (word32)kSSS_CipherType_EC_TWISTED_ED)) { + word32 i; + word32 rawSz = 32U; + + /* Plug & Trust prepends SubjectPublicKeyInfo DER and reverses these + * little-endian applet values after attestation. The signature is + * over the original applet bytes, so strip DER and undo that reverse. */ + if ((result->cipherType == + (word32)kSSS_CipherType_EC_MONTGOMERY) && + (result->valueSz > 56U)) { + rawSz = 56U; /* X448 */ + } + if ((result->valueSz < rawSz) || (*componentSz < rawSz)) { + return BUFFER_E; + } + for (i = 0U; i < rawSz; i++) { + component[i] = result->value[result->valueSz - 1U - i]; + } + *componentSz = rawSz; + return 0; + } + + if (*componentSz < result->valueSz) { + return BUFFER_E; + } + XMEMCPY(component, result->value, result->valueSz); + *componentSz = result->valueSz; + return 0; +} + +static int se050_attest_append(byte* out, word32 outSz, word32* offset, + const byte* in, word32 inSz) +{ + if ((out == NULL) || (offset == NULL) || + ((in == NULL) && (inSz != 0U)) || (*offset > outSz) || + (inSz > (outSz - *offset))) { + return BUFFER_E; + } + if (inSz != 0U) { + XMEMCPY(out + *offset, in, inSz); + *offset += inSz; + } + return 0; +} + +#if SSS_HAVE_SE05X_VER_GTE_07_02 +static int se050_attest_append_tlv(byte* out, word32 outSz, word32* offset, + byte tag, const byte* value, word32 valueSz) +{ + byte header[SE050_TLV_OVERHEAD]; + int ret; + + if (valueSz > 0xFFFFU) { + return BAD_LENGTH_E; + } + header[0] = tag; + header[1] = 0x82; + header[2] = (byte)(valueSz >> 8); + header[3] = (byte)valueSz; + ret = se050_attest_append(out, outSz, offset, header, sizeof(header)); + if (ret == 0) + ret = se050_attest_append(out, outSz, offset, value, valueSz); + return ret; +} +#endif + +static int se050_build_attestation_data(const wc_se050_attst_result* result, + word32 componentIndex, byte* component, word32 componentSz, + byte* signedData, word32 signedDataSz, word32* used) +{ +#ifdef NO_HASH_WRAPPER + (void)result; + (void)componentIndex; + (void)component; + (void)componentSz; + (void)signedData; + (void)signedDataSz; + (void)used; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#else + int ret = 0; + word32 offset = 0; + const sss_se05x_attst_comp_data_t* data; + + if ((result == NULL) || (signedData == NULL) || (used == NULL) || + (componentIndex >= result->raw.valid_number)) { + return BAD_FUNC_ARG; + } + data = &result->raw.data[componentIndex]; + if ((data->attributeLen > sizeof(data->attribute)) || + (data->chipIdLen > sizeof(data->chipId)) || + (data->timeStampLen > sizeof(data->timeStamp)) || + (data->signatureLen > sizeof(data->signature))) { + return BAD_LENGTH_E; + } +#if SSS_HAVE_SE05X_VER_GTE_07_02 + if ((data->cmdLen > sizeof(data->cmd)) || + (data->objSizeLen > sizeof(data->objSize))) { + return BAD_LENGTH_E; + } +#else + if (data->outrandomLen > sizeof(data->outrandom)) { + return BAD_LENGTH_E; + } +#endif + +#if SSS_HAVE_SE05X_VER_GTE_07_02 + { + int digestSz; + byte commandDigest[WC_MAX_DIGEST_SIZE]; + + digestSz = wc_HashGetDigestSize(result->hashAlgo); + if ((digestSz <= 0) || (data->cmdLen > UINT32_MAX)) { + return BAD_FUNC_ARG; + } + ret = wc_Hash(result->hashAlgo, data->cmd, (word32)data->cmdLen, + commandDigest, (word32)digestSz); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + commandDigest, (word32)digestSz); + if ((ret == 0) && (componentSz != 0U)) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_1, component, componentSz); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_2, data->chipId, (word32)data->chipIdLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_3, data->attribute, + (word32)data->attributeLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_4, data->objSize, + (word32)data->objSizeLen); + if (ret == 0) + ret = se050_attest_append_tlv(signedData, signedDataSz, &offset, + (byte)kSE05x_TAG_TIMESTAMP, data->timeStamp.ts, + (word32)data->timeStampLen); + ForceZero(commandDigest, sizeof(commandDigest)); + } +#else + ret = se050_attest_append(signedData, signedDataSz, &offset, component, + componentSz); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->attribute, (word32)data->attributeLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->timeStamp.ts, (word32)data->timeStampLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->outrandom, (word32)data->outrandomLen); + if (ret == 0) + ret = se050_attest_append(signedData, signedDataSz, &offset, + data->chipId, (word32)data->chipIdLen); +#endif + + if (ret == 0) + *used = offset; + return ret; +#endif +} + +static int se050_verify_attestation_freshness( + const sss_se05x_attst_comp_data_t* data, const byte* expectedRandom, + word32 expectedRandomSz) +{ + if ((data == NULL) || (expectedRandom == NULL) || + (expectedRandomSz != SE050_ATTEST_RANDOM_SIZE)) { + return BAD_FUNC_ARG; + } + +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && \ + SSS_HAVE_SE05X_VER_GTE_07_02 + { + word32 offset = 7U; + word32 commandDataSz; + int found = 0; + + if ((data->cmdLen < offset) || (data->cmdLen > sizeof(data->cmd)) || + (data->cmd[0] != (byte)kSE05x_CLA) || + (data->cmd[1] != + (byte)kSE05x_INS_READ_With_Attestation) || + (data->cmd[4] != 0U)) { + return BAD_LENGTH_E; + } + commandDataSz = ((word32)data->cmd[5] << 8) | data->cmd[6]; + if (commandDataSz != ((word32)data->cmdLen - offset)) { + return BAD_LENGTH_E; + } + + while (offset < data->cmdLen) { + byte tag; + byte lengthByte; + word32 valueSz; + + tag = data->cmd[offset++]; + if (offset >= data->cmdLen) { + return BAD_LENGTH_E; + } + lengthByte = data->cmd[offset++]; + if (lengthByte <= 0x7FU) { + valueSz = lengthByte; + } + else if (lengthByte == 0x81U) { + if (offset >= data->cmdLen) { + return BAD_LENGTH_E; + } + valueSz = data->cmd[offset++]; + } + else if (lengthByte == 0x82U) { + if (((word32)data->cmdLen - offset) < 2U) { + return BAD_LENGTH_E; + } + valueSz = ((word32)data->cmd[offset] << 8) | + data->cmd[offset + 1U]; + offset += 2U; + } + else { + return BAD_LENGTH_E; + } + if (valueSz > ((word32)data->cmdLen - offset)) { + return BAD_LENGTH_E; + } + if (tag == (byte)kSE05x_TAG_7) { + if (found || (valueSz != expectedRandomSz)) { + return BAD_LENGTH_E; + } + if (ConstantCompare(data->cmd + offset, expectedRandom, + expectedRandomSz) != 0) { + return WC_NO_ERR_TRACE(SIG_VERIFY_E); + } + found = 1; + } + offset += valueSz; + } + return found ? 0 : BAD_LENGTH_E; } - if (status == kStatus_SSS_Success) { - status = sss_key_store_set_key(&host_keystore, &newObj, object, - objectSz, (objectSz * 8), NULL, 0); +#else + if (data->outrandomLen != expectedRandomSz) { + return BAD_LENGTH_E; } - wolfSSL_CryptHwMutexUnLock(); - - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; + if (ConstantCompare(data->outrandom, expectedRandom, + expectedRandomSz) != 0) { + return WC_NO_ERR_TRACE(SIG_VERIFY_E); } - - return ret; + return 0; +#endif } -/** - * Get binary object from SE050 from specified key ID. - * - * keyId SE050 key ID to get binary object from - * out output buffer to place binary object - * outSz size of output buffer on input, size of written object on output - * - * Returns 0 on success, LENGTH_ONLY_E if out is NULL with outSz set to - * required buffer size, and other negative on error. - */ -int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz) +int wc_se050_verify_attestation(const wc_se050_attst_result* result, + const byte* attestPubDer, word32 attestPubDerSz, + const byte* expectedRandom, word32 expectedRandomSz, int* res) { - int ret = 0; - sss_object_t object; - sss_key_store_t host_keystore; - sss_status_t status = kStatus_SSS_Success; - size_t outBitSz = 0; - - /* If out is NULL, outSz set to required size and LENGTH_ONLY_E returned */ - if (outSz == NULL) { +#if defined(NO_HASH_WRAPPER) || defined(NO_ASN) + (void)result; + (void)attestPubDer; + (void)attestPubDerSz; + (void)expectedRandom; + (void)expectedRandomSz; + if (res != NULL) + *res = 0; + return WC_NO_ERR_TRACE(NOT_COMPILED_IN); +#else + int ret = 0; + int keyDecoded = 0; + int sigType = WC_SIGNATURE_TYPE_NONE; + word32 i; + word32 idx; + word32 componentSz; + word32 signedDataSz; + word32 signedDataUsed; + byte* component = NULL; + byte* signedData = NULL; +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + int eccInit = 0; + ecc_key eccKey; +#endif +#ifndef NO_RSA + int rsaInit = 0; + RsaKey rsaKey; +#endif + void* verifyKey = NULL; + word32 verifyKeySz = 0; + + if ((result == NULL) || (attestPubDer == NULL) || + (attestPubDerSz == 0U) || (expectedRandom == NULL) || + (expectedRandomSz != SE050_ATTEST_RANDOM_SIZE) || + (res == NULL) || + (result->raw.valid_number == 0U) || + (result->raw.valid_number > SE05X_MAX_ATTST_DATA)) { return BAD_FUNC_ARG; } - - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; + *res = 0; + if (ConstantCompare(result->freshness, expectedRandom, + expectedRandomSz) != 0) { + return 0; } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); - if (status == kStatus_SSS_Success) { - status = sss_key_object_init(&object, &host_keystore); +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + XMEMSET(&eccKey, 0, sizeof(eccKey)); +#endif +#ifndef NO_RSA + XMEMSET(&rsaKey, 0, sizeof(rsaKey)); +#endif +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + ret = wc_ecc_init(&eccKey); + if (ret == 0) { + eccInit = 1; + idx = 0; + ret = wc_EccPublicKeyDecode(attestPubDer, &idx, &eccKey, + attestPubDerSz); + if (ret == 0) { + keyDecoded = 1; + sigType = WC_SIGNATURE_TYPE_ECC; + verifyKey = &eccKey; + verifyKeySz = sizeof(eccKey); + } } - if (status == kStatus_SSS_Success) { - ret = se050_get_object_size(&host_keystore, keyId); - if (ret < 0) { - status = kStatus_SSS_Fail; +#endif +#ifndef NO_RSA + if (!keyDecoded) { + ret = wc_InitRsaKey(&rsaKey, NULL); + if (ret == 0) { + rsaInit = 1; + idx = 0; + ret = wc_RsaPublicKeyDecode(attestPubDer, &idx, &rsaKey, + attestPubDerSz); + if (ret == 0) { + keyDecoded = 1; + sigType = WC_SIGNATURE_TYPE_RSA_W_ENC; + verifyKey = &rsaKey; + verifyKeySz = sizeof(rsaKey); + } } - else { - if (out == NULL) { - *outSz = ret; - wolfSSL_CryptHwMutexUnLock(); - return WC_NO_ERR_TRACE(LENGTH_ONLY_E); + } +#endif + if (!keyDecoded) { + goto cleanup; + } + + component = (byte*)XMALLOC(WC_SE050_ATTEST_VALUE_MAX, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + signedDataSz = WC_SE050_ATTEST_VALUE_MAX + MAX_POLICY_BUFFER_SIZE + + WC_MAX_DIGEST_SIZE + 128U; + signedData = (byte*)XMALLOC(signedDataSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if ((component == NULL) || (signedData == NULL)) { + ret = MEMORY_E; + goto cleanup; + } + + ret = 0; + for (i = 0; (i < result->raw.valid_number) && (ret == 0); i++) { + ret = se050_verify_attestation_freshness(&result->raw.data[i], + expectedRandom, expectedRandomSz); + if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + ret = 0; + goto cleanup; + } + componentSz = WC_SE050_ATTEST_VALUE_MAX; + if (ret == 0) { + ret = se050_attested_component(result, i, component, + &componentSz); + } + if (ret == 0) { + ret = se050_build_attestation_data(result, i, component, + componentSz, signedData, signedDataSz, &signedDataUsed); + } + if (ret == 0) { + if ((result->raw.data[i].signatureLen == 0U) || + (result->raw.data[i].signatureLen > + sizeof(result->raw.data[i].signature))) { + ret = BAD_LENGTH_E; + break; } - if ((word32)ret > *outSz) { - WOLFSSL_MSG("Output buffer not large enough for object"); - wolfSSL_CryptHwMutexUnLock(); - return BAD_LENGTH_E; + ret = wc_SignatureVerify(result->hashAlgo, + (enum wc_SignatureType)sigType, signedData, signedDataUsed, + result->raw.data[i].signature, + (word32)result->raw.data[i].signatureLen, verifyKey, + verifyKeySz); + if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + ret = 0; + goto cleanup; } - ret = 0; } } - if (status == kStatus_SSS_Success) { - status = sss_key_object_get_handle(&object, keyId); + if (ret == 0) + *res = 1; + +cleanup: + if (component != NULL) { + ForceZero(component, WC_SE050_ATTEST_VALUE_MAX); + XFREE(component, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - if (status == kStatus_SSS_Success) { - outBitSz = (*outSz) * 8; - status = sss_key_store_get_key(&host_keystore, &object, out, - (size_t*)outSz, &outBitSz); + if (signedData != NULL) { + ForceZero(signedData, signedDataSz); + XFREE(signedData, NULL, DYNAMIC_TYPE_TMP_BUFFER); } - wolfSSL_CryptHwMutexUnLock(); +#if defined(HAVE_ECC) && defined(HAVE_ECC_VERIFY) + if (eccInit) + wc_ecc_free(&eccKey); +#endif +#ifndef NO_RSA + if (rsaInit) + wc_FreeRsaKey(&rsaKey); +#endif + return ret; +#endif +} - if (status != kStatus_SSS_Success) { - ret = WC_HW_E; +int wc_se050_validate_provisioned_key(word32 keyId, word32 attestKeyId, + const byte* expectedPubDer, word32 expectedPubDerSz, + const byte* attestPubDer, word32 attestPubDerSz, int* res) +{ + int ret; + int verified = 0; + wc_se050_attst_result result; + + if ((expectedPubDer == NULL) || (expectedPubDerSz == 0U) || + (attestPubDer == NULL) || (attestPubDerSz == 0U) || + (res == NULL)) { + return BAD_FUNC_ARG; } + *res = 0; + ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, + NULL, 0, &result); + if (ret == 0) + ret = wc_se050_verify_attestation(&result, attestPubDer, + attestPubDerSz, result.freshness, + (word32)sizeof(result.freshness), &verified); + if ((ret == 0) && verified && (result.valueSz == expectedPubDerSz) && + (XMEMCMP(result.value, expectedPubDer, expectedPubDerSz) == 0)) { + *res = 1; + } + ForceZero(&result, sizeof(result)); return ret; } +#endif /* !WOLFSSL_SE050_NO_ATTEST */ + #if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) /** @@ -924,8 +2521,84 @@ int se050_rsa_create_key(struct RsaKey* key, int size, long e) return ret; } +static int se050_rsa_generate_key(word32 keyId, int size, long e, + const sss_policy_t* policy) +{ + sss_status_t status = kStatus_SSS_Success; + sss_object_t keyPair; + sss_key_store_t host_keystore; + int keyObjectInit = 0; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if ((keyId >= SE050_KEYID_START) || (size <= 0) || + ((size & 7) != 0) || (e != 65537)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = se050_require_new_object(keyId); + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_RSA); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyPair, &host_keystore); + if (status == kStatus_SSS_Success) { + keyObjectInit = 1; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&keyPair, keyId, + kSSS_KeyPart_Pair, kSSS_CipherType_RSA, (size / 8), + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_generate_key(&host_keystore, &keyPair, + size, (void*)policy); + } + + if (keyObjectInit) { + sss_key_object_free(&keyPair); + } + wolfSSL_CryptHwMutexUnLock(); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; +} + +int wc_se050_rsa_generate_key_policy(word32 keyId, int size, long e, + const sss_policy_t* policy) +{ + return se050_rsa_generate_key(keyId, size, e, policy); +} + +int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e, + word32 policyFlags, word32 authObjId) +{ + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + int ret; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_rsa_generate_key(keyId, size, e, policy); +} + static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, - word32 rsaDerSize, int keyType) + word32 rsaDerSize, int keyType, const sss_policy_t* policy, + int requireNew) { int ret = 0; int keySize; @@ -936,6 +2609,11 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, struct RsaKey key; sss_key_part_t keyPart = kSSS_KeyPart_Pair; + if ((cfg_se050_i2c_pi == NULL) || (rsaDer == NULL) || + (rsaDerSize == 0U)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; } @@ -946,6 +2624,10 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, return BAD_FUNC_ARG; } + if (requireNew) { + status = se050_require_new_object(keyId); + } + ret = wc_InitRsaKey(&key, NULL); if (ret != 0) { status = kStatus_SSS_Fail; @@ -987,7 +2669,7 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, } if (status == kStatus_SSS_Success) { status = sss_key_store_set_key(&host_keystore, &newKey, rsaDer, - rsaDerSize, (keySize * 8), NULL, 0); + rsaDerSize, (keySize * 8), (void*)policy, 0); } wolfSSL_CryptHwMutexUnLock(); @@ -1013,7 +2695,8 @@ static int se050_rsa_insert_key(word32 keyId, const byte* rsaDer, int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize) { - return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE); + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + NULL, 0); } /** @@ -1028,7 +2711,55 @@ int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, int wc_se050_rsa_insert_public_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize) { - return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC); + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + NULL, 0); +} + +int wc_se050_rsa_insert_private_key_policy(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, const sss_policy_t* policy) +{ + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + policy, 0); +} + +int wc_se050_rsa_insert_public_key_policy(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, const sss_policy_t* policy) +{ + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + policy, 0); +} + +static int se050_rsa_insert_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, int keyType, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_rsa_insert_key(keyId, rsaDer, rsaDerSize, keyType, policy, + 1); +} + +int wc_se050_rsa_insert_private_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_rsa_insert_key_ex(keyId, rsaDer, rsaDerSize, RSA_PRIVATE, + policyFlags, authObjId); +} + +int wc_se050_rsa_insert_public_key_ex(word32 keyId, const byte* rsaDer, + word32 rsaDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_rsa_insert_key_ex(keyId, rsaDer, rsaDerSize, RSA_PUBLIC, + policyFlags, authObjId); } /** @@ -2048,6 +3779,87 @@ static int se050_map_curve(int curve_id, int keySize, return ret; } +static int se050_ecc_generate_key(word32 keyId, int keySize, int curveId, + const sss_policy_t* policy) +{ + sss_status_t status = kStatus_SSS_Success; + sss_object_t keyPair; + sss_key_store_t host_keystore; + sss_cipher_type_t curveType; + int keyObjectInit = 0; + int keySizeBits; + int ret; + + if (cfg_se050_i2c_pi == NULL) { + return WC_HW_E; + } + if ((keyId >= SE050_KEYID_START) || (keySize <= 0)) { + return BAD_FUNC_ARG; + } + ret = se050_map_curve(curveId, keySize, &keySizeBits, &curveType); + if (ret != 0) { + return ret; + } + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + + status = se050_require_new_object(keyId); + if (status == kStatus_SSS_Success) { + status = sss_key_store_context_init(&host_keystore, + cfg_se050_i2c_pi); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_allocate(&host_keystore, + SE050_KEYSTOREID_ECC); + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_init(&keyPair, &host_keystore); + if (status == kStatus_SSS_Success) { + keyObjectInit = 1; + } + } + if (status == kStatus_SSS_Success) { + status = sss_key_object_allocate_handle(&keyPair, keyId, + kSSS_KeyPart_Pair, curveType, keySize, + kKeyObject_Mode_Persistent); + } + if (status == kStatus_SSS_Success) { + status = sss_key_store_generate_key(&host_keystore, &keyPair, + keySizeBits, (void*)policy); + } + + if (keyObjectInit) { + sss_key_object_free(&keyPair); + } + wolfSSL_CryptHwMutexUnLock(); + return (status == kStatus_SSS_Success) ? 0 : WC_HW_E; +} + +int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize, int curveId, + const sss_policy_t* policy) +{ + return se050_ecc_generate_key(keyId, keySize, curveId, policy); +} + +int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize, int curveId, + word32 policyFlags, word32 authObjId) +{ + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + int ret; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_ecc_generate_key(keyId, keySize, curveId, policy); +} + static sss_algorithm_t se050_map_hash_alg(int hashLen) { sss_algorithm_t algorithm = kAlgorithm_None; @@ -2067,7 +3879,8 @@ static sss_algorithm_t se050_map_hash_alg(int hashLen) } static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, - word32 eccDerSize, int keyType) + word32 eccDerSize, int keyType, const sss_policy_t* policy, + int requireNew) { int ret = 0; struct ecc_key key; @@ -2080,6 +3893,11 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, sss_cipher_type_t curveType = kSSS_CipherType_NONE; sss_key_part_t keyPart = kSSS_KeyPart_Pair; + if ((cfg_se050_i2c_pi == NULL) || (eccDer == NULL) || + (eccDerSize == 0U)) { + return BAD_FUNC_ARG; + } + if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; } @@ -2090,6 +3908,10 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, return BAD_FUNC_ARG; } + if (requireNew) { + status = se050_require_new_object(keyId); + } + ret = wc_ecc_init(&key); if (ret != 0) { status = kStatus_SSS_Fail; @@ -2131,8 +3953,7 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, } if (status == kStatus_SSS_Success) { status = sss_key_store_set_key(&host_keystore, &newKey, eccDer, - eccDerSize, keySizeBits, - NULL, 0); + eccDerSize, keySizeBits, (void*)policy, 0); } wolfSSL_CryptHwMutexUnLock(); @@ -2157,7 +3978,8 @@ static int se050_ecc_insert_key(word32 keyId, const byte* eccDer, int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, word32 eccDerSize) { - return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY); + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + NULL, 0); } /** @@ -2172,7 +3994,55 @@ int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, int wc_se050_ecc_insert_private_key(word32 keyId, const byte* eccDer, word32 eccDerSize) { - return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY); + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + NULL, 0); +} + +int wc_se050_ecc_insert_public_key_policy(word32 keyId, const byte* eccDer, + word32 eccDerSize, const sss_policy_t* policy) +{ + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + policy, 0); +} + +int wc_se050_ecc_insert_private_key_policy(word32 keyId, const byte* eccDer, + word32 eccDerSize, const sss_policy_t* policy) +{ + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + policy, 0); +} + +static int se050_ecc_insert_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, int keyType, word32 policyFlags, word32 authObjId) +{ + int ret; + const sss_policy_t* policy = NULL; + se050_policy_set policySet; + + ret = se050_build_policy_set(SE050_POLICY_OBJECT_ASYM, policyFlags, + authObjId, &policySet); + if (ret != 0) { + return ret; + } + if (policyFlags != 0U) { + policy = &policySet.policy; + } + return se050_ecc_insert_key(keyId, eccDer, eccDerSize, keyType, policy, + 1); +} + +int wc_se050_ecc_insert_public_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_ecc_insert_key_ex(keyId, eccDer, eccDerSize, ECC_PUBLICKEY, + policyFlags, authObjId); +} + +int wc_se050_ecc_insert_private_key_ex(word32 keyId, const byte* eccDer, + word32 eccDerSize, word32 policyFlags, word32 authObjId) +{ + return se050_ecc_insert_key_ex(keyId, eccDer, eccDerSize, ECC_PRIVATEKEY, + policyFlags, authObjId); } int se050_ecc_sign_hash_ex(const byte* in, word32 inLen, MATH_INT_T* r, MATH_INT_T* s, diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 4d60dcaa81d..37db77c0165 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -646,10 +646,15 @@ int wolfCrypt_Init(void) #endif #if defined(WOLFSSL_SE050) && defined(WOLFSSL_SE050_INIT) - ret = wc_se050_init(NULL); - if (ret != 0) { - WOLFSSL_MSG("SE050 init failed"); - WOLFCRYPT_INIT_RAISE_BAD_STATE(); + /* An application may need runtime SCP03 keys to open the SE05x + * before calling wolfCrypt_Init(). Keep that configured session + * instead of trying to replace it with the compiled-in defaults. */ + if (wc_se050_get_session() == NULL) { + ret = wc_se050_init(NULL); + if (ret != 0) { + WOLFSSL_MSG("SE050 init failed"); + WOLFCRYPT_INIT_RAISE_BAD_STATE(); + } } #endif @@ -878,6 +883,16 @@ int wolfCrypt_Cleanup(void) ret = ret2; } #endif + #if defined(WOLFSSL_SE050) && defined(WOLFSSL_SE050_INIT) + if (wc_se050_get_session() != NULL) { + int ret2 = wc_se050_close(); + + /* A session installed with wc_se050_set_config() is owned by the + * application and wc_se050_close() deliberately rejects it. */ + if ((ret == 0) && (ret2 != WC_NO_ERR_TRACE(BAD_STATE_E))) + ret = ret2; + } + #endif #if defined(WOLFSSL_TROPIC01) Tropic01_Deinit(); #endif diff --git a/wolfssl/wolfcrypt/port/nxp/se050_port.h b/wolfssl/wolfcrypt/port/nxp/se050_port.h index ab57a2b3cb0..62e907a080a 100644 --- a/wolfssl/wolfcrypt/port/nxp/se050_port.h +++ b/wolfssl/wolfcrypt/port/nxp/se050_port.h @@ -98,6 +98,54 @@ enum SE050KeyType { SE050_CURVE25519_KEY }; +/* SE05x secure object permissions. Attaching any policy makes the applet + * default-deny all permissions that are not explicitly granted. A value of + * zero preserves the applet default policy by attaching no policy. */ +#define WC_SE050_POLICY_ALLOW_DELETE 0x00000001U +#define WC_SE050_POLICY_ALLOW_WRITE 0x00000002U +#define WC_SE050_POLICY_ALLOW_READ 0x00000004U +#define WC_SE050_POLICY_ALLOW_SIGN 0x00000008U +#define WC_SE050_POLICY_ALLOW_VERIFY 0x00000010U +#define WC_SE050_POLICY_ALLOW_ENCRYPT 0x00000020U +#define WC_SE050_POLICY_ALLOW_DECRYPT 0x00000040U +#define WC_SE050_POLICY_ALLOW_KA 0x00000080U +#define WC_SE050_POLICY_ALLOW_KD 0x00000100U +#define WC_SE050_POLICY_ALLOW_GEN 0x00000200U +#define WC_SE050_POLICY_ALLOW_IMPORT_EXPORT 0x00000400U +#define WC_SE050_POLICY_ALLOW_ATTEST 0x00000800U +#define WC_SE050_POLICY_REQUIRE_SM 0x00001000U + +/* Platform SCP03 uses one 128-bit ENC, MAC and data-encryption key. */ +typedef struct wc_se050_scp03_keys { + byte enc[16]; + byte mac[16]; + byte dek[16]; +} wc_se050_scp03_keys; + +#ifndef WC_SE050_ATTEST_VALUE_MAX +#define WC_SE050_ATTEST_VALUE_MAX 1024U +#endif + +#ifndef WOLFSSL_SE050_NO_ATTEST +/* Result of an attested object read. The public object value is returned in + * the same DER form as sss_key_store_get_key(). cipherType, objectType and + * curveId describe the object and are used by the host verifier to recover + * the exact applet response value. */ +typedef struct wc_se050_attst_result { + byte value[WC_SE050_ATTEST_VALUE_MAX]; + word32 valueSz; + byte freshness[16]; + byte origin; + word32 authObjId; + word32 policyFlags; + word32 cipherType; + word32 objectType; + word32 curveId; + enum wc_HashType hashAlgo; + sss_se05x_attst_data_t raw; +} wc_se050_attst_result; +#endif + #ifdef WOLFSSL_SE050_HASH typedef struct { @@ -111,8 +159,56 @@ typedef struct { /* Public Functions */ WOLFSSL_API int wc_se050_set_config(sss_session_t *pSession, sss_key_store_t *pHostKeyStore, sss_key_store_t *pKeyStore); +/** Return the configured SSS session and keystores. Output pointers may be + * NULL. Direct middleware use must be bracketed by wc_se050_lock/unlock. */ +WOLFSSL_API int wc_se050_get_config(sss_session_t **pSession, + sss_key_store_t **pHostKeyStore, sss_key_store_t **pKeyStore); +/** Return the SSS session currently used by the wolfCrypt SE05x port. */ +WOLFSSL_API sss_session_t* wc_se050_get_session(void); +/** Return the low-level SE05x session, or NULL when none is configured. */ +WOLFSSL_API pSe05xSession_t wc_se050_get_se05x_session(void); +/** Acquire/release the shared wolfCrypt hardware transport lock. */ +WOLFSSL_API int wc_se050_lock(void); +WOLFSSL_API void wc_se050_unlock(void); #ifdef WOLFSSL_SE050_INIT WOLFSSL_API int wc_se050_init(const char* portName); +/** Close a session opened by wc_se050_init() or wc_se050_init_ex(). */ +WOLFSSL_API int wc_se050_close(void); +#if defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 +/** Open Platform SCP03 using caller-supplied 128-bit ENC/MAC/DEK keys. */ +WOLFSSL_API int wc_se050_init_ex(const char* portName, + const wc_se050_scp03_keys* keys); +#endif +#endif + +#ifdef HAVE_HKDF +/** Deterministically derive the Platform SCP03 ENC, MAC and DEK keys from a + * seed. No SE05x session is required, so this can be called before + * wolfCrypt_Init() to recover keys after a power cycle. */ +WOLFSSL_API int wc_se050_scp03_derive_keys_seed(const byte* seed, + word32 seedSz, wc_se050_scp03_keys* derivedOut); +#endif + +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + defined(WOLFSSL_SE050_INIT) && \ + defined(SSS_HAVE_HOSTCRYPTO_ANY) && SSS_HAVE_HOSTCRYPTO_ANY && \ + defined(SSS_HAVE_SCP_SCP03_SSS) && SSS_HAVE_SCP_SCP03_SSS && \ + defined(SSS_HAVE_SE05X_AUTH_PLATFSCP03) && \ + SSS_HAVE_SE05X_AUTH_PLATFSCP03 +/** Destructively replace the Platform SCP03 key set using secured PUT KEY. + * The port temporarily authenticates to the Security Domain, then returns + * with a fresh IoT applet session authenticated by newKeys. */ +WOLFSSL_API int wc_se050_scp03_rotate_keys( + const wc_se050_scp03_keys* newKeys, byte keyVersion); +#ifdef HAVE_HKDF +/** Derive three keys with the documented HKDF-SHA256 construction and rotate. + * Persist the seed before calling. derivedOut may be NULL. */ +WOLFSSL_API int wc_se050_scp03_rotate_keys_seed(const byte* seed, + word32 seedSz, byte keyVersion, wc_se050_scp03_keys* derivedOut); +#endif #endif WOLFSSL_API int wc_se050_erase_object(word32 keyId); @@ -120,16 +216,82 @@ WOLFSSL_API int wc_se050_ecc_insert_public_key(word32 keyId, const byte* eccDer, word32 eccDerSize); WOLFSSL_API int wc_se050_ecc_insert_private_key(word32 keyId, const byte* eccDer, word32 eccDerSize); +/** Insert an ECC public/private key with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_ecc_insert_public_key_ex(word32 keyId, + const byte* eccDer, word32 eccDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_ecc_insert_private_key_ex(word32 keyId, + const byte* eccDer, word32 eccDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_ecc_insert_public_key_policy(word32 keyId, + const byte* eccDer, word32 eccDerSize, const sss_policy_t* policy); +WOLFSSL_API int wc_se050_ecc_insert_private_key_policy(word32 keyId, + const byte* eccDer, word32 eccDerSize, const sss_policy_t* policy); +#ifdef HAVE_ECC +/** Generate a persistent ECC key pair at a caller-selected, unused ID with a + * flag-based immutable policy. keySize is in bytes. */ +WOLFSSL_API int wc_se050_ecc_generate_key_ex(word32 keyId, int keySize, + int curveId, word32 policyFlags, word32 authObjId); +/** Generate a persistent ECC key pair with a raw middleware policy. */ +WOLFSSL_API int wc_se050_ecc_generate_key_policy(word32 keyId, int keySize, + int curveId, const sss_policy_t* policy); +#endif WOLFSSL_API int wc_se050_rsa_insert_public_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize); WOLFSSL_API int wc_se050_rsa_insert_private_key(word32 keyId, const byte* rsaDer, word32 rsaDerSize); +/** Insert an RSA public/private key with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_rsa_insert_public_key_ex(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_rsa_insert_private_key_ex(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_rsa_insert_public_key_policy(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, const sss_policy_t* policy); +WOLFSSL_API int wc_se050_rsa_insert_private_key_policy(word32 keyId, + const byte* rsaDer, word32 rsaDerSize, const sss_policy_t* policy); +#if !defined(NO_RSA) && !defined(WOLFSSL_SE050_NO_RSA) +/** Generate a persistent RSA key pair at a caller-selected, unused ID with a + * flag-based immutable policy. size is in bits and e must be 65537. */ +WOLFSSL_API int wc_se050_rsa_generate_key_ex(word32 keyId, int size, long e, + word32 policyFlags, word32 authObjId); +/** Generate a persistent RSA key pair with a raw middleware policy. */ +WOLFSSL_API int wc_se050_rsa_generate_key_policy(word32 keyId, int size, + long e, const sss_policy_t* policy); +#endif WOLFSSL_API int wc_se050_insert_binary_object(word32 keyId, const byte* object, word32 objectSz); +/** Insert a binary object with a flag-based immutable policy. */ +WOLFSSL_API int wc_se050_insert_binary_object_ex(word32 keyId, + const byte* object, word32 objectSz, word32 policyFlags, + word32 authObjId); +WOLFSSL_API int wc_se050_insert_binary_object_policy(word32 keyId, + const byte* object, word32 objectSz, const sss_policy_t* policy); WOLFSSL_API int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz); +/** Read raw object attributes, including policy records and origin. */ +WOLFSSL_API int wc_se050_get_object_attributes(word32 keyId, byte* attr, + word32* attrSz); + +#ifndef WOLFSSL_SE050_NO_ATTEST +/** Read and attest an object. Freshness must be 16 bytes or NULL. */ +WOLFSSL_API int wc_se050_attest_object(word32 keyId, word32 attestKeyId, + enum wc_HashType hashAlgo, const byte* random, word32 randomSz, + wc_se050_attst_result* result); +/** Verify all returned attestation components with an ECC/RSA public key and + * require the independently retained 16-byte freshness challenge. */ +WOLFSSL_API int wc_se050_verify_attestation( + const wc_se050_attst_result* result, const byte* attestPubDer, + word32 attestPubDerSz, const byte* expectedRandom, + word32 expectedRandomSz, int* res); +/** Attest a key, verify the signature, and compare its public-key DER. */ +WOLFSSL_API int wc_se050_validate_provisioned_key(word32 keyId, + word32 attestKeyId, const byte* expectedPubDer, word32 expectedPubDerSz, + const byte* attestPubDer, word32 attestPubDerSz, int* res); +#endif /* Private Functions */ WOLFSSL_LOCAL word32 se050_allocate_key(int keyType); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index b111438a368..3872da0c79f 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3290,6 +3290,14 @@ #define WOLFSSL_CRYPT_HW_MUTEX 1 #endif +/* SE05x operations share one session and transport. Enable the wolfCrypt + * hardware mutex by default so the public SE05x lock API and the port's + * internal serialization are effective in normal threaded builds. */ +#if defined(WOLFSSL_SE050) && !defined(SINGLE_THREADED) && \ + !defined(WOLFSSL_CRYPT_HW_MUTEX) + #define WOLFSSL_CRYPT_HW_MUTEX 1 +#endif + #if !defined(XMALLOC_USER) && !defined(MICRIUM_MALLOC) && \ !defined(WOLFSSL_LEANPSK) && !defined(NO_WOLFSSL_MEMORY) && \ !defined(XMALLOC_OVERRIDE) From 95b147fc92c2c311fe6190d9b0616fc256b31a02 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 3 Sep 2026 16:32:35 +0100 Subject: [PATCH 2/4] Guard SE05x RSA attestation in verify-only builds --- wolfcrypt/src/port/nxp/se050_port.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 33ffe2a0cd2..bf1f2af9b7e 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1761,8 +1761,6 @@ int wc_se050_attest_object(word32 keyId, word32 attestKeyId, static int se050_attested_component(const wc_se050_attst_result* result, word32 componentIndex, byte* component, word32* componentSz) { - int ret; - if ((result == NULL) || (component == NULL) || (componentSz == NULL) || (componentIndex >= result->raw.valid_number)) { return BAD_FUNC_ARG; @@ -1770,7 +1768,8 @@ static int se050_attested_component(const wc_se050_attst_result* result, if ((result->cipherType == (word32)kSSS_CipherType_RSA) || (result->cipherType == (word32)kSSS_CipherType_RSA_CRT)) { - #ifndef NO_RSA + #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_VERIFY_ONLY) + int ret; RsaKey key; word32 idx = 0; byte exponent[8]; @@ -1809,6 +1808,7 @@ static int se050_attested_component(const wc_se050_attst_result* result, (result->cipherType == (word32)kSSS_CipherType_EC_NIST_K) || (result->cipherType == (word32)kSSS_CipherType_EC_BRAINPOOL)) { #ifdef HAVE_ECC + int ret; ecc_key key; word32 idx = 0; From 8f058eb927fac8f4c9570ef5d25d39a6a8d865e0 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Fri, 4 Sep 2026 13:13:35 +0100 Subject: [PATCH 3/4] Return BAD_STATE_E for uninitialized SE05x object APIs Reject object reads before passing a null session into the NXP keystore API, and classify erase requests without an active session consistently as a state error. --- wolfcrypt/src/port/nxp/se050_port.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index bf1f2af9b7e..39ee3f91374 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -916,7 +916,7 @@ int wc_se050_erase_object(word32 id) #endif if (cfg_se050_i2c_pi == NULL) { - return BAD_FUNC_ARG; + return WC_NO_ERR_TRACE(BAD_STATE_E); } if (wolfSSL_CryptHwMutexLock() != 0) { @@ -1440,6 +1440,9 @@ int wc_se050_get_binary_object(word32 keyId, byte* out, word32* outSz) if (outSz == NULL) { return BAD_FUNC_ARG; } + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } if (wolfSSL_CryptHwMutexLock() != 0) { return BAD_MUTEX_E; From 3ec3f7957663e7254b0089c0d42a83a064c75b39 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Fri, 4 Sep 2026 15:03:14 +0100 Subject: [PATCH 4/4] Fix SE05x attestation and mutex handling Avoid nested hardware mutex acquisition in AES key replacement and SE05x TRNG seeding. Keep SCP03 key wrapping in host AES, require AES direct support, and bind attestation validation to a caller-provided challenge. --- wolfcrypt/src/port/nxp/README_SE050.md | 47 ++++++++--- wolfcrypt/src/port/nxp/se050_port.c | 108 ++++++++++++------------ wolfcrypt/src/random.c | 9 +- wolfssl/wolfcrypt/port/nxp/se050_port.h | 14 ++- 4 files changed, 98 insertions(+), 80 deletions(-) diff --git a/wolfcrypt/src/port/nxp/README_SE050.md b/wolfcrypt/src/port/nxp/README_SE050.md index d708d6b3458..9195a0ec3a1 100644 --- a/wolfcrypt/src/port/nxp/README_SE050.md +++ b/wolfcrypt/src/port/nxp/README_SE050.md @@ -255,8 +255,9 @@ for small builds that do not provision or validate attested objects. Enables the destructive Platform SCP03 key-rotation APIs. This is deliberately opt-in. The middleware must enable Platform SCP03 and HostCrypto, and wolfSSL -must be built with `WOLFSSL_SE050_INIT`. The seed-based helper additionally -requires HKDF (`--enable-hkdf` / `HAVE_HKDF`). +must be built with `WOLFSSL_SE050_INIT` and AES direct support +(`WOLFSSL_AES_DIRECT`). The seed-based helper additionally requires HKDF +(`--enable-hkdf` / `HAVE_HKDF`). **`WOLFSSL_SE050_NO_ECDHE`** @@ -638,6 +639,11 @@ The port uses the configured middleware transport when its transport macro is visible. Otherwise it uses T=1 over I2C, which is the documented/default SE05x port configuration. +When the middleware HostCrypto backend uses wolfSSL, the SCP03 handshake needs +entropy before the authenticated SE05x session exists. Configure an independent +host entropy source and define `WOLFSSL_SE050_NO_TRNG` so the handshake does not +try to bootstrap itself from the SE05x TRNG. + When `HAVE_HKDF` is enabled, the active keys can be regenerated deterministically from a protected seed without an open SE05x session: @@ -714,19 +720,25 @@ normally. Never test rotation on a production part. ### Object Attestation and Provisioning Validation -`wc_se050_attest_object()` performs an attested read using a caller-provisioned -attestation key. The attestation key's object policy must grant -`WC_SE050_POLICY_ALLOW_ATTEST`. Freshness is exactly 16 bytes; when `random` is -`NULL`, the SE05x TRNG supplies it unless that path was compiled out. +`wc_se050_attest_object()` performs an attested read using an +application-provisioned attestation key. The attestation key's object policy +must grant +`WC_SE050_POLICY_ALLOW_ATTEST`. The caller must supply an independently +generated 16-byte freshness challenge. Generate and retain that challenge +outside the SE05x so a response cannot select its own freshness value. ```c wc_se050_attst_result result; +byte freshness[16]; int valid; -ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, - freshness, sizeof(freshness), &result); -ret = wc_se050_verify_attestation(&result, attestPublicDer, - attestPublicDerSz, freshness, sizeof(freshness), &valid); +ret = application_get_host_random(freshness, sizeof(freshness)); +if (ret == 0) + ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, + freshness, sizeof(freshness), &result); +if (ret == 0) + ret = wc_se050_verify_attestation(&result, attestPublicDer, + attestPublicDerSz, freshness, sizeof(freshness), &valid); ``` The verifier supports the pre-7.2 and 7.2+ signed-data formats and ECC or RSA @@ -742,9 +754,18 @@ trust in that key. The application must validate the attestation key's certificate/provisioning chain separately. `wc_se050_validate_provisioned_key()` combines a SHA-256 attested read, -signature verification, and exact DER public-key comparison. The attestation -key and certificate chain are customer-provisioned; reserved NXP credentials -are variant-specific and are not selected automatically. +signature verification, and exact DER public-key comparison. It takes the +same caller-generated 16-byte freshness challenge: + +```c +ret = wc_se050_validate_provisioned_key(keyId, attestKeyId, + expectedPublicDer, expectedPublicDerSz, attestPublicDer, + attestPublicDerSz, freshness, sizeof(freshness), &valid); +``` + +The attestation key and certificate chain are application-provisioned; +reserved NXP credentials are variant-specific and are not selected +automatically. ### wolfSSL SE050 Factory Reset diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 39ee3f91374..52721f8b4c4 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -679,7 +679,7 @@ static int se050_scp03_encrypt_block(const byte* key, const byte* in, ALIGN16 byte alignedOut[SE050_SCP03_KEY_SZ]; XMEMSET(&aes, 0, sizeof(aes)); -#if defined(WOLFSSL_SE050_CRYPT) && defined(HAVE_AESGCM) +#ifdef WOLFSSL_SE050_CRYPT /* SCP03 static keys are host secrets. Never route DEK wrapping or KCV * generation back through the secure element. */ aes.useSWCrypt = 1; @@ -897,32 +897,13 @@ int wc_se050_scp03_rotate_keys_seed(const byte* seed, word32 seedSz, #endif /* HAVE_HKDF */ #endif /* WOLFSSL_SE050_SCP03_ROTATE && SE050_RUNTIME_SCP03 */ -/** - * Erase and free an object stored in SE050. - * - * keyId ID of object to erase - * - * Returns 0 on success, negative on error. - */ -int wc_se050_erase_object(word32 id) +static int se050_erase_object_locked(word32 id) { int ret = 0; sss_object_t object; sss_key_store_t host_keystore; sss_status_t status = kStatus_SSS_Success; -#ifdef SE050_DEBUG - printf("wc_se050_erase_object: id %d\n", id); -#endif - - if (cfg_se050_i2c_pi == NULL) { - return WC_NO_ERR_TRACE(BAD_STATE_E); - } - - if (wolfSSL_CryptHwMutexLock() != 0) { - return BAD_MUTEX_E; - } - status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); if (status == kStatus_SSS_Success) { status = sss_key_store_allocate(&host_keystore, @@ -938,7 +919,6 @@ int wc_se050_erase_object(word32 id) status = sss_key_store_erase_key(&host_keystore, &object); sss_key_object_free(&object); } - wolfSSL_CryptHwMutexUnLock(); if (status != kStatus_SSS_Success) { ret = WC_HW_E; @@ -947,6 +927,34 @@ int wc_se050_erase_object(word32 id) return ret; } +/** + * Erase and free an object stored in SE050. + * + * keyId ID of object to erase + * + * Returns 0 on success, negative on error. + */ +int wc_se050_erase_object(word32 id) +{ + int ret; + +#ifdef SE050_DEBUG + printf("wc_se050_erase_object: id %d\n", id); +#endif + + if (cfg_se050_i2c_pi == NULL) { + return WC_NO_ERR_TRACE(BAD_STATE_E); + } + + if (wolfSSL_CryptHwMutexLock() != 0) { + return BAD_MUTEX_E; + } + ret = se050_erase_object_locked(id); + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + word32 se050_allocate_key(int keyType) { word32 keyId = 0; @@ -1168,13 +1176,16 @@ int se050_aes_set_key(Aes* aes, const byte* key, word32 keylen, aes->rounds = keylen/4 + 6; - /* free existing key in slot first before storing new one */ - ret = wc_se050_erase_object(aes->keyId); - if (ret != 0) { - wolfSSL_CryptHwMutexUnLock(); - return ret; + /* Free an existing key in the slot before storing a replacement. */ + if (aes->keyIdSet != 0U) { + ret = se050_erase_object_locked(aes->keyId); + if (ret != 0) { + wolfSSL_CryptHwMutexUnLock(); + return ret; + } + aes->keyId = 0; + aes->keyIdSet = 0; } - aes->keyIdSet = 0; status = sss_key_store_context_init(&host_keystore, cfg_se050_i2c_pi); if (status == kStatus_SSS_Success) { @@ -1653,9 +1664,6 @@ int wc_se050_attest_object(word32 keyId, word32 attestKeyId, { int ret = 0; int i; - byte generatedRandom[SE050_ATTEST_RANDOM_SIZE]; - const byte* freshness = random; - word32 freshnessSz = randomSz; size_t valueSz; size_t valueBitSz = 0; sss_algorithm_t algorithm = kAlgorithm_None; @@ -1666,32 +1674,17 @@ int wc_se050_attest_object(word32 keyId, word32 attestKeyId, sss_se05x_object_t* seAttestObject; sss_status_t status = kStatus_SSS_Fail; - if ((result == NULL) || (keyId == attestKeyId) || - ((random == NULL) && (randomSz != 0U)) || - ((random != NULL) && (randomSz != SE050_ATTEST_RANDOM_SIZE))) { + if ((result == NULL) || (keyId == attestKeyId) || (random == NULL) || + (randomSz != SE050_ATTEST_RANDOM_SIZE)) { return BAD_FUNC_ARG; } if (cfg_se050_i2c_pi == NULL) { return WC_NO_ERR_TRACE(BAD_STATE_E); } - if (freshness == NULL) { - #if !defined(WC_NO_RNG) && !defined(WOLFSSL_SE050_NO_TRNG) - ret = se050_get_random_number((word32)sizeof(generatedRandom), - generatedRandom); - if (ret != 0) { - return ret; - } - freshness = generatedRandom; - freshnessSz = (word32)sizeof(generatedRandom); - #else - return WC_NO_ERR_TRACE(NOT_COMPILED_IN); - #endif - } - XMEMSET(result, 0, sizeof(*result)); result->hashAlgo = hashAlgo; - XMEMCPY(result->freshness, freshness, SE050_ATTEST_RANDOM_SIZE); + XMEMCPY(result->freshness, random, SE050_ATTEST_RANDOM_SIZE); for (i = 0; i < SE05X_MAX_ATTST_DATA; i++) { result->raw.data[i].attributeLen = sizeof(result->raw.data[i].attribute); @@ -1740,14 +1733,13 @@ int wc_se050_attest_object(word32 keyId, word32 attestKeyId, status = sss_se05x_key_store_get_key_attst( (sss_se05x_key_store_t*)&keyStore, seObject, result->value, &valueSz, &valueBitSz, seAttestObject, algorithm, - (byte*)freshness, freshnessSz, &result->raw); + result->freshness, sizeof(result->freshness), &result->raw); if (status == kStatus_SSS_Success) { result->valueSz = (word32)valueSz; } } wolfSSL_CryptHwMutexUnLock(); - ForceZero(generatedRandom, sizeof(generatedRandom)); if (status != kStatus_SSS_Success) { return (ret != 0) ? ret : WC_HW_E; } @@ -1761,6 +1753,8 @@ int wc_se050_attest_object(word32 keyId, word32 attestKeyId, return 0; } +#if !defined(NO_HASH_WRAPPER) && !defined(NO_SIG_WRAPPER) && \ + !defined(NO_ASN) static int se050_attested_component(const wc_se050_attst_result* result, word32 componentIndex, byte* component, word32* componentSz) { @@ -2080,12 +2074,13 @@ static int se050_verify_attestation_freshness( return 0; #endif } +#endif /* !NO_HASH_WRAPPER && !NO_SIG_WRAPPER && !NO_ASN */ int wc_se050_verify_attestation(const wc_se050_attst_result* result, const byte* attestPubDer, word32 attestPubDerSz, const byte* expectedRandom, word32 expectedRandomSz, int* res) { -#if defined(NO_HASH_WRAPPER) || defined(NO_ASN) +#if defined(NO_HASH_WRAPPER) || defined(NO_SIG_WRAPPER) || defined(NO_ASN) (void)result; (void)attestPubDer; (void)attestPubDerSz; @@ -2244,7 +2239,8 @@ int wc_se050_verify_attestation(const wc_se050_attst_result* result, int wc_se050_validate_provisioned_key(word32 keyId, word32 attestKeyId, const byte* expectedPubDer, word32 expectedPubDerSz, - const byte* attestPubDer, word32 attestPubDerSz, int* res) + const byte* attestPubDer, word32 attestPubDerSz, const byte* random, + word32 randomSz, int* res) { int ret; int verified = 0; @@ -2252,17 +2248,17 @@ int wc_se050_validate_provisioned_key(word32 keyId, word32 attestKeyId, if ((expectedPubDer == NULL) || (expectedPubDerSz == 0U) || (attestPubDer == NULL) || (attestPubDerSz == 0U) || + (random == NULL) || (randomSz != SE050_ATTEST_RANDOM_SIZE) || (res == NULL)) { return BAD_FUNC_ARG; } *res = 0; ret = wc_se050_attest_object(keyId, attestKeyId, WC_HASH_TYPE_SHA256, - NULL, 0, &result); + random, randomSz, &result); if (ret == 0) ret = wc_se050_verify_attestation(&result, attestPubDer, - attestPubDerSz, result.freshness, - (word32)sizeof(result.freshness), &verified); + attestPubDerSz, random, randomSz, &verified); if ((ret == 0) && verified && (result.valueSz == expectedPubDerSz) && (XMEMCMP(result.value, expectedPubDer, expectedPubDerSz) == 0)) { *res = 1; diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 2e396516740..a3d9e2dd43d 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -5627,19 +5627,12 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) #include int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz){ - int ret = 0; - (void)os; if (output == NULL) { return BUFFER_E; } - ret = wolfSSL_CryptHwMutexLock(); - if (ret == 0) { - ret = se050_get_random_number(sz, output); - wolfSSL_CryptHwMutexUnLock(); - } - return ret; + return se050_get_random_number(sz, output); } #elif defined(WOLFSSL_NXP_RNG_1) diff --git a/wolfssl/wolfcrypt/port/nxp/se050_port.h b/wolfssl/wolfcrypt/port/nxp/se050_port.h index 62e907a080a..33bc6b96e6f 100644 --- a/wolfssl/wolfcrypt/port/nxp/se050_port.h +++ b/wolfssl/wolfcrypt/port/nxp/se050_port.h @@ -26,6 +26,11 @@ #include #include /* for MATH_INT_T */ +#if defined(WOLFSSL_SE050_SCP03_ROTATE) && \ + (defined(NO_AES) || !defined(WOLFSSL_AES_DIRECT)) + #error WOLFSSL_SE050_SCP03_ROTATE requires AES direct support +#endif + #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wundef" @@ -277,7 +282,8 @@ WOLFSSL_API int wc_se050_get_object_attributes(word32 keyId, byte* attr, word32* attrSz); #ifndef WOLFSSL_SE050_NO_ATTEST -/** Read and attest an object. Freshness must be 16 bytes or NULL. */ +/** Read and attest an object using a caller-generated 16-byte freshness + * challenge. */ WOLFSSL_API int wc_se050_attest_object(word32 keyId, word32 attestKeyId, enum wc_HashType hashAlgo, const byte* random, word32 randomSz, wc_se050_attst_result* result); @@ -287,10 +293,12 @@ WOLFSSL_API int wc_se050_verify_attestation( const wc_se050_attst_result* result, const byte* attestPubDer, word32 attestPubDerSz, const byte* expectedRandom, word32 expectedRandomSz, int* res); -/** Attest a key, verify the signature, and compare its public-key DER. */ +/** Attest a key with a caller-generated 16-byte freshness challenge, verify + * the signature, and compare its public-key DER. */ WOLFSSL_API int wc_se050_validate_provisioned_key(word32 keyId, word32 attestKeyId, const byte* expectedPubDer, word32 expectedPubDerSz, - const byte* attestPubDer, word32 attestPubDerSz, int* res); + const byte* attestPubDer, word32 attestPubDerSz, const byte* random, + word32 randomSz, int* res); #endif /* Private Functions */