diff --git a/.github/workflows/se050-sim.yml b/.github/workflows/se050-sim.yml index 9799ddb307..1f7cc4bd27 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: 895ff41b2447efe952e902c467c5ef479f3eda37 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,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 - run: docker run --rm wolfssl-se050-sim:ci-${{ matrix.name }} + # 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/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 814001c990..9f6f6fe9f2 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 @@ -891,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 diff --git a/wolfcrypt/src/port/nxp/se050_port.c b/wolfcrypt/src/port/nxp/se050_port.c index 5e62fed708..556e2d2b4a 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) { @@ -2850,6 +2853,9 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, status = sss_key_object_allocate_handle(&deriveKey, keyIdAes, kSSS_KeyPart_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, keySize, kKeyObject_Mode_Transient); @@ -2882,8 +2888,70 @@ int se050_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, 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 + * 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); + /* 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 { + status = kStatus_SSS_Fail; + } + } + } +#endif 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. */ @@ -2896,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; } @@ -3359,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; +#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; - 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", @@ -3399,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) { @@ -3441,10 +3515,12 @@ 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, + /* 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, keySize, kKeyObject_Mode_Transient); @@ -3454,12 +3530,16 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, &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 = sizeof(out->point); size_t outlenSzBits = outlenSz * 8; + deriveKeyCreated = 1; /* derived key export */ status = sss_key_store_get_key(&host_keystore, &deriveKey, out->point, &outlenSz, &outlenSzBits); @@ -3473,8 +3553,85 @@ int se050_curve25519_shared_secret(curve25519_key* private_key, 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 + * 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; + } + } + } +#endif 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.*/ @@ -3487,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; }