From 596a9a574cad84508cf62d49d035ad4f87099049 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Tue, 21 Jul 2026 17:46:06 +0100 Subject: [PATCH 01/16] SE050: create ECDH derive target object for applet 7.2 middleware Middleware built for SE05x applet >= 07_02 (required for SE052) derives the ECDH shared secret with Se05x_API_ECDHGenerateSharedSecret_InObject, which requires TLV[TAG_7] to reference an existing HMACKey object sized exactly to the shared secret; otherwise the applet returns SW 0x6985 (conditions not satisfied). The port never created this object, so ECDH offload failed with WC_HW_E on such builds. Create the derive target as an HMACKey object of the exact secret size before the derive, for both ECC and Curve25519 shared secrets. Read the result back as AES type since sss_se05x_key_store_get_key has no HMAC read case. --- wolfcrypt/src/port/nxp/se050_port.c | 47 +++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 5e62fed708f..4bfa129210d 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2850,25 +2850,42 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, - kSSS_CipherType_Binary, + kSSS_CipherType_HMAC, keySize, kKeyObject_Mode_Transient); } + if (status == kStatus_SSS_Success) { + byte keyBuf[MAX_ECC_BYTES]; + + /* Try to delete existing key first, ignore return since will + * fail if no key exists yet */ + sss_key_store_erase_key(&host_keystore, &deriveKey); + + /* SE05x applet 7.2 and later stores the ECDH result into an + * existing HMACKey object whose size must equal the shared + * secret exactly, so the object must be created before the + * derive (returns SW 0x6985 otherwise) */ + XMEMSET(keyBuf, 0, sizeof(keyBuf)); + status = sss_key_store_set_key(&host_keystore, &deriveKey, + keyBuf, keySize, keySize * 8, NULL, 0); + if (status == kStatus_SSS_Success) { + deriveKeyCreated = 1; + } + } if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret); if (status == kStatus_SSS_Success) { - /* Try to delete existing key first, ignore return since will - * fail if no key exists yet */ - sss_key_store_erase_key(&host_keystore, &deriveKey); status = sss_derive_key_dh(&ctx_derive_key, &ref_public_key, &deriveKey); } if (status == kStatus_SSS_Success) { size_t outlenSz = (size_t)*outlen; size_t outlenSzBits = outlenSz * 8; - deriveKeyCreated = 1; + /* sss_key_store_get_key has no HMAC read case, so read the + * object back as AES type (both are a plain ReadObject) */ + deriveKey.cipherType = kSSS_CipherType_AES; /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out, &outlenSz, &outlenSzBits); @@ -3445,10 +3462,25 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, - kSSS_CipherType_Binary, + kSSS_CipherType_HMAC, keySize, kKeyObject_Mode_Transient); } + if (status == kStatus_SSS_Success) { + byte keyBuf[CURVE25519_KEYSIZE]; + + /* Try to delete existing key first, ignore return since will + * fail if no key exists yet */ + sss_key_store_erase_key(&host_keystore, &deriveKey); + + /* SE05x applet 7.2 and later stores the ECDH result into an + * existing HMACKey object whose size must equal the shared + * secret exactly, so the object must be created before the + * derive (returns SW 0x6985 otherwise) */ + XMEMSET(keyBuf, 0, sizeof(keyBuf)); + status = sss_key_store_set_key(&host_keystore, &deriveKey, + keyBuf, keySize, keySize * 8, NULL, 0); + } if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, @@ -3460,6 +3492,9 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, if (status == kStatus_SSS_Success) { size_t outlenSz = sizeof(out->point); size_t outlenSzBits = outlenSz * 8; + /* sss_key_store_get_key has no HMAC read case, so read the + * object back as AES type (both are a plain ReadObject) */ + deriveKey.cipherType = kSSS_CipherType_AES; /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out->point, &outlenSz, &outlenSzBits); From e7ed0283a15adce36bf330517690156169ddb570 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 23 Jul 2026 12:07:49 +0100 Subject: [PATCH 02/16] SE050: only flag curve25519 derive key after it is created Align se050_curve25519_shared_secret with the ECC path: set deriveKeyCreated only after sss_key_store_set_key succeeds, so the cleanup path cannot erase or free a derive key object whose handle was never allocated. --- wolfcrypt/src/port/nxp/se050_port.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 4bfa129210d..1b9aa7db45d 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -3458,7 +3458,6 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, } if (status == kStatus_SSS_Success) { word32 keyIdAes = se050_allocate_key(SE050_AES_KEY); - deriveKeyCreated = 1; status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, @@ -3480,6 +3479,9 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, XMEMSET(keyBuf, 0, sizeof(keyBuf)); status = sss_key_store_set_key(&host_keystore, &deriveKey, keyBuf, keySize, keySize * 8, NULL, 0); + if (status == kStatus_SSS_Success) { + deriveKeyCreated = 1; + } } if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, From 6213bbaf58492b11b36fa7ac5436734b624d732d Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 23 Jul 2026 12:07:49 +0100 Subject: [PATCH 03/16] SE050 CI: pin strict simulator for applet 7.2 ECDH contract Bump SIMULATORS_REF to the simulator commit that supports HMACKey WriteSymmKey and the applet 7.2 ECDH InObject contract, and run the test container with SE050_SIM_STRICT_ECDH=1 so the simulator refuses an ECDH derive whose Tag7 target object was not pre-created. This is the failure mode seen on SE05x applet >= 7.2 hardware and reproduces it in CI against the unfixed port. The pinned simulator commit also consumes the WOLFSSL_EXTRA_CFLAGS build ARG, so the only-key-id matrix leg now really builds with -DWOLFSSL_SE050_ONLY_KEY_ID (previously the ARG was not consumed and the leg silently built the default configuration), and it fixes the simulator to derive the EC public key on private-only key pair import so wc_ecc_use_key_id can read the public part back. Raise the job timeout for cold image rebuilds until the weekend cron refreshes the registry cache for the new ref. --- .github/workflows/se050-sim.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 9799ddb3078..a5bd428858e 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,14 +31,16 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 745893640e21a15b7df8c70567c522953aba2f2c + SIMULATORS_REF: 41f85a5ec53886b5090eb641cc9754d24818ba91 jobs: se050_sim: name: wolfCrypt against SE050 simulator (${{ matrix.name }}) if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} runs-on: ubuntu-24.04 - timeout-minutes: 30 + # Cold image rebuilds (registry cache miss after a SIMULATORS_REF bump, + # until the weekend cron refreshes the cache) need well over 30 minutes. + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -100,4 +102,8 @@ jobs: cache-to: ${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.name == 'default') && 'type=registry,ref=ghcr.io/wolfssl/wolfssl-sim-cache:se050,mode=max' || '' }} - name: Run wolfCrypt tests against simulator - run: docker run --rm wolfssl-se050-sim:ci-${{ matrix.name }} + # SE050_SIM_STRICT_ECDH=1 makes the simulator enforce the applet 7.2 + # ECDH InObject contract: the Tag7 target must be pre-created as an + # HMACKey object of exactly the shared-secret size or the derive is + # refused with SW 0x6985, matching SE05x applet >= 7.2 hardware. + run: docker run --rm -e SE050_SIM_STRICT_ECDH=1 wolfssl-se050-sim:ci-${{ matrix.name }} From 356aa6338bad3deba432063ba8e1fab24838a260 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 23 Jul 2026 12:32:09 +0100 Subject: [PATCH 04/16] SE050 CI: correct pinned simulator commit id The previous pin abbreviated to the right commit but the full hash was invalid, so the simulator checkout step could never resolve it. Pin the full commit id of the merged simulator change. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index a5bd428858e..5aa49bafe02 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 41f85a5ec53886b5090eb641cc9754d24818ba91 + SIMULATORS_REF: 41f85a521c66a3f7ed57fda745a39495f493e9d9 jobs: se050_sim: From 473101ac95bf0c2e75d23db288592a5b6c90bcb0 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 10:29:47 +0100 Subject: [PATCH 05/16] SE050: attach read policy to ECDH derive target object (ZD 22212) Applet 7.2 denies ReadObject on a symmetric key object that was created with no policy attached, so the shared secret written into the derive target by Se05x_API_ECDHGenerateSharedSecret_InObject could not be exported: sss_key_store_get_key failed with SW 0x6986 (command not allowed) on SE05x applet >= 7.2 hardware. Create the derive target with an attached common policy granting read, write and delete. An attached policy replaces the applet default entirely, so write (the ECDH engine storing the result) and delete (the cleanup path) must be granted explicitly alongside read. Guarded by SSS_HAVE_SE05X_VER_GTE_07_02 so builds against older middleware keep creating the object with no policy attached. --- wolfcrypt/src/port/nxp/se050_port.c | 50 +++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 1b9aa7db45d..bfbf7555f7b 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2856,6 +2856,10 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, } if (status == kStatus_SSS_Success) { byte keyBuf[MAX_ECC_BYTES]; + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 + sss_policy_u commonPol; + sss_policy_t derivePolicy; + #endif /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ @@ -2866,8 +2870,29 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, * secret exactly, so the object must be created before the * derive (returns SW 0x6985 otherwise) */ XMEMSET(keyBuf, 0, sizeof(keyBuf)); + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 + /* Applet 7.2 denies ReadObject on a symmetric key object created + * with no policy attached (SW 0x6986), so the derived secret + * could not be exported. Attach a policy allowing the host to + * read the secret back, overwrite the object and delete it; an + * attached policy replaces the applet default entirely, so write + * and delete must be granted explicitly as well */ + XMEMSET(&commonPol, 0, sizeof(commonPol)); + XMEMSET(&derivePolicy, 0, sizeof(derivePolicy)); + commonPol.type = KPolicy_Common; + commonPol.auth_obj_id = 0; + commonPol.policy.common.can_Read = 1; + commonPol.policy.common.can_Write = 1; + commonPol.policy.common.can_Delete = 1; + derivePolicy.nPolicies = 1; + derivePolicy.policies[0] = &commonPol; + status = sss_key_store_set_key(&host_keystore, &deriveKey, + keyBuf, keySize, keySize * 8, &derivePolicy, + sizeof(derivePolicy)); + #else status = sss_key_store_set_key(&host_keystore, &deriveKey, keyBuf, keySize, keySize * 8, NULL, 0); + #endif if (status == kStatus_SSS_Success) { deriveKeyCreated = 1; } @@ -3467,6 +3492,10 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, } if (status == kStatus_SSS_Success) { byte keyBuf[CURVE25519_KEYSIZE]; + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 + sss_policy_u commonPol; + sss_policy_t derivePolicy; + #endif /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ @@ -3477,8 +3506,29 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, * secret exactly, so the object must be created before the * derive (returns SW 0x6985 otherwise) */ XMEMSET(keyBuf, 0, sizeof(keyBuf)); + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 + /* Applet 7.2 denies ReadObject on a symmetric key object created + * with no policy attached (SW 0x6986), so the derived secret + * could not be exported. Attach a policy allowing the host to + * read the secret back, overwrite the object and delete it; an + * attached policy replaces the applet default entirely, so write + * and delete must be granted explicitly as well */ + XMEMSET(&commonPol, 0, sizeof(commonPol)); + XMEMSET(&derivePolicy, 0, sizeof(derivePolicy)); + commonPol.type = KPolicy_Common; + commonPol.auth_obj_id = 0; + commonPol.policy.common.can_Read = 1; + commonPol.policy.common.can_Write = 1; + commonPol.policy.common.can_Delete = 1; + derivePolicy.nPolicies = 1; + derivePolicy.policies[0] = &commonPol; + status = sss_key_store_set_key(&host_keystore, &deriveKey, + keyBuf, keySize, keySize * 8, &derivePolicy, + sizeof(derivePolicy)); + #else status = sss_key_store_set_key(&host_keystore, &deriveKey, keyBuf, keySize, keySize * 8, NULL, 0); + #endif if (status == kStatus_SSS_Success) { deriveKeyCreated = 1; } From 4c055f52727ab1773786d794a5b0c8c1238cd71e Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 10:41:21 +0100 Subject: [PATCH 06/16] SE050 CI: pin simulator with applet 7.2 read-policy enforcement Bump SIMULATORS_REF to the simulator commit that, in strict mode, refuses ReadObject on an HMACKey object unless the policy attached at creation grants POLICY_OBJ_ALLOW_READ. This reproduces the SW 0x6986 derive-target readback failure seen on SE05x applet >= 7.2 hardware (ZD 22212) and verifies the attached-policy fix in the previous commit. The pinned commit is wolfSSL/simulators PR #10 and becomes fetchable from the upstream clone once that PR is merged. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 5aa49bafe02..83f8a0413c2 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 41f85a521c66a3f7ed57fda745a39495f493e9d9 + SIMULATORS_REF: 7cb780a86ae25db8571463f52f7d495caa59e4b7 jobs: se050_sim: From 50bb36d3bc059e21cc6ca54fe6abf16183e26b86 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 15:50:09 +0100 Subject: [PATCH 07/16] SE050: keep Binary ECDH derive target on pre-7.2 middleware Real SE050 hardware (applet 3.1.1, JCOP4) refuses ReadObject on a symmetric key object created without a read policy just like applet 7.2 does, and pre-7.2 middleware has no way to grant that policy: sss_policy_common_u can_Read maps to POLICY_OBJ_ALLOW_READ only for SSS_HAVE_SE05X_VER_GTE_07_02 builds and the symmetric key policy union has no read flag at all. Switching the derive target to an HMACKey object unconditionally therefore broke ECDH offload on applet 3.x parts with SW 0x6986 at the shared secret export. Restrict the HMACKey target and its attached read policy to SSS_HAVE_SE05X_VER_GTE_07_02 builds and restore the original Binary object flow otherwise: no pre-created target, erase before derive, and the middleware creates the object when storing the derived secret. Binary objects are readable without an attached policy. Verified on SE050C (applet 3.1.1) hardware: the ECC and CURVE25519 wolfCrypt tests fail with SW 0x6986 without this change and pass with it, matching master behavior on the same part. The applet 7.2 path is unchanged. --- wolfcrypt/src/port/nxp/se050_port.c | 56 ++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index bfbf7555f7b..486b6db1558 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2850,16 +2850,23 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 kSSS_CipherType_HMAC, + #else + /* The applet denies ReadObject on a symmetric key object + * created without a read policy, and pre-7.2 middleware has + * no way to grant one, so keep the Binary derive target + * which ReadObject allows by default */ + kSSS_CipherType_Binary, + #endif keySize, kKeyObject_Mode_Transient); } +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 if (status == kStatus_SSS_Success) { byte keyBuf[MAX_ECC_BYTES]; - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 sss_policy_u commonPol; sss_policy_t derivePolicy; - #endif /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ @@ -2870,8 +2877,7 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, * secret exactly, so the object must be created before the * derive (returns SW 0x6985 otherwise) */ XMEMSET(keyBuf, 0, sizeof(keyBuf)); - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - /* Applet 7.2 denies ReadObject on a symmetric key object created + /* The applet denies ReadObject on a symmetric key object created * with no policy attached (SW 0x6986), so the derived secret * could not be exported. Attach a policy allowing the host to * read the secret back, overwrite the object and delete it; an @@ -2889,28 +2895,34 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, status = sss_key_store_set_key(&host_keystore, &deriveKey, keyBuf, keySize, keySize * 8, &derivePolicy, sizeof(derivePolicy)); - #else - status = sss_key_store_set_key(&host_keystore, &deriveKey, - keyBuf, keySize, keySize * 8, NULL, 0); - #endif if (status == kStatus_SSS_Success) { deriveKeyCreated = 1; } } +#endif if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret); if (status == kStatus_SSS_Success) { + #if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) + /* Try to delete existing key first, ignore return since will + * fail if no key exists yet */ + sss_key_store_erase_key(&host_keystore, &deriveKey); + #endif status = sss_derive_key_dh(&ctx_derive_key, &ref_public_key, &deriveKey); } if (status == kStatus_SSS_Success) { size_t outlenSz = (size_t)*outlen; size_t outlenSzBits = outlenSz * 8; + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 /* sss_key_store_get_key has no HMAC read case, so read the * object back as AES type (both are a plain ReadObject) */ deriveKey.cipherType = kSSS_CipherType_AES; + #else + deriveKeyCreated = 1; + #endif /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out, &outlenSz, &outlenSzBits); @@ -3486,16 +3498,23 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 kSSS_CipherType_HMAC, + #else + /* The applet denies ReadObject on a symmetric key object + * created without a read policy, and pre-7.2 middleware has + * no way to grant one, so keep the Binary derive target + * which ReadObject allows by default */ + kSSS_CipherType_Binary, + #endif keySize, kKeyObject_Mode_Transient); } +#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 if (status == kStatus_SSS_Success) { byte keyBuf[CURVE25519_KEYSIZE]; - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 sss_policy_u commonPol; sss_policy_t derivePolicy; - #endif /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ @@ -3506,8 +3525,7 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, * secret exactly, so the object must be created before the * derive (returns SW 0x6985 otherwise) */ XMEMSET(keyBuf, 0, sizeof(keyBuf)); - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - /* Applet 7.2 denies ReadObject on a symmetric key object created + /* The applet denies ReadObject on a symmetric key object created * with no policy attached (SW 0x6986), so the derived secret * could not be exported. Attach a policy allowing the host to * read the secret back, overwrite the object and delete it; an @@ -3525,28 +3543,34 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, status = sss_key_store_set_key(&host_keystore, &deriveKey, keyBuf, keySize, keySize * 8, &derivePolicy, sizeof(derivePolicy)); - #else - status = sss_key_store_set_key(&host_keystore, &deriveKey, - keyBuf, keySize, keySize * 8, NULL, 0); - #endif if (status == kStatus_SSS_Success) { deriveKeyCreated = 1; } } +#endif if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret); if (status == kStatus_SSS_Success) { + #if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) + /* Try to delete existing key first, ignore return since will + * fail if no key exists yet */ + sss_key_store_erase_key(&host_keystore, &deriveKey); + #endif status = sss_derive_key_dh(&ctx_derive_key, &ref_public_key, &deriveKey); } if (status == kStatus_SSS_Success) { size_t outlenSz = sizeof(out->point); size_t outlenSzBits = outlenSz * 8; + #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 /* sss_key_store_get_key has no HMAC read case, so read the * object back as AES type (both are a plain ReadObject) */ deriveKey.cipherType = kSSS_CipherType_AES; + #else + deriveKeyCreated = 1; + #endif /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out->point, &outlenSz, &outlenSzBits); From 2ae4ec73002f85454a455d42270c40ef40d98284 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 15:57:15 +0100 Subject: [PATCH 08/16] SE050 CI: pin simulator that always enforces symmetric read policy Bump SIMULATORS_REF to the simulator commit that refuses ReadObject on an HMACKey object without POLICY_OBJ_ALLOW_READ in all modes, matching real hardware on every applet generation as verified on SE050C applet 3.1.1 silicon. With this pin a plain (non-strict) CI run reproduces the SW 0x6986 readback failure that previously only appeared on hardware, covering the pre-7.2 Binary derive target fix in the previous commit. The pinned commit is wolfSSL/simulators PR #11 and becomes fetchable from the upstream clone once that PR is merged. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 83f8a0413c2..6483dfb565d 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 7cb780a86ae25db8571463f52f7d495caa59e4b7 + SIMULATORS_REF: d8ca6318796c3cde35ca9a8af44250ab85335e21 jobs: se050_sim: From f165744b519c517798ac51d45a28997fb25941bf Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 16:06:23 +0100 Subject: [PATCH 09/16] Add SSS_HAVE_SE05X_VER_GTE_07_02 to known macro extras The SE050 port now keys the ECDH derive target type and read policy off the middleware's SSS_HAVE_SE05X_VER_GTE_07_02 feature macro. --- .wolfssl_known_macro_extras | 1 + 1 file changed, 1 insertion(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 814001c990c..73d4a1b7a01 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -609,6 +609,7 @@ SP_USE_DIVTI3 SQRTMOD_USE_MOD_EXP SSL_SNIFFER_EXPORTS SSN_BUILDING_LIBYASSL +SSS_HAVE_SE05X_VER_GTE_07_02 STATIC_CHUNKS_ONLY STM32F107xC STM32F207xx From a2bde67b70e16a0e886691d1384ce15bdfde18d5 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 30 Jul 2026 16:07:00 +0100 Subject: [PATCH 10/16] SE050 CI: update pinned simulator to PR 11 head The pinned simulator gained an sdk-test fix and review changes; move SIMULATORS_REF to the final wolfSSL/simulators PR #11 head. Becomes fetchable from the upstream clone once that PR is merged. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 6483dfb565d..6ffde1e0101 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: d8ca6318796c3cde35ca9a8af44250ab85335e21 + SIMULATORS_REF: 6c12fb15b492e35ce8ca82dc97fbd53f07fbe6fd jobs: se050_sim: From d75c09296c08d720eb6df6c1c371e2172fa29905 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 3 Aug 2026 14:43:03 +0100 Subject: [PATCH 11/16] SE050: derive ECDH shared secret via direct APDU on applet 7.2 The applet refuses to export a symmetric key object regardless of the policy attached at its creation: on SE051 applet 7.2.0 hardware, ReadObject on an HMACKey object whose attributes confirm an attached POLICY_OBJ_ALLOW_READ still fails with SW 0x6986, so a derived secret stored in an SE05x object can never be read back and the previous attach-a-read-policy approach cannot work (ZD 22212). Se05x_API_ECDHGenerateSharedSecret, which returns the shared secret directly in the APDU response, is accepted by the same applet. It is also what sss_se05x_derive_key_dh itself uses whenever the derived key object lives in a host keystore, so use it for the applet >= 7.2 ECDH offload instead of deriving into an SE05x object: pass the private key id and the peer public point, taken from the wolfSSL key when the peer is a software key or read back from the resident public key object otherwise. Montgomery points and secrets are byte swapped around the call, matching the middleware's own handling. The pre-7.2 flow is restructured but behaviorally unchanged (Binary derive target created by the middleware during the derive, read back afterwards), and drops fewer APDUs per derive on 7.2 since no target object is created, read or deleted. Verified on SE051 applet 7.2.0 hardware: ECC P-256 and X25519 shared secrets derive successfully in both directions, where the previous approach failed with SW 0x6986. The pre-7.2 path remains as validated on SE050C applet 3.1.1 hardware. --- wolfcrypt/src/port/nxp/se050_port.c | 239 +++++++++++++++------------- 1 file changed, 131 insertions(+), 108 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 486b6db1558..467290e46fa 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2845,84 +2845,34 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&deriveKey, &host_keystore); } +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { word32 keyIdAes = se050_allocate_key(SE050_AES_KEY); status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - kSSS_CipherType_HMAC, - #else - /* The applet denies ReadObject on a symmetric key object - * created without a read policy, and pre-7.2 middleware has - * no way to grant one, so keep the Binary derive target - * which ReadObject allows by default */ + /* The applet denies ReadObject on a symmetric key object no + * matter what policy is attached, so the derive target must + * be a Binary object, which ReadObject allows by default */ kSSS_CipherType_Binary, - #endif keySize, kKeyObject_Mode_Transient); } -#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - if (status == kStatus_SSS_Success) { - byte keyBuf[MAX_ECC_BYTES]; - sss_policy_u commonPol; - sss_policy_t derivePolicy; - - /* Try to delete existing key first, ignore return since will - * fail if no key exists yet */ - sss_key_store_erase_key(&host_keystore, &deriveKey); - - /* SE05x applet 7.2 and later stores the ECDH result into an - * existing HMACKey object whose size must equal the shared - * secret exactly, so the object must be created before the - * derive (returns SW 0x6985 otherwise) */ - XMEMSET(keyBuf, 0, sizeof(keyBuf)); - /* The applet denies ReadObject on a symmetric key object created - * with no policy attached (SW 0x6986), so the derived secret - * could not be exported. Attach a policy allowing the host to - * read the secret back, overwrite the object and delete it; an - * attached policy replaces the applet default entirely, so write - * and delete must be granted explicitly as well */ - XMEMSET(&commonPol, 0, sizeof(commonPol)); - XMEMSET(&derivePolicy, 0, sizeof(derivePolicy)); - commonPol.type = KPolicy_Common; - commonPol.auth_obj_id = 0; - commonPol.policy.common.can_Read = 1; - commonPol.policy.common.can_Write = 1; - commonPol.policy.common.can_Delete = 1; - derivePolicy.nPolicies = 1; - derivePolicy.policies[0] = &commonPol; - status = sss_key_store_set_key(&host_keystore, &deriveKey, - keyBuf, keySize, keySize * 8, &derivePolicy, - sizeof(derivePolicy)); - if (status == kStatus_SSS_Success) { - deriveKeyCreated = 1; - } - } -#endif if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret); if (status == kStatus_SSS_Success) { - #if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ sss_key_store_erase_key(&host_keystore, &deriveKey); - #endif status = sss_derive_key_dh(&ctx_derive_key, &ref_public_key, &deriveKey); } if (status == kStatus_SSS_Success) { size_t outlenSz = (size_t)*outlen; size_t outlenSzBits = outlenSz * 8; - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - /* sss_key_store_get_key has no HMAC read case, so read the - * object back as AES type (both are a plain ReadObject) */ - deriveKey.cipherType = kSSS_CipherType_AES; - #else deriveKeyCreated = 1; - #endif /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out, &outlenSz, &outlenSzBits); @@ -2932,6 +2882,59 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, sss_derive_key_context_free(&ctx_derive_key); } +#else + if (status == kStatus_SSS_Success) { + /* Middleware built for applet >= 7.2 derives into an SE05x + * resident object, but the applet refuses to export a symmetric + * key object regardless of the policy attached at its creation + * (verified on SE051 applet 7.2.0 hardware), so a derived secret + * stored in an object can never be read back. Use the direct + * APDU that returns the shared secret in the response instead, + * as the middleware itself does whenever the derived key lives + * in a host keystore. */ + byte peerPoint[SE050_ECC_DER_MAX]; + word32 peerPointSz = (word32)sizeof(peerPoint); + smStatus_t sm; + + if (public_key->keyIdSet == 0) { + ret = wc_ecc_export_x963(public_key, peerPoint, &peerPointSz); + if (ret != 0) { + status = kStatus_SSS_Fail; + } + } + else { + /* Peer public key is SE050-resident: read the DER encoding + * back and use the trailing uncompressed point */ + size_t derSz = sizeof(peerPoint); + size_t derSzBits = derSz * 8; + word32 pointSz = (word32)(1 + 2 * keySize); + status = sss_key_store_get_key(&host_keystore, &ref_public_key, + peerPoint, &derSz, &derSzBits); + if (status == kStatus_SSS_Success && derSz >= pointSz && + peerPoint[derSz - pointSz] == 0x04) { + XMEMMOVE(peerPoint, peerPoint + derSz - pointSz, pointSz); + peerPointSz = pointSz; + } + else { + status = kStatus_SSS_Fail; + } + } + if (status == kStatus_SSS_Success) { + size_t outSz = (size_t)*outlen; + sm = Se05x_API_ECDHGenerateSharedSecret( + &((sss_se05x_session_t*)cfg_se050_i2c_pi)->s_ctx, + private_key->keyId, peerPoint, peerPointSz, out, &outSz); + if (sm == SM_OK) { + *outlen = (word32)outSz; + } + else { + status = kStatus_SSS_Fail; + } + } + } + (void)ctx_derive_key; + (void)deriveKey; +#endif if (deriveKeyCreated) { sss_key_store_erase_key(&host_keystore, &deriveKey); sss_key_object_free(&deriveKey); @@ -3493,84 +3496,34 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&deriveKey, &host_keystore); } +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { word32 keyIdAes = se050_allocate_key(SE050_AES_KEY); status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_Default, - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - kSSS_CipherType_HMAC, - #else - /* The applet denies ReadObject on a symmetric key object - * created without a read policy, and pre-7.2 middleware has - * no way to grant one, so keep the Binary derive target - * which ReadObject allows by default */ + /* The applet denies ReadObject on a symmetric key object no + * matter what policy is attached, so the derive target must + * be a Binary object, which ReadObject allows by default */ kSSS_CipherType_Binary, - #endif keySize, kKeyObject_Mode_Transient); } -#if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - if (status == kStatus_SSS_Success) { - byte keyBuf[CURVE25519_KEYSIZE]; - sss_policy_u commonPol; - sss_policy_t derivePolicy; - - /* Try to delete existing key first, ignore return since will - * fail if no key exists yet */ - sss_key_store_erase_key(&host_keystore, &deriveKey); - - /* SE05x applet 7.2 and later stores the ECDH result into an - * existing HMACKey object whose size must equal the shared - * secret exactly, so the object must be created before the - * derive (returns SW 0x6985 otherwise) */ - XMEMSET(keyBuf, 0, sizeof(keyBuf)); - /* The applet denies ReadObject on a symmetric key object created - * with no policy attached (SW 0x6986), so the derived secret - * could not be exported. Attach a policy allowing the host to - * read the secret back, overwrite the object and delete it; an - * attached policy replaces the applet default entirely, so write - * and delete must be granted explicitly as well */ - XMEMSET(&commonPol, 0, sizeof(commonPol)); - XMEMSET(&derivePolicy, 0, sizeof(derivePolicy)); - commonPol.type = KPolicy_Common; - commonPol.auth_obj_id = 0; - commonPol.policy.common.can_Read = 1; - commonPol.policy.common.can_Write = 1; - commonPol.policy.common.can_Delete = 1; - derivePolicy.nPolicies = 1; - derivePolicy.policies[0] = &commonPol; - status = sss_key_store_set_key(&host_keystore, &deriveKey, - keyBuf, keySize, keySize * 8, &derivePolicy, - sizeof(derivePolicy)); - if (status == kStatus_SSS_Success) { - deriveKeyCreated = 1; - } - } -#endif if (status == kStatus_SSS_Success) { status = sss_derive_key_context_init(&ctx_derive_key, cfg_se050_i2c_pi, &ref_private_key, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret); if (status == kStatus_SSS_Success) { - #if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) /* Try to delete existing key first, ignore return since will * fail if no key exists yet */ sss_key_store_erase_key(&host_keystore, &deriveKey); - #endif status = sss_derive_key_dh(&ctx_derive_key, &ref_public_key, &deriveKey); } if (status == kStatus_SSS_Success) { size_t outlenSz = sizeof(out->point); size_t outlenSzBits = outlenSz * 8; - #if defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02 - /* sss_key_store_get_key has no HMAC read case, so read the - * object back as AES type (both are a plain ReadObject) */ - deriveKey.cipherType = kSSS_CipherType_AES; - #else deriveKeyCreated = 1; - #endif /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out->point, &outlenSz, &outlenSzBits); @@ -3580,6 +3533,76 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, sss_derive_key_context_free(&ctx_derive_key); } +#else + if (status == kStatus_SSS_Success) { + /* Middleware built for applet >= 7.2 derives into an SE05x + * resident object, but the applet refuses to export a symmetric + * key object regardless of the policy attached at its creation + * (verified on SE051 applet 7.2.0 hardware), so a derived secret + * stored in an object can never be read back. Use the direct + * APDU that returns the shared secret in the response instead, + * as the middleware itself does whenever the derived key lives + * in a host keystore. The applet speaks big endian for + * Montgomery keys, so the peer point and the returned secret are + * both byte swapped, matching sss_se05x_derive_key_dh. */ + byte peerPoint[CURVE25519_KEYSIZE]; + word32 peerPointSz = (word32)sizeof(peerPoint); + smStatus_t sm; + int i; + byte swp; + + if (public_key->keyIdSet == 0) { + ret = wc_curve25519_export_public_ex(public_key, peerPoint, + &peerPointSz, EC25519_LITTLE_ENDIAN); + if (ret != 0) { + status = kStatus_SSS_Fail; + } + } + else { + /* Peer public key is SE050-resident: read the DER encoding + * back; the raw little endian point is the trailing bytes */ + byte derBuf[CURVE25519_PUB_KEY_SIZE + 12]; + size_t derSz = sizeof(derBuf); + size_t derSzBits = derSz * 8; + status = sss_key_store_get_key(&host_keystore, &ref_public_key, + derBuf, &derSz, &derSzBits); + if (status == kStatus_SSS_Success && + derSz >= CURVE25519_KEYSIZE) { + XMEMCPY(peerPoint, derBuf + derSz - CURVE25519_KEYSIZE, + CURVE25519_KEYSIZE); + peerPointSz = CURVE25519_KEYSIZE; + } + else { + status = kStatus_SSS_Fail; + } + } + if (status == kStatus_SSS_Success) { + size_t outSz = sizeof(out->point); + for (i = 0; i < CURVE25519_KEYSIZE / 2; i++) { + swp = peerPoint[i]; + peerPoint[i] = peerPoint[CURVE25519_KEYSIZE - 1 - i]; + peerPoint[CURVE25519_KEYSIZE - 1 - i] = swp; + } + sm = Se05x_API_ECDHGenerateSharedSecret( + &((sss_se05x_session_t*)cfg_se050_i2c_pi)->s_ctx, + private_key->keyId, peerPoint, peerPointSz, + out->point, &outSz); + if (sm == SM_OK && outSz == CURVE25519_KEYSIZE) { + for (i = 0; i < CURVE25519_KEYSIZE / 2; i++) { + swp = out->point[i]; + out->point[i] = out->point[CURVE25519_KEYSIZE - 1 - i]; + out->point[CURVE25519_KEYSIZE - 1 - i] = swp; + } + out->pointSz = (word32)outSz; + } + else { + status = kStatus_SSS_Fail; + } + } + } + (void)ctx_derive_key; + (void)deriveKey; +#endif if (deriveKeyCreated) { sss_key_store_erase_key(&host_keystore, &deriveKey); sss_key_object_free(&deriveKey); From dc364d0eaab5575a752dee3c4d72ffd10dd676b4 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 3 Aug 2026 15:00:37 +0100 Subject: [PATCH 12/16] SE050 CI: pin simulator with direct ECDH variant support Bump SIMULATORS_REF to the simulator commit that accepts the Tag7-less direct ECDH form the port now uses on applet >= 7.2 and that refuses HMACKey ReadObject unconditionally, both matching SE051 applet 7.2.0 hardware. The pinned commit is wolfSSL/simulators PR #12 and becomes fetchable from the upstream clone once that PR is merged. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 6ffde1e0101..ca90a108c17 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 6c12fb15b492e35ce8ca82dc97fbd53f07fbe6fd + SIMULATORS_REF: 4c2b1ef089ebdd8fe4b9c624c13b5de8002dfcf8 jobs: se050_sim: From 160c616a97d8231abab8de184bacef3a50de1930 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 3 Aug 2026 15:07:34 +0100 Subject: [PATCH 13/16] SE050 CI: update pinned simulator to PR 12 head The pinned simulator gained review cleanups; move SIMULATORS_REF to the final wolfSSL/simulators PR #12 head. Becomes fetchable from the upstream clone once that PR is merged. --- .github/workflows/se050-sim.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index ca90a108c17..d43a83becf2 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -31,7 +31,7 @@ permissions: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SIMULATORS_REF: 4c2b1ef089ebdd8fe4b9c624c13b5de8002dfcf8 + SIMULATORS_REF: 895ff41b2447efe952e902c467c5ef479f3eda37 jobs: se050_sim: From e30200c2369e780e7cc2b35479cb1a368e6bf3d9 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 3 Aug 2026 16:34:23 +0100 Subject: [PATCH 14/16] Drop WOLFSSL_MEM_FAIL_COUNT from known macro extras tests/unit-mcdc/test_memory_whitebox.c defines the macro directly, so the macro check now knows it without the extras entry and flags the entry as unneeded. --- .wolfssl_known_macro_extras | 1 - 1 file changed, 1 deletion(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 73d4a1b7a01..9f6f6fe9f2b 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -892,7 +892,6 @@ WOLFSSL_MAKE_SYSTEM_NAME_LINUX WOLFSSL_MAKE_SYSTEM_NAME_WSL WOLFSSL_MANUALLY_SELECT_DEVICE_CONFIG WOLFSSL_MDK5 -WOLFSSL_MEM_FAIL_COUNT WOLFSSL_MICROCHIP_AESGCM WOLFSSL_MLKEM_ASM_TEST WOLFSSL_MLKEM_INVNTT_UNROLL From 6176a5a77daad3c85faa28defaf0bc42a3f736a5 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Tue, 4 Aug 2026 11:42:20 +0100 Subject: [PATCH 15/16] SE050: skip peer key upload on applet 7.2 ECDH path (peer review) The direct ECDH APDU carries the peer public point in the command, so uploading the peer key to the SE050 on the applet >= 7.2 path wasted APDU round trips, consumed a persistent object slot per distinct peer in the default build, and added a failure path the derive does not need. Confine the upload, the keyId bookkeeping and the keyCreated cleanup to the pre-7.2 arm; on 7.2 builds a reference object is only taken when the peer public key is already SE050-resident. Also from review: validate the ECC direct-APDU response length against the curve size, mirroring the Curve25519 arm; scope the derive-key state (deriveKey, ctx_derive_key, deriveKeyCreated and their init and cleanup) into the pre-7.2 arm instead of voiding it; and reword the CI workflow comment to describe SE050_SIM_STRICT_ECDH as a regression guard, noting the pre-7.2 arm is hardware-verified (SE050C applet 3.1.1) until an 03_XX matrix leg exists. Verified: wolfCrypt suite passes against the strict simulator on the 07_02 build; the pre-7.2 arm compiles clean against an 03_XX SDK. --- .github/workflows/se050-sim.yml | 12 ++++-- wolfcrypt/src/port/nxp/se050_port.c | 64 ++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index d43a83becf2..1f7cc4bd27f 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -102,8 +102,12 @@ jobs: cache-to: ${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.name == 'default') && 'type=registry,ref=ghcr.io/wolfssl/wolfssl-sim-cache:se050,mode=max' || '' }} - name: Run wolfCrypt tests against simulator - # SE050_SIM_STRICT_ECDH=1 makes the simulator enforce the applet 7.2 - # ECDH InObject contract: the Tag7 target must be pre-created as an - # HMACKey object of exactly the shared-secret size or the derive is - # refused with SW 0x6985, matching SE05x applet >= 7.2 hardware. + # SE050_SIM_STRICT_ECDH=1 enforces the applet 7.2 InObject contract + # (a Tag7 target must already exist as an exactly-sized HMACKey). + # The port now always uses the Tag7-less direct variant, so this is + # a regression guard: it fails the run if the InObject flow comes + # back. Note the SDK in the image is built for applet 07_02, so only + # the >= 7.2 side of the port's version split is compiled here; the + # pre-7.2 Binary derive flow is hardware-verified (SE050C, applet + # 3.1.1) until an 03_XX matrix leg is added. run: docker run --rm -e SE050_SIM_STRICT_ECDH=1 wolfssl-se050-sim:ci-${{ matrix.name }} diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 467290e46fa..d3dd1d8ea18 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2754,14 +2754,16 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, sss_key_store_t host_keystore; sss_object_t ref_private_key; sss_object_t ref_public_key; - sss_object_t deriveKey; - sss_derive_key_t ctx_derive_key; - word32 keyId = 0; int keySize; int keySizeBits; sss_cipher_type_t curveType; +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) + sss_object_t deriveKey; + sss_derive_key_t ctx_derive_key; + word32 keyId = 0; int keyCreated = 0; int deriveKeyCreated = 0; +#endif #ifdef SE050_DEBUG printf("se050_ecc_shared_secret: priv %p, pub %p, out %p (%d)\n", @@ -2801,6 +2803,7 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&ref_public_key, &host_keystore); } +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { keyId = public_key->keyId; if (public_key->keyIdSet == 0) { @@ -2845,7 +2848,6 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&deriveKey, &host_keystore); } -#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { word32 keyIdAes = se050_allocate_key(SE050_AES_KEY); status = sss_key_object_allocate_handle(&deriveKey, @@ -2882,7 +2884,19 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, sss_derive_key_context_free(&ctx_derive_key); } + if (deriveKeyCreated) { + sss_key_store_erase_key(&host_keystore, &deriveKey); + sss_key_object_free(&deriveKey); + } #else + /* The direct APDU carries the peer public point in the command, so + * the peer key is never uploaded to the SE050 on this path; a + * reference object is only needed when the peer public key is + * already SE050-resident. */ + if (status == kStatus_SSS_Success && public_key->keyIdSet != 0) { + status = sss_key_object_get_handle(&ref_public_key, + public_key->keyId); + } if (status == kStatus_SSS_Success) { /* Middleware built for applet >= 7.2 derives into an SE05x * resident object, but the applet refuses to export a symmetric @@ -2924,7 +2938,9 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, sm = Se05x_API_ECDHGenerateSharedSecret( &((sss_se05x_session_t*)cfg_se050_i2c_pi)->s_ctx, private_key->keyId, peerPoint, peerPointSz, out, &outSz); - if (sm == SM_OK) { + /* a NIST curve shared secret is always exactly keySize + * bytes; anything else indicates a malformed response */ + if (sm == SM_OK && outSz == (size_t)keySize) { *outlen = (word32)outSz; } else { @@ -2932,15 +2948,10 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, } } } - (void)ctx_derive_key; - (void)deriveKey; #endif - if (deriveKeyCreated) { - sss_key_store_erase_key(&host_keystore, &deriveKey); - sss_key_object_free(&deriveKey); - } if (status == kStatus_SSS_Success) { +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) #ifdef WOLFSSL_SE050_ONLY_KEY_ID if (keyCreated) { /* The peer's public key was uploaded for this derivation only. */ @@ -2953,13 +2964,16 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, public_key->keyId = keyId; public_key->keyIdSet = 1; } +#endif /* !SSS_HAVE_SE05X_VER_GTE_07_02 */ ret = 0; } else { +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (keyCreated) { sss_key_store_erase_key(&host_keystore, &ref_public_key); sss_key_object_free(&ref_public_key); } +#endif if (ret == 0) { ret = WC_HW_E; } @@ -3416,12 +3430,14 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, sss_key_store_t host_keystore; sss_object_t ref_private_key; sss_object_t ref_public_key; + int keySize = CURVE25519_KEYSIZE; +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) sss_object_t deriveKey; sss_derive_key_t ctx_derive_key; word32 keyId; - int keySize = CURVE25519_KEYSIZE; int keyCreated = 0; int deriveKeyCreated = 0; +#endif #ifdef SE050_DEBUG printf("se050_curve25519_shared_secret: priv %p, pub %p, out %p (%d)\n", @@ -3456,6 +3472,7 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&ref_public_key, &host_keystore); } +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { keyId = public_key->keyId; if (public_key->keyIdSet == 0) { @@ -3496,7 +3513,6 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, if (status == kStatus_SSS_Success) { status = sss_key_object_init(&deriveKey, &host_keystore); } -#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (status == kStatus_SSS_Success) { word32 keyIdAes = se050_allocate_key(SE050_AES_KEY); status = sss_key_object_allocate_handle(&deriveKey, @@ -3533,7 +3549,19 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, sss_derive_key_context_free(&ctx_derive_key); } + if (deriveKeyCreated) { + sss_key_store_erase_key(&host_keystore, &deriveKey); + sss_key_object_free(&deriveKey); + } #else + /* The direct APDU carries the peer public point in the command, so + * the peer key is never uploaded to the SE050 on this path; a + * reference object is only needed when the peer public key is + * already SE050-resident. */ + if (status == kStatus_SSS_Success && public_key->keyIdSet != 0) { + status = sss_key_object_get_handle(&ref_public_key, + public_key->keyId); + } if (status == kStatus_SSS_Success) { /* Middleware built for applet >= 7.2 derives into an SE05x * resident object, but the applet refuses to export a symmetric @@ -3600,15 +3628,10 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, } } } - (void)ctx_derive_key; - (void)deriveKey; #endif - if (deriveKeyCreated) { - sss_key_store_erase_key(&host_keystore, &deriveKey); - sss_key_object_free(&deriveKey); - } if (status == kStatus_SSS_Success) { +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) #ifdef WOLFSSL_SE050_ONLY_KEY_ID if (keyCreated) { /* The peer's public key was uploaded for this derivation only.*/ @@ -3621,13 +3644,16 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, public_key->keyId = keyId; public_key->keyIdSet = 1; } +#endif /* !SSS_HAVE_SE05X_VER_GTE_07_02 */ ret = 0; } else { +#if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) if (keyCreated) { sss_key_store_erase_key(&host_keystore, &ref_public_key); sss_key_object_free(&ref_public_key); } +#endif if (ret == 0) ret = WC_HW_E; } From 5d20302d9331b9bf7e27f7b0d5d42fdaa79697b6 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Tue, 4 Aug 2026 15:39:38 +0100 Subject: [PATCH 16/16] SE050: scope curve25519 keySize to the pre-7.2 arm The applet >= 7.2 arm of se050_curve25519_shared_secret uses CURVE25519_KEYSIZE directly, leaving keySize unused there and failing -Werror maintainer builds with unused-variable. --- wolfcrypt/src/port/nxp/se050_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index d3dd1d8ea18..556e2d2b4a7 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -3430,8 +3430,8 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, sss_key_store_t host_keystore; sss_object_t ref_private_key; sss_object_t ref_public_key; - int keySize = CURVE25519_KEYSIZE; #if !(defined(SSS_HAVE_SE05X_VER_GTE_07_02) && SSS_HAVE_SE05X_VER_GTE_07_02) + int keySize = CURVE25519_KEYSIZE; sss_object_t deriveKey; sss_derive_key_t ctx_derive_key; word32 keyId;