Merge pull request #10971 from LinuxJedi/se050_applet72_ecdh

SE050: create ECDH derive target object for applet 7.2 middleware
pull/10974/head
David Garske 2026-08-04 17:35:23 -07:00 committed by GitHub
commit 0ffedd2b69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 179 additions and 9 deletions

View File

@ -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 }}

View File

@ -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

View File

@ -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;
}