Fix SE050 Ed25519 port bugs and add simulator CI workflow

- 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.
This commit is contained in:
Andrew Hutchings
2026-04-12 07:51:58 +01:00
committed by Marco Oliverio
parent 9176185d66
commit 6d2845751b
4 changed files with 135 additions and 5 deletions
+70
View File
@@ -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
+18
View File
@@ -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;
+2
View File
@@ -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;
}
+45 -5
View File
@@ -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 */