From 6d2845751b98082207a27be02be59df8578a286c Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sun, 12 Apr 2026 07:51:58 +0100 Subject: [PATCH 01/11] Fix SE050 Ed25519 port bugs and add simulator CI workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - se050_ed25519_verify_msg: initialize *res = 0 at entry so failures don't leak a stale res = 1 from a prior good verify. - Ed25519 import functions: reset keyIdSet / keyId under WOLFSSL_SE050 in wc_ed25519_import_private_key_ex, wc_ed25519_import_private_only, wc_ed25519_import_public_ex so overwriting host-side key material invalidates any prior SE050 object binding. - New workflow .github/workflows/se050-sim.yml: builds wolfSSL against the NXP Plug&Trust SDK and runs the wolfCrypt tests against the SE050Sim simulator. Patches the upstream Dockerfile to use the PR's wolfSSL source. - ed25519_test SE050 adjustments: - Cap the RFC 8032 loop at 5 iters — iter 5's 1023 B msg exceeds NXP SDK SE05X_TLV_BUF_SIZE_CMD = 900. - rareEd verifies and private-only sign: expect WC_HW_E (SE050 delegates malformed-input rejection to the secure element) instead of BAD_FUNC_ARG / SIG_VERIFY_E. - Skip ed25519ctx_test / ed25519ph_test — SE050 port drops the context/prehash params so RFC 8032 ctx/ph vectors can't byte-match. --- .github/workflows/se050-sim.yml | 70 +++++++++++++++++++++++++++++ wolfcrypt/src/ed25519.c | 18 ++++++++ wolfcrypt/src/port/nxp/se050_port.c | 2 + wolfcrypt/test/test.c | 50 ++++++++++++++++++--- 4 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/se050-sim.yml diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml new file mode 100644 index 00000000000..2e2c0cbb409 --- /dev/null +++ b/.github/workflows/se050-sim.yml @@ -0,0 +1,70 @@ +name: SE050 simulator test + +# START OF COMMON SECTION +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +# END OF COMMON SECTION + +# Build the SE050 software simulator (https://github.com/LinuxJedi/SE050Sim), +# build wolfSSL against its NXP Plug&Trust SDK + simulator bridge, and run the +# wolfCrypt SE050 test binary against the simulator TCP server. +# +# The simulator's own Dockerfile (Dockerfile.wolfcrypt) clones wolfSSL master. +# We patch it to COPY the PR checkout instead so CI reflects the PR's source. + +env: + # Pin the simulator to a known-good revision. Bump this deliberately after + # validating upstream changes in a standalone PR. + SE050SIM_REF: main + +jobs: + se050_sim: + name: wolfCrypt against SE050 simulator + if: github.repository_owner == 'wolfssl' + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Checkout wolfSSL (PR source) + uses: actions/checkout@v4 + with: + path: wolfssl-src + + - name: Clone SE050 simulator + run: | + git clone https://github.com/LinuxJedi/SE050Sim se050sim + cd se050sim && git checkout "$SE050SIM_REF" + + - name: Stage PR wolfSSL into simulator build context + run: mv wolfssl-src se050sim/wolfssl + + - name: Patch Dockerfile to use PR wolfSSL instead of upstream master + working-directory: se050sim + run: | + sed -i 's|^RUN git clone --depth 1 https://github.com/wolfSSL/wolfssl.git /app/wolfssl$|COPY wolfssl /app/wolfssl|' Dockerfile.wolfcrypt + # Fail fast if the pattern drifted upstream — better a clear error + # than a CI run that silently tests master. + grep -q '^COPY wolfssl /app/wolfssl$' Dockerfile.wolfcrypt + ! grep -q 'git clone .*wolfssl\.git' Dockerfile.wolfcrypt + + - uses: docker/setup-buildx-action@v3 + + - name: Build wolfCrypt-SE050 test image + uses: docker/build-push-action@v5 + with: + context: se050sim + file: se050sim/Dockerfile.wolfcrypt + push: false + load: true + tags: wolfssl-se050-sim:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Run wolfCrypt tests against simulator + run: docker run --rm wolfssl-se050-sim:ci diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 1bf12f7b24d..6150242806a 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -1169,6 +1169,12 @@ int wc_ed25519_import_public_ex(const byte* in, word32 inLen, ed25519_key* key, if (inLen < ED25519_PUB_KEY_SIZE) return BAD_FUNC_ARG; +#ifdef WOLFSSL_SE050 + /* Importing new key material invalidates any prior SE050 object binding. */ + key->keyIdSet = 0; + key->keyId = 0; +#endif + /* compressed prefix according to draft http://www.ietf.org/id/draft-koch-eddsa-for-openpgp-02.txt */ if (in[0] == 0x40 && inLen == ED25519_PUB_KEY_SIZE + 1) { @@ -1255,6 +1261,12 @@ int wc_ed25519_import_private_only(const byte* priv, word32 privSz, if (privSz != ED25519_KEY_SIZE) return BAD_FUNC_ARG; +#ifdef WOLFSSL_SE050 + /* Importing new key material invalidates any prior SE050 object binding. */ + key->keyIdSet = 0; + key->keyId = 0; +#endif + XMEMCPY(key->k, priv, ED25519_KEY_SIZE); key->privKeySet = 1; @@ -1311,6 +1323,12 @@ int wc_ed25519_import_private_key_ex(const byte* priv, word32 privSz, return BAD_FUNC_ARG; } +#ifdef WOLFSSL_SE050 + /* Importing new key material invalidates any prior SE050 object binding. */ + key->keyIdSet = 0; + key->keyId = 0; +#endif + XMEMCPY(key->k, priv, ED25519_KEY_SIZE); key->privKeySet = 1; diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index e476874b4e1..987fdb53929 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -3039,6 +3039,8 @@ int se050_ed25519_verify_msg(const byte* signature, word32 signatureLen, key, signature, signatureLen, msg, msgLen); #endif + *res = 0; + if (cfg_se050_i2c_pi == NULL) { return WC_HW_E; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5f846fcc7e8..debcb1b031b 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41340,7 +41340,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) #if defined(HAVE_ED25519_SIGN) && defined(HAVE_ED25519_KEY_EXPORT) && \ defined(HAVE_ED25519_KEY_IMPORT) +#ifdef WOLFSSL_SE050 + /* Iter 5 uses RFC 8032 msg4 (~1023 bytes), which exceeds the NXP + * Plug&Trust SDK's SE05X_TLV_BUF_SIZE_CMD = 900 byte APDU buffer: + * EdDSASign fails with "Not enough buffer" before the command reaches + * the secure element. Cap at 5 iterations until the SDK buffer is + * enlarged upstream. */ + for (i = 0; i < 5; i++) { +#else for (i = 0; i < 6; i++) { +#endif outlen = sizeof(out); XMEMSET(out, 0, sizeof(out)); @@ -41413,7 +41422,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) #endif /* HAVE_ED25519_VERIFY */ } -#ifdef HAVE_ED25519_VERIFY +#if defined(HAVE_ED25519_VERIFY) + /* These cases exercise host-side signature-encoding pre-validation (e.g., + * sig == curve order). The SE050 port delegates verify to the secure + * element, which rejects all four inputs with WC_HW_E rather than the + * BAD_FUNC_ARG / SIG_VERIFY_E the host-side path produces — so the + * expected error code differs below when built against an SE050. */ { /* Run tests for some rare code paths */ /* sig is exactly equal to the order */ @@ -41466,28 +41480,45 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); +#ifdef WOLFSSL_SE050 + #define RARE_ED_BAD_ENC_E WC_HW_E + #define RARE_ED_BAD_SIG_E WC_HW_E +#else + #define RARE_ED_BAD_ENC_E BAD_FUNC_ARG + #define RARE_ED_BAD_SIG_E SIG_VERIFY_E +#endif + ret = wc_ed25519_verify_msg(rareEd1, sizeof(rareEd1), msgs[0], msgSz[0], &verify, key); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + if (ret != WC_NO_ERR_TRACE(RARE_ED_BAD_ENC_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); ret = wc_ed25519_verify_msg(rareEd2, sizeof(rareEd2), msgs[0], msgSz[0], &verify, key); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + if (ret != WC_NO_ERR_TRACE(RARE_ED_BAD_ENC_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); ret = wc_ed25519_verify_msg(rareEd3, sizeof(rareEd3), msgs[0], msgSz[0], &verify, key); - if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + if (ret != WC_NO_ERR_TRACE(RARE_ED_BAD_ENC_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); ret = wc_ed25519_verify_msg(rareEd4, sizeof(rareEd4), msgs[0], msgSz[0], &verify, key); - if (ret != WC_NO_ERR_TRACE(SIG_VERIFY_E)) + if (ret != WC_NO_ERR_TRACE(RARE_ED_BAD_SIG_E)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); + + #undef RARE_ED_BAD_ENC_E + #undef RARE_ED_BAD_SIG_E } #endif /* HAVE_ED25519_VERIFY */ +#ifndef WOLFSSL_SE050 + /* Ed25519ctx and Ed25519ph require passing a context / prehash flag + * through to the signer. The SE050 port's se050_ed25519_sign_msg / + * _verify_msg drop those parameters and always do plain Ed25519, so the + * RFC 8032 ctx/ph test vectors cannot match. Skip these variants when + * built against an SE050. */ ret = ed25519ctx_test(); if (ret != 0) goto cleanup; @@ -41495,6 +41526,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) ret = ed25519ph_test(); if (ret != 0) goto cleanup; +#endif /* !WOLFSSL_SE050 */ #ifndef NO_ASN /* Try ASN.1 encoded private-only key and public key. */ @@ -41509,8 +41541,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) sizeof(badPrivateEd25519)) == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, cleanup); + /* Signing with a private-only key (no public loaded yet) is rejected on + * the host with BAD_FUNC_ARG. The SE050 port instead fails inside + * sss_se05x_key_store_set_ecc_keypair and returns WC_HW_E, so accept + * that alternate error code when built against an SE050. */ ret = wc_ed25519_sign_msg(msgs[0], msgSz[0], out, &outlen, key3); +#ifdef WOLFSSL_SE050 + if (ret != WC_NO_ERR_TRACE(WC_HW_E)) +#else if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) +#endif ERROR_OUT(WC_TEST_RET_ENC_EC(ret), cleanup); /* try with a buffer size that is too large */ From f08d7ba950316e98de408b34cadd5f3bc1b03664 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Sun, 12 Apr 2026 10:50:02 +0100 Subject: [PATCH 02/11] Fix SE050 RSA port bugs - se050_rsa_verify: when the function uploads only the public part of the key (keyCreated == 1), erase the transient SE050 object and don't persist keyIdSet = 1. A subsequent sign on the same RsaKey was reusing the public-only SE050 object and failing. Pre-existing bindings (from wc_RsaUseKeyId or a prior sign that uploaded a keypair) are preserved untouched. - rsa_keygen_test: add WOLFSSL_SE050 to the existing WOLFSSL_CRYPTOCELL guard around the export-then-decode round-trip. SE050-generated keys keep their private components in the secure element, so wc_RsaKeyToDer + wc_RsaPrivateKeyDecode cannot complete. Matching guard on the idx declaration to avoid an unused-variable warning. --- wolfcrypt/src/port/nxp/se050_port.c | 18 ++++++++++++++++-- wolfcrypt/test/test.c | 11 +++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 987fdb53929..ea951452fde 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1538,8 +1538,22 @@ int se050_rsa_verify(const byte* in, word32 inLen, byte* out, word32 outLen, } if (status == kStatus_SSS_Success) { - key->keyId = keyId; - key->keyIdSet = 1; + if (keyCreated) { + /* We uploaded only the public part of the key for this verify. + * Don't persist keyIdSet=1 — a later sign on the same RsaKey + * would reuse this binding and fail because the SE050 object has + * no private material. Erase the transient object so the next + * SE050 op (sign or verify) re-uploads from whatever the host + * RsaKey currently holds. */ + sss_key_store_erase_key(&host_keystore, &newKey); + sss_key_object_free(&newKey); + } + else { + /* Pre-existing keyIdSet=1 binding (e.g. wc_RsaUseKeyId or prior + * sign that uploaded a keypair). Preserve it. */ + key->keyId = keyId; + key->keyIdSet = 1; + } } else { if (keyCreated) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index debcb1b031b..8d9ebd5924f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -25357,7 +25357,7 @@ static wc_test_ret_t rsa_keygen_test(WC_RNG* rng) #else byte der[1280]; #endif -#ifndef WOLFSSL_CRYPTOCELL +#if !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SE050) word32 idx = 0; #endif int derSz = 0; @@ -25435,13 +25435,16 @@ static wc_test_ret_t rsa_keygen_test(WC_RNG* rng) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa); -#ifndef WOLFSSL_CRYPTOCELL +#if !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SE050) idx = 0; - /* The private key part of the key gen pairs from cryptocell can't be exported */ + /* The private key part of key pairs generated inside a secure element + * (CryptoCell, SE050) stays in hardware and isn't available to + * wc_RsaKeyToDer, so the exported DER can't be parsed back as a + * complete RSAPrivateKey. */ ret = wc_RsaPrivateKeyDecode(der, &idx, genKey, (word32)derSz); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa); -#endif /* WOLFSSL_CRYPTOCELL */ +#endif /* WOLFSSL_CRYPTOCELL && !WOLFSSL_SE050 */ #endif /* !WC_TEST_SKIP_RSA_PRIVATE_EXPORT */ exit_rsa: From 931893c62b5b93631c0432d7467f6b751eae017d Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Mon, 13 Apr 2026 06:33:48 +0100 Subject: [PATCH 03/11] Address items in code review --- .github/workflows/se050-sim.yml | 4 +--- wolfcrypt/src/ed25519.c | 33 +++++++++++++++++++++++------ wolfcrypt/src/port/nxp/se050_port.c | 25 ++++++++++++++++++---- wolfcrypt/test/test.c | 2 +- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 2e2c0cbb409..0f7cb3de493 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -20,8 +20,6 @@ concurrency: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - # Pin the simulator to a known-good revision. Bump this deliberately after - # validating upstream changes in a standalone PR. SE050SIM_REF: main jobs: @@ -48,7 +46,7 @@ jobs: working-directory: se050sim run: | sed -i 's|^RUN git clone --depth 1 https://github.com/wolfSSL/wolfssl.git /app/wolfssl$|COPY wolfssl /app/wolfssl|' Dockerfile.wolfcrypt - # Fail fast if the pattern drifted upstream — better a clear error + # Fail fast if the pattern drifted upstream -- better a clear error # than a CI run that silently tests master. grep -q '^COPY wolfssl /app/wolfssl$' Dockerfile.wolfcrypt ! grep -q 'git clone .*wolfssl\.git' Dockerfile.wolfcrypt diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 6150242806a..8a78f2d4805 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -1170,9 +1170,15 @@ int wc_ed25519_import_public_ex(const byte* in, word32 inLen, ed25519_key* key, return BAD_FUNC_ARG; #ifdef WOLFSSL_SE050 - /* Importing new key material invalidates any prior SE050 object binding. */ - key->keyIdSet = 0; + /* Importing new key material invalidates any prior SE050 object binding; + * erase the old object (no-op when keyIdSet == 0) so the host and the + * secure element agree on what's bound. Clear the binding fields + * explicitly afterwards so a stale keyId never survives, even when + * se050_ed25519_free_key() returns early because the SE050 session isn't + * configured yet. */ + se050_ed25519_free_key(key); key->keyId = 0; + key->keyIdSet = 0; #endif /* compressed prefix according to draft @@ -1262,9 +1268,15 @@ int wc_ed25519_import_private_only(const byte* priv, word32 privSz, return BAD_FUNC_ARG; #ifdef WOLFSSL_SE050 - /* Importing new key material invalidates any prior SE050 object binding. */ - key->keyIdSet = 0; + /* Importing new key material invalidates any prior SE050 object binding; + * erase the old object (no-op when keyIdSet == 0) so the host and the + * secure element agree on what's bound. Clear the binding fields + * explicitly afterwards so a stale keyId never survives, even when + * se050_ed25519_free_key() returns early because the SE050 session isn't + * configured yet. */ + se050_ed25519_free_key(key); key->keyId = 0; + key->keyIdSet = 0; #endif XMEMCPY(key->k, priv, ED25519_KEY_SIZE); @@ -1324,9 +1336,18 @@ int wc_ed25519_import_private_key_ex(const byte* priv, word32 privSz, } #ifdef WOLFSSL_SE050 - /* Importing new key material invalidates any prior SE050 object binding. */ - key->keyIdSet = 0; + /* Importing new key material invalidates any prior SE050 object binding; + * erase the old object (no-op when keyIdSet == 0) so the host and the + * secure element agree on what's bound. key->k is overwritten before the + * wc_ed25519_import_public_ex() call below, so the binding must be + * dropped here first in case that function fails its own early-return + * argument checks before reaching its reset. Clear the binding fields + * explicitly afterwards so a stale keyId never survives, even when + * se050_ed25519_free_key() returns early because the SE050 session isn't + * configured yet. */ + se050_ed25519_free_key(key); key->keyId = 0; + key->keyIdSet = 0; #endif XMEMCPY(key->k, priv, ED25519_KEY_SIZE); diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index ea951452fde..1bf08224f21 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1483,7 +1483,7 @@ int se050_rsa_verify(const byte* in, word32 inLen, byte* out, word32 outLen, keyId = se050_allocate_key(SE050_RSA_KEY); status = sss_key_object_allocate_handle(&newKey, keyId, kSSS_KeyPart_Public, kSSS_CipherType_RSA, keySz, - kKeyObject_Mode_Persistent); + kKeyObject_Mode_Transient); } if (status == kStatus_SSS_Success) { /* Try to delete existing key first, ignore return since will @@ -1540,7 +1540,7 @@ int se050_rsa_verify(const byte* in, word32 inLen, byte* out, word32 outLen, if (status == kStatus_SSS_Success) { if (keyCreated) { /* We uploaded only the public part of the key for this verify. - * Don't persist keyIdSet=1 — a later sign on the same RsaKey + * Don't persist keyIdSet=1 -- a later sign on the same RsaKey * would reuse this binding and fail because the SE050 object has * no private material. Erase the transient object so the next * SE050 op (sign or verify) re-uploads from whatever the host @@ -3053,6 +3053,10 @@ int se050_ed25519_verify_msg(const byte* signature, word32 signatureLen, key, signature, signatureLen, msg, msgLen); #endif + if (signature == NULL || msg == NULL || key == NULL || res == NULL) { + return BAD_FUNC_ARG; + } + *res = 0; if (cfg_se050_i2c_pi == NULL) { @@ -3115,8 +3119,21 @@ int se050_ed25519_verify_msg(const byte* signature, word32 signatureLen, } if (status == kStatus_SSS_Success) { - key->keyId = keyId; - key->keyIdSet = 1; + if (keyCreated) { + /* We uploaded only the public part of the key for this verify. + * Don't persist keyIdSet=1 -- a later sign on the same ed25519_key + * would reuse this binding and fail because the SE050 object has + * no private material. Erase the transient object so the next + * SE050 op re-uploads. Mirrors the fix in se050_rsa_verify. */ + sss_key_store_erase_key(&host_keystore, &newKey); + sss_key_object_free(&newKey); + } + else { + /* Pre-existing keyIdSet=1 binding (from prior sign that uploaded + * a keypair, or explicit caller setup). Preserve it. */ + key->keyId = keyId; + key->keyIdSet = 1; + } *res = 1; ret = 0; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 8d9ebd5924f..2ad81370317 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -41429,7 +41429,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed25519_test(void) /* These cases exercise host-side signature-encoding pre-validation (e.g., * sig == curve order). The SE050 port delegates verify to the secure * element, which rejects all four inputs with WC_HW_E rather than the - * BAD_FUNC_ARG / SIG_VERIFY_E the host-side path produces — so the + * BAD_FUNC_ARG / SIG_VERIFY_E the host-side path produces -- so the * expected error code differs below when built against an SE050. */ { /* Run tests for some rare code paths */ From fff6f3fc328e6e6fc7b78d252f742c5c65c57eee Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 13 Apr 2026 19:15:25 +0200 Subject: [PATCH 04/11] se050: fix RSA Encrypt with only pubkey setting keyId --- wolfcrypt/src/port/nxp/se050_port.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 1bf08224f21..598e2e25867 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -1710,8 +1710,17 @@ int se050_rsa_public_encrypt(const byte* in, word32 inLen, byte* out, } if (status == kStatus_SSS_Success) { - key->keyId = keyId; - key->keyIdSet = 1; + if (keyCreated) { + /* Public-key encrypt imported a temporary public object only. + * Do not bind that SE050 object to the caller's RsaKey or later + * private-key operations will try to reuse a public handle. */ + sss_key_store_erase_key(&host_keystore, &newKey); + sss_key_object_free(&newKey); + } + else { + key->keyId = keyId; + key->keyIdSet = 1; + } ret = encSz; } else { From ed2e230f7843241d6d257e35321b56984cc5acf2 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 13 Apr 2026 19:15:57 +0200 Subject: [PATCH 05/11] se050: initialize keyId to abide compilers --- wolfcrypt/src/port/nxp/se050_port.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 598e2e25867..9ec92f53df9 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2600,7 +2600,7 @@ int se050_ecc_create_key(struct ecc_key* key, int curve_id, int keySize) sss_key_store_t host_keystore; uint8_t derBuf[SE050_ECC_DER_MAX]; size_t derSz = sizeof(derBuf); - word32 keyId; + word32 keyId = 0; int keySizeBits; sss_cipher_type_t curveType; int keyCreated = 0; @@ -2694,7 +2694,7 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, sss_object_t ref_public_key; sss_object_t deriveKey; sss_derive_key_t ctx_derive_key; - word32 keyId; + word32 keyId = 0; int keySize; int keySizeBits; sss_cipher_type_t curveType; From e6a7be8969e73e1694c7fb8ed16427eb16aa1eab Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 13 Apr 2026 19:32:53 +0200 Subject: [PATCH 06/11] test: rsa: reset RSA key between operation --- wolfcrypt/test/test.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 2ad81370317..697f0fc5bd2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -26009,6 +26009,21 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rsa_test(void) #if !defined(WOLFSSL_RSA_VERIFY_ONLY) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \ !defined(WC_NO_RNG) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) + /* Reload the key so the public-encrypt below is the first operation + * against it. Exercises backends that distinguish public-only material + * from full-keypair bindings: a public-encrypt on a freshly-loaded key + * must not prevent the subsequent private-decrypt from using the private + * key material the caller originally provided. */ +#ifndef NO_ASN + wc_FreeRsaKey(key); + ret = wc_InitRsaKey_ex(key, HEAP_HINT, devId); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa); + idx = 0; + ret = wc_RsaPrivateKeyDecode(tmp, &idx, key, (word32)bytes); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_rsa); +#endif do { #if defined(WOLFSSL_ASYNC_CRYPT) ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN); From 0fbfbb8089ed6f662c21f3fd38f6a32e09348c27 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 14 Apr 2026 16:34:21 +0200 Subject: [PATCH 07/11] se050: refuse invalid digest sz in ECDSA --- wolfcrypt/src/port/nxp/se050_port.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 9ec92f53df9..4439ee48256 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -2146,11 +2146,8 @@ int se050_ecc_sign_hash_ex(const byte* in, word32 inLen, MATH_INT_T* r, MATH_INT algorithm = se050_map_hash_alg(inLen); if (algorithm == kAlgorithm_None) { - inLen = keySize; /* try key size */ - algorithm = se050_map_hash_alg(inLen); - } - if (algorithm == kAlgorithm_None) { - return ECC_CURVE_OID_E; + WOLFSSL_MSG("SE050 ECDSA sign only supports SHA-1/224/256/384/512 digest sizes"); + return BAD_LENGTH_E; } if (wolfSSL_CryptHwMutexLock() != 0) { @@ -2317,11 +2314,8 @@ int se050_ecc_verify_hash_ex(const byte* hash, word32 hashLen, MATH_INT_T* r, algorithm = se050_map_hash_alg(hashLen); if (algorithm == kAlgorithm_None) { - hashLen = keySize; /* try key size */ - algorithm = se050_map_hash_alg(hashLen); - } - if (algorithm == kAlgorithm_None) { - return ECC_CURVE_OID_E; + WOLFSSL_MSG("SE050 ECDSA verify only supports SHA-1/224/256/384/512 digest sizes"); + return BAD_LENGTH_E; } if (wolfSSL_CryptHwMutexLock() != 0) { From 5cd2db0b4d98abc9da208d2a09bfd79f0c60d989 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 14 Apr 2026 16:52:11 +0200 Subject: [PATCH 08/11] se050: skip ecc_test_buffers (invalid digest sz) --- wolfcrypt/test/test.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 697f0fc5bd2..371a3eede9a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -928,8 +928,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void); #if defined(USE_CERT_BUFFERS_256) && !defined(WOLFSSL_ATECC508A) && \ !defined(WOLFSSL_ATECC608A) && !defined(NO_ECC256) && \ defined(HAVE_ECC_VERIFY) && defined(HAVE_ECC_SIGN) && \ - !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) - /* skip for ATECC508/608A, cannot import private key buffers */ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) && \ + !defined(WOLFSSL_SE050) + /* skip for ATECC508/608A (cannot import private key buffers) and + * SE050 (test vector uses a digest size SE050 does not accept) */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test_buffers(void); #endif #endif @@ -3033,8 +3035,10 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ #if defined(USE_CERT_BUFFERS_256) && !defined(WOLFSSL_ATECC508A) && \ !defined(WOLFSSL_ATECC608A) && !defined(NO_ECC256) && \ defined(HAVE_ECC_VERIFY) && defined(HAVE_ECC_SIGN) && \ - !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) - /* skip for ATECC508/608A, cannot import private key buffers */ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) && \ + !defined(WOLFSSL_SE050) + /* skip for ATECC508/608A (cannot import private key buffers) and + * SE050 (test vector uses a digest size SE050 does not accept) */ if ( (ret = ecc_test_buffers()) != 0) TEST_FAIL("ECC buffer test failed!\n", ret); else @@ -39256,7 +39260,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_encrypt_test(void) #if defined(USE_CERT_BUFFERS_256) && !defined(WOLFSSL_ATECC508A) && \ !defined(WOLFSSL_ATECC608A) && !defined(NO_ECC256) && \ defined(HAVE_ECC_VERIFY) && defined(HAVE_ECC_SIGN) && \ - !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(NO_ECC_SECP) && \ + !defined(WOLFSSL_SE050) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test_buffers(void) { size_t bytes; From f55ecb51242664afdb691c00a39e04c4d8adf353 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 3 Mar 2026 11:30:38 +0100 Subject: [PATCH 09/11] se050: add support for SE050 SIGN ONLY options --- .wolfssl_known_macro_extras | 3 +++ wolfcrypt/src/ecc.c | 9 ++++++--- wolfcrypt/src/port/nxp/README_SE050.md | 19 +++++++++++++++++++ wolfcrypt/src/rsa.c | 2 ++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 0e58d4f11b0..86fe9a36de8 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -880,7 +880,10 @@ WOLFSSL_SE050_AUTO_ERASE WOLFSSL_SE050_CRYPT WOLFSSL_SE050_HASH WOLFSSL_SE050_INIT +WOLFSSL_SE050_NOECDHE +WOLFSSL_SE050_NO_ECDSA_VERIFY WOLFSSL_SE050_NO_RSA +WOLFSSL_SE050_NO_RSA_VERIFY WOLFSSL_SE050_NO_TRNG WOLFSSL_SECURE_RENEGOTIATION_ON_BY_DEFAULT WOLFSSL_SERVER_EXAMPLE diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 2da1a22e6bf..82ecbab6b2c 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -291,6 +291,9 @@ ECC Curve Sizes: #undef HAVE_ECC_VERIFY_HELPER #define HAVE_ECC_VERIFY_HELPER #endif +#if defined(WOLFSSL_SE050_NO_ECDSA_VERIFY) + #define HAVE_ECC_VERIFY_HELPER +#endif #if !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \ !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \ @@ -4767,7 +4770,7 @@ int wc_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, err = silabs_ecc_shared_secret(private_key, public_key, out, outlen); #elif defined(WOLFSSL_KCAPI_ECC) err = KcapiEcc_SharedSecret(private_key, public_key, out, outlen); -#elif defined(WOLFSSL_SE050) +#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NOECDHE) err = se050_ecc_shared_secret(private_key, public_key, out, outlen); #else err = wc_ecc_shared_secret_ex(private_key, &public_key->pubkey, out, outlen); @@ -5761,7 +5764,7 @@ static int _ecc_make_key_ex(WC_RNG* rng, int keysize, ecc_key* key, else { err = NOT_COMPILED_IN; } -#elif defined(WOLFSSL_SE050) +#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NOECDHE) err = se050_ecc_create_key(key, key->dp->id, key->dp->size); key->type = ECC_PRIVATEKEY; #elif defined(WOLFSSL_CRYPTOCELL) @@ -9261,7 +9264,7 @@ int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash, #elif defined(WOLFSSL_XILINX_CRYPT_VERSAL) byte sigRS[ECC_MAX_CRYPTO_HW_SIZE * 2]; byte hashcopy[ECC_MAX_CRYPTO_HW_SIZE] = {0}; -#elif defined(WOLFSSL_SE050) +#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_ECDSA_VERIFY) #else int curveLoaded = 0; DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT); diff --git a/wolfcrypt/src/port/nxp/README_SE050.md b/wolfcrypt/src/port/nxp/README_SE050.md index ee94f5a499c..dcbba50f39e 100644 --- a/wolfcrypt/src/port/nxp/README_SE050.md +++ b/wolfcrypt/src/port/nxp/README_SE050.md @@ -246,6 +246,25 @@ 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_NOECDHE`** + +Disables offloading ECDH key generation and shared secret operations to the +SE050. When defined, `wc_ecc_make_key()` and `wc_ecc_shared_secret()` will +use wolfCrypt software instead of the SE050. + +**`WOLFSSL_SE050_NO_ECDSA_VERIFY`** + +When defined, ECDSA signing (`wc_ecc_sign_hash()`) continues to be offloaded +to the SE050, but ECDSA verification (`wc_ecc_verify_hash()`) uses wolfCrypt +software. + +**`WOLFSSL_SE050_NO_RSA_VERIFY`** + +When defined, RSA PKCS#1 v1.5 signing (`wc_RsaSSL_Sign()`) continues to be +offloaded to the SE050, but RSA PKCS#1 v1.5 verification (`wc_RsaSSL_Verify()`) +uses wolfCrypt software (public-key exponentiation + unpad). RSA PSS verify and +RSA key-exchange decrypt are unaffected. + ## wolfSSL HostCrypto Support The NXP SE05x Plug & Trust Middleware by default can use either OpenSSL or diff --git a/wolfcrypt/src/rsa.c b/wolfcrypt/src/rsa.c index 4bd5062596a..0def9792002 100644 --- a/wolfcrypt/src/rsa.c +++ b/wolfcrypt/src/rsa.c @@ -3660,6 +3660,7 @@ static int RsaPrivateDecryptEx(const byte* in, word32 inLen, byte* out, } return ret; } + #if !defined(WOLFSSL_SE050_NO_RSA_VERIFY) else if (rsa_type == RSA_PUBLIC_DECRYPT && pad_value == RSA_BLOCK_TYPE_1 && pad_type != WC_RSA_PSS_PAD) { @@ -3675,6 +3676,7 @@ static int RsaPrivateDecryptEx(const byte* in, word32 inLen, byte* out, } return ret; } + #endif /* !WOLFSSL_SE050_NO_RSA_VERIFY */ #endif /* RSA CRYPTO HW */ From 2f5df5c9f8c9834eae49ebdde8f204f4cff8423f Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 15 Apr 2026 08:33:30 +0200 Subject: [PATCH 10/11] se050: abide compilers warning about init keyId --- 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 4439ee48256..ab87034e7a2 100644 --- a/wolfcrypt/src/port/nxp/se050_port.c +++ b/wolfcrypt/src/port/nxp/se050_port.c @@ -810,7 +810,7 @@ int se050_rsa_get_key_id(struct RsaKey* key, word32* keyId) int se050_rsa_create_key(struct RsaKey* key, int size, long e) { int ret = 0; - word32 keyId; + word32 keyId = 0; int keyCreated = 0; sss_status_t status = kStatus_SSS_Success; sss_object_t keyPair; From 8b01033d0b0d2d241b6d64d59f23a0e64f9a3430 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 15 Apr 2026 08:36:07 +0200 Subject: [PATCH 11/11] se050: reviewer's fixes --- .github/workflows/se050-sim.yml | 2 +- .wolfssl_known_macro_extras | 2 +- wolfcrypt/src/ecc.c | 6 +++--- wolfcrypt/src/port/nxp/README_SE050.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 0f7cb3de493..08f94b93d93 100644 --- a/.github/workflows/se050-sim.yml +++ b/.github/workflows/se050-sim.yml @@ -20,7 +20,7 @@ concurrency: # We patch it to COPY the PR checkout instead so CI reflects the PR's source. env: - SE050SIM_REF: main + SE050SIM_REF: 8fda9212c306fbee0dcd66f2dd52b13f65f13e00 jobs: se050_sim: diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 86fe9a36de8..9ece8e072e7 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -880,7 +880,7 @@ WOLFSSL_SE050_AUTO_ERASE WOLFSSL_SE050_CRYPT WOLFSSL_SE050_HASH WOLFSSL_SE050_INIT -WOLFSSL_SE050_NOECDHE +WOLFSSL_SE050_NO_ECDHE WOLFSSL_SE050_NO_ECDSA_VERIFY WOLFSSL_SE050_NO_RSA WOLFSSL_SE050_NO_RSA_VERIFY diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 82ecbab6b2c..c622a8b434c 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -291,7 +291,7 @@ ECC Curve Sizes: #undef HAVE_ECC_VERIFY_HELPER #define HAVE_ECC_VERIFY_HELPER #endif -#if defined(WOLFSSL_SE050_NO_ECDSA_VERIFY) +#if defined(WOLFSSL_SE050_NO_ECDSA_VERIFY) && defined(HAVE_ECC_VERIFY) #define HAVE_ECC_VERIFY_HELPER #endif @@ -4770,7 +4770,7 @@ int wc_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, err = silabs_ecc_shared_secret(private_key, public_key, out, outlen); #elif defined(WOLFSSL_KCAPI_ECC) err = KcapiEcc_SharedSecret(private_key, public_key, out, outlen); -#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NOECDHE) +#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_ECDHE) err = se050_ecc_shared_secret(private_key, public_key, out, outlen); #else err = wc_ecc_shared_secret_ex(private_key, &public_key->pubkey, out, outlen); @@ -5764,7 +5764,7 @@ static int _ecc_make_key_ex(WC_RNG* rng, int keysize, ecc_key* key, else { err = NOT_COMPILED_IN; } -#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NOECDHE) +#elif defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_ECDHE) err = se050_ecc_create_key(key, key->dp->id, key->dp->size); key->type = ECC_PRIVATEKEY; #elif defined(WOLFSSL_CRYPTOCELL) diff --git a/wolfcrypt/src/port/nxp/README_SE050.md b/wolfcrypt/src/port/nxp/README_SE050.md index dcbba50f39e..ae10c916152 100644 --- a/wolfcrypt/src/port/nxp/README_SE050.md +++ b/wolfcrypt/src/port/nxp/README_SE050.md @@ -246,7 +246,7 @@ 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_NOECDHE`** +**`WOLFSSL_SE050_NO_ECDHE`** Disables offloading ECDH key generation and shared secret operations to the SE050. When defined, `wc_ecc_make_key()` and `wc_ecc_shared_secret()` will