Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/se050-sim.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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:
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"

Comment thread
LinuxJedi marked this conversation as resolved.
- 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
39 changes: 39 additions & 0 deletions wolfcrypt/src/ed25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,18 @@ 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;
* 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);
Comment thread
LinuxJedi marked this conversation as resolved.
key->keyId = 0;
key->keyIdSet = 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) {
Expand Down Expand Up @@ -1255,6 +1267,18 @@ 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;
* 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);
Comment thread
LinuxJedi marked this conversation as resolved.
key->keyId = 0;
key->keyIdSet = 0;
#endif

XMEMCPY(key->k, priv, ED25519_KEY_SIZE);
key->privKeySet = 1;

Expand Down Expand Up @@ -1311,6 +1335,21 @@ 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;
* 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);
Comment thread
LinuxJedi marked this conversation as resolved.
key->keyId = 0;
key->keyIdSet = 0;
#endif

XMEMCPY(key->k, priv, ED25519_KEY_SIZE);
key->privKeySet = 1;

Expand Down
43 changes: 38 additions & 5 deletions wolfcrypt/src/port/nxp/se050_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Comment thread
LinuxJedi marked this conversation as resolved.
* 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);
Comment thread
LinuxJedi marked this conversation as resolved.
}
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) {
Expand Down Expand Up @@ -3039,6 +3053,12 @@ 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;

Comment thread
LinuxJedi marked this conversation as resolved.
if (cfg_se050_i2c_pi == NULL) {
return WC_HW_E;
}
Expand Down Expand Up @@ -3099,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
Comment thread
LinuxJedi marked this conversation as resolved.
* 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;
}
Expand Down
61 changes: 52 additions & 9 deletions wolfcrypt/test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -41340,7 +41343,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));

Expand Down Expand Up @@ -41413,7 +41425,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 */
Expand Down Expand Up @@ -41466,35 +41483,53 @@ 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;

ret = ed25519ph_test();
if (ret != 0)
goto cleanup;
#endif /* !WOLFSSL_SE050 */

#ifndef NO_ASN
/* Try ASN.1 encoded private-only key and public key. */
Expand All @@ -41509,8 +41544,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 */
Expand Down
Loading