From 02c3086e00cefbbc9b10f3113265ffee5f092299 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 12 Jan 2026 22:56:13 +0000 Subject: [PATCH] Added ECDHE support --- wolfcrypt/src/port/st/stsafe.c | 314 ++++++++++++++++++++++++++++++--- 1 file changed, 289 insertions(+), 25 deletions(-) diff --git a/wolfcrypt/src/port/st/stsafe.c b/wolfcrypt/src/port/st/stsafe.c index 6d2609898..4beeb4c41 100644 --- a/wolfcrypt/src/port/st/stsafe.c +++ b/wolfcrypt/src/port/st/stsafe.c @@ -103,6 +103,16 @@ static stsafe_curve_id_t g_stsafe_curve_mode = STSAFE_DEFAULT_CURVE; /* Internal Helper Functions */ /* ========================================================================== */ +/** + * \brief Helper macros to store/retrieve slot number in devCtx + * \details Slot number is stored directly in devCtx as void* to avoid + * dynamic memory allocation. Slot values are small (0, 1, 0xFF) + * so safe to cast to/from void*. + */ +#define STSAFE_SLOT_TO_DEVCXT(slot) ((void*)(uintptr_t)(slot)) +#define STSAFE_DEVCXT_TO_SLOT(devCtx) ((stsafe_slot_t)(uintptr_t)(devCtx)) + + /** * \brief Get key size in bytes for a given curve */ @@ -263,16 +273,21 @@ int stsafe_interface_init(void) /** * \brief Generate ECC key pair on STSAFE-A120 + * \details Uses dedicated key slot (slot 1) for persistent keys. + * For ephemeral ECDHE keys, use stsafe_create_ecdhe_key() instead. */ static int stsafe_create_key(stsafe_slot_t* pSlot, stsafe_curve_id_t curve_id, uint8_t* pPubKeyRaw) { int rc = STSAFE_A_OK; stse_ReturnCode_t ret; - stsafe_slot_t slot = STSAFE_KEY_SLOT_1; /* Ephemeral slot */ + stsafe_slot_t slot = STSAFE_KEY_SLOT_1; /* Use dedicated key slot for persistent keys */ - /* Generate key pair - public key is X||Y concatenated */ - ret = stse_generate_ecc_key_pair(&g_stse_handler, slot, curve_id, + /* Generate key pair - public key is X||Y concatenated + * Note: stse_generate_ecc_key_pair expects stse_ecc_key_type_t, + * but stsafe_curve_id_t values match stse_ecc_key_type_t enum values */ + ret = stse_generate_ecc_key_pair(&g_stse_handler, slot, + (stse_ecc_key_type_t)curve_id, 255, /* usage_limit */ pPubKeyRaw); if (ret != STSE_OK) { @@ -287,6 +302,34 @@ static int stsafe_create_key(stsafe_slot_t* pSlot, stsafe_curve_id_t curve_id, return rc; } +/** + * \brief Generate ECDHE ephemeral key pair on STSAFE-A120 + * \details Uses stse_generate_ECDHE_key_pair() which generates truly + * ephemeral keys (not stored in slots). The private key remains + * in STSE internal memory for use with shared secret computation. + * Public key is returned in X||Y format (same as stse_generate_ecc_key_pair). + */ +static int stsafe_create_ecdhe_key(stsafe_curve_id_t curve_id, + uint8_t* pPubKeyRaw) +{ + int rc = STSAFE_A_OK; + stse_ReturnCode_t ret; + + if (pPubKeyRaw == NULL) { + return BAD_FUNC_ARG; + } + + /* Generate ECDHE ephemeral key pair - public key returned as X||Y */ + ret = stse_generate_ECDHE_key_pair(&g_stse_handler, + (stse_ecc_key_type_t)curve_id, pPubKeyRaw); + if (ret != STSE_OK) { + STSAFE_INTERFACE_PRINTF("stse_generate_ECDHE_key_pair error: %d\n", ret); + rc = (int)ret; + } + + return rc; +} + /** * \brief ECDSA sign using STSAFE-A120 */ @@ -363,11 +406,57 @@ static int stsafe_shared_secret(stsafe_slot_t slot, stsafe_curve_id_t curve_id, XMEMCPY(peerPubKey, pPubKeyX, key_sz); XMEMCPY(peerPubKey + key_sz, pPubKeyY, key_sz); - /* Compute shared secret */ - ret = stse_ecc_establish_shared_secret(&g_stse_handler, slot, curve_id, - peerPubKey, pSharedSecret); + /* Compute shared secret + * Note: stse_ecc_establish_shared_secret expects stse_ecc_key_type_t. + * For STSAFE-A120, stsafe_curve_id_t values match stse_ecc_key_type_t enum values: + * STSAFE_ECC_CURVE_P256 (0) = STSE_ECC_KT_NIST_P_256 (0) + * STSAFE_ECC_CURVE_P384 (1) = STSE_ECC_KT_NIST_P_384 (1) */ + ret = stse_ecc_establish_shared_secret(&g_stse_handler, slot, + (stse_ecc_key_type_t)curve_id, peerPubKey, pSharedSecret); if (ret != STSE_OK) { - STSAFE_INTERFACE_PRINTF("stse_ecc_establish_shared_secret error: %d\n", + STSAFE_INTERFACE_PRINTF("stse_ecc_establish_shared_secret error: %d (slot: %d, curve_id: %d)\n", + ret, slot, curve_id); + rc = (int)ret; + } + + if (rc == STSAFE_A_OK) { + *pSharedSecretLen = (int32_t)key_sz; + } + + return rc; +} + +/** + * \brief ECDHE shared secret using STSAFE-A120 + * \details Computes shared secret using the ephemeral ECDHE private key + * that was generated by stsafe_create_ecdhe_key(). The ephemeral + * private key is stored internally in the STSE device. + */ +static int stsafe_shared_secret_ecdhe(stsafe_curve_id_t curve_id, + uint8_t* pPubKeyX, uint8_t* pPubKeyY, + uint8_t* pSharedSecret, + int32_t* pSharedSecretLen) +{ + int rc = STSAFE_A_OK; + stse_ReturnCode_t ret; + int key_sz = stsafe_get_key_size(curve_id); + uint8_t peerPubKey[STSAFE_MAX_PUBKEY_RAW_LEN]; + + if (pPubKeyX == NULL || pPubKeyY == NULL || pSharedSecret == NULL || + pSharedSecretLen == NULL || key_sz == 0) { + return BAD_FUNC_ARG; + } + + /* Combine peer X and Y (X||Y format) */ + XMEMCPY(peerPubKey, pPubKeyX, key_sz); + XMEMCPY(peerPubKey + key_sz, pPubKeyY, key_sz); + + /* Compute shared secret using ephemeral slot (0xFF) + * The ephemeral private key was generated by stse_generate_ECDHE_key_pair() */ + ret = stse_ecc_establish_shared_secret(&g_stse_handler, + STSAFE_KEY_SLOT_EPHEMERAL, curve_id, peerPubKey, pSharedSecret); + if (ret != STSE_OK) { + STSAFE_INTERFACE_PRINTF("stse_ecc_establish_shared_secret (ECDHE) error: %d\n", ret); rc = (int)ret; } @@ -977,7 +1066,7 @@ int SSL_STSAFE_LoadDeviceCertificate(byte** pRawCertificate, #if !defined(WOLFCRYPT_ONLY) && defined(HAVE_PK_CALLBACKS) /** - * \brief Key Gen Callback (used by TLS server) + * \brief Key Gen Callback (used by TLS server for ECDHE) */ int SSL_STSAFE_CreateKeyCb(WOLFSSL* ssl, ecc_key* key, word32 keySz, int ecc_curve, void* ctx) @@ -995,7 +1084,7 @@ int SSL_STSAFE_CreateKeyCb(WOLFSSL* ssl, ecc_key* key, word32 keySz, (void)ctx; #ifdef USE_STSAFE_VERBOSE - WOLFSSL_MSG("CreateKeyCb: STSAFE"); + WOLFSSL_MSG("CreateKeyCb: STSAFE (ECDHE)"); #endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -1009,11 +1098,23 @@ int SSL_STSAFE_CreateKeyCb(WOLFSSL* ssl, ecc_key* key, word32 keySz, if (err == 0) { curve_id = stsafe_get_ecc_curve_id(ecc_curve); +#ifdef WOLFSSL_STSAFEA120 + /* Use ECDHE ephemeral key generation for A120 */ + err = stsafe_create_ecdhe_key(curve_id, pubKeyRaw); + if (err != STSAFE_A_OK) { + STSAFE_INTERFACE_PRINTF("stsafe_create_ecdhe_key error: %d\n", err); + err = WC_HW_E; + } + /* For ECDHE, slot is not used (ephemeral key stored internally) */ + slot = STSAFE_KEY_SLOT_EPHEMERAL; +#else + /* Legacy A100/A110 uses slot-based key generation */ err = stsafe_create_key(&slot, curve_id, pubKeyRaw); if (err != STSAFE_A_OK) { STSAFE_INTERFACE_PRINTF("stsafe_create_key error: %d\n", err); err = WC_HW_E; } +#endif } if (err == 0) { @@ -1025,6 +1126,8 @@ int SSL_STSAFE_CreateKeyCb(WOLFSSL* ssl, ecc_key* key, word32 keySz, XFREE(pubKeyRaw, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif + (void)slot; /* May be unused for A120 ECDHE */ + return err; } @@ -1212,7 +1315,7 @@ int SSL_STSAFE_SignCertificateCb(WOLFSSL* ssl, const byte* in, } /** - * \brief Shared Secret Callback (ECDH) + * \brief Shared Secret Callback (ECDHE) */ int SSL_STSAFE_SharedSecretCb(WOLFSSL* ssl, ecc_key* otherKey, unsigned char* pubKeyDer, unsigned int* pubKeySz, @@ -1242,7 +1345,7 @@ int SSL_STSAFE_SharedSecretCb(WOLFSSL* ssl, ecc_key* otherKey, (void)ctx; #ifdef USE_STSAFE_VERBOSE - WOLFSSL_MSG("SharedSecretCb: STSAFE"); + WOLFSSL_MSG("SharedSecretCb: STSAFE (ECDHE)"); #endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -1274,12 +1377,24 @@ int SSL_STSAFE_SharedSecretCb(WOLFSSL* ssl, ecc_key* otherKey, otherKeyY, &otherKeyY_len); if (err == 0) { +#ifdef WOLFSSL_STSAFEA120 + /* Use ECDHE ephemeral key generation for A120 */ + err = stsafe_create_ecdhe_key(curve_id, pubKeyRaw); + if (err != STSAFE_A_OK) { + STSAFE_INTERFACE_PRINTF("stsafe_create_ecdhe_key error: %d\n", + err); + err = WC_HW_E; + } + slot = STSAFE_KEY_SLOT_EPHEMERAL; +#else + /* Legacy A100/A110 uses slot-based key generation */ err = stsafe_create_key(&slot, curve_id, pubKeyRaw); if (err != STSAFE_A_OK) { STSAFE_INTERFACE_PRINTF("stsafe_create_key error: %d\n", err); err = WC_HW_E; } +#endif } if (err == 0) { @@ -1304,12 +1419,23 @@ int SSL_STSAFE_SharedSecretCb(WOLFSSL* ssl, ecc_key* otherKey, } if (err == 0) { +#ifdef WOLFSSL_STSAFEA120 + /* Use ECDHE shared secret computation for A120 */ + err = stsafe_shared_secret_ecdhe(curve_id, otherKeyX, otherKeyY, + out, (int32_t*)outlen); + if (err != STSAFE_A_OK) { + STSAFE_INTERFACE_PRINTF("stsafe_shared_secret_ecdhe error: %d\n", err); + err = WC_HW_E; + } +#else + /* Legacy A100/A110 uses slot-based shared secret */ err = stsafe_shared_secret(slot, curve_id, otherKeyX, otherKeyY, out, (int32_t*)outlen); if (err != STSAFE_A_OK) { STSAFE_INTERFACE_PRINTF("stsafe_shared_secret error: %d\n", err); err = WC_HW_E; } +#endif } if (tmpKeyInit) { @@ -1420,7 +1546,29 @@ int wolfSSL_STSAFE_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) curve_id = stsafe_get_ecc_curve_id(ecc_curve); key_sz = stsafe_get_key_size(curve_id); + /* For A120, generate keys in slot 1 (persistent slot) by default for ECDSA signing. + * For ECDH operations, ephemeral keys will be generated on-demand in the ECDH callback + * if needed (see WC_PK_TYPE_ECDH handling below). */ +#ifdef WOLFSSL_STSAFEA120 + stse_ReturnCode_t ret; + slot = STSAFE_KEY_SLOT_1; /* Use persistent slot for ECDSA signing */ + ret = stse_generate_ecc_key_pair(&g_stse_handler, slot, + (stse_ecc_key_type_t)curve_id, + 255, /* usage_limit for persistent keys */ + pubKeyRaw); + if (ret != STSE_OK) { + STSAFE_INTERFACE_PRINTF("stse_generate_ecc_key_pair (slot 1) error: %d\n", ret); + rc = (int)ret; + } else { + rc = STSAFE_A_OK; + } + if (rc != STSAFE_A_OK) { + rc = WC_HW_E; + } +#else + /* Legacy A100/A110 uses slot-based key generation */ rc = stsafe_create_key(&slot, curve_id, pubKeyRaw); +#endif if (rc != STSAFE_A_OK) { STSAFE_INTERFACE_PRINTF("stsafe_create_key error: %d\n", rc); @@ -1429,8 +1577,19 @@ int wolfSSL_STSAFE_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) } if (rc == 0) { + /* Store slot number directly in devCtx (no dynamic allocation) */ + info->pk.eckg.key->devCtx = STSAFE_SLOT_TO_DEVCXT(slot); + } + + if (rc == 0) { + /* Import public key - preserve devCtx */ + void* saved_devCtx = info->pk.eckg.key->devCtx; rc = wc_ecc_import_unsigned(info->pk.eckg.key, pubKeyRaw, &pubKeyRaw[key_sz], NULL, ecc_curve); + /* Restore devCtx in case import cleared it */ + if (saved_devCtx != NULL && info->pk.eckg.key->devCtx != saved_devCtx) { + info->pk.eckg.key->devCtx = saved_devCtx; + } } #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -1483,8 +1642,15 @@ int wolfSSL_STSAFE_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) XMEMCPY(&digest[key_sz - inSz], info->pk.eccsign.in, inSz); XMEMSET(sigRS, 0, STSAFE_MAX_SIG_LEN); - /* Use slot 1 where keys are generated */ - rc = stsafe_sign(STSAFE_KEY_SLOT_1, curve_id, digest, sigRS); + /* Retrieve slot from devCtx if available, otherwise use default */ + stsafe_slot_t slot = STSAFE_KEY_SLOT_1; /* Default fallback */ + if (info->pk.eccsign.key != NULL && info->pk.eccsign.key->devCtx != NULL) { + slot = STSAFE_DEVCXT_TO_SLOT(info->pk.eccsign.key->devCtx); + STSAFE_INTERFACE_PRINTF("STSAFE: Using slot %d from devCtx for signing\n", slot); + } else { + WOLFSSL_MSG("STSAFE: Warning: devCtx not found, using default slot 1"); + } + rc = stsafe_sign(slot, curve_id, digest, sigRS); if (rc != STSAFE_A_OK) { STSAFE_INTERFACE_PRINTF("stsafe_sign error: %d\n", rc); rc = WC_HW_E; @@ -1624,22 +1790,120 @@ int wolfSSL_STSAFE_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) #endif if (rc == 0) { - ecc_curve = info->pk.ecdh.public_key->dp->id; - curve_id = stsafe_get_ecc_curve_id(ecc_curve); + /* Get curve from private_key (hardware key), not public_key (peer key) */ + if (info->pk.ecdh.private_key != NULL && + info->pk.ecdh.private_key->dp != NULL) { + ecc_curve = info->pk.ecdh.private_key->dp->id; + } else if (info->pk.ecdh.public_key != NULL && + info->pk.ecdh.public_key->dp != NULL) { + /* Fallback to public_key if private_key not available */ + ecc_curve = info->pk.ecdh.public_key->dp->id; + } else { + rc = BAD_FUNC_ARG; + } + if (rc == 0) { + curve_id = stsafe_get_ecc_curve_id(ecc_curve); + /* Note: STSAFE_ECC_CURVE_P256 is 0, so we can't use STSAFE_DEFAULT_CURVE check. + * Instead, verify the curve_id is valid by checking it's one of the supported curves */ + if (curve_id != STSAFE_ECC_CURVE_P256 && curve_id != STSAFE_ECC_CURVE_P384) { + rc = BAD_FUNC_ARG; + } + } - rc = wc_ecc_export_public_raw(info->pk.ecdh.public_key, - otherKeyX, &otherKeyX_len, otherKeyY, &otherKeyY_len); + if (rc == 0) { + rc = wc_ecc_export_public_raw(info->pk.ecdh.public_key, + otherKeyX, &otherKeyX_len, otherKeyY, &otherKeyY_len); + } } if (rc == 0) { *info->pk.ecdh.outlen = 0; - /* Use slot 1 where keys are generated */ - rc = stsafe_shared_secret(STSAFE_KEY_SLOT_1, curve_id, - otherKeyX, otherKeyY, - info->pk.ecdh.out, (int32_t*)info->pk.ecdh.outlen); - if (rc != STSAFE_A_OK) { - STSAFE_INTERFACE_PRINTF("stsafe_shared_secret error: %d\n", - rc); - rc = WC_HW_E; + + /* Check if private key is software but public key is hardware. + * In this case, we can't use hardware for computation since the + * private key is not in a slot. Return CRYPTOCB_UNAVAILABLE to + * let software handle it (but software path may also fail if + * public key export fails). */ + if (info->pk.ecdh.private_key == NULL || + info->pk.ecdh.private_key->devId == INVALID_DEVID) { + if (info->pk.ecdh.public_key != NULL && + info->pk.ecdh.public_key->devId != INVALID_DEVID) { + WOLFSSL_MSG("STSAFE: Private key is software, public key is hardware - cannot use hardware"); + rc = CRYPTOCB_UNAVAILABLE; + } + } + + if (rc == 0) { + /* For ECDH operations, use ephemeral slot (0xFF). + * Keys are generated in slot 1 by default (for ECDSA signing). + * If the key is in slot 1, generate a new ephemeral key for ECDH. + * If the key is already in the ephemeral slot, use it directly. */ + stsafe_slot_t slot; + stsafe_slot_t original_slot = STSAFE_KEY_SLOT_1; + int need_ephemeral_key = 0; + + if (info->pk.ecdh.private_key != NULL && + info->pk.ecdh.private_key->devCtx != NULL) { + original_slot = STSAFE_DEVCXT_TO_SLOT(info->pk.ecdh.private_key->devCtx); + + /* If key is in slot 1 (for ECDSA), we need to generate ephemeral key for ECDH */ + if (original_slot == STSAFE_KEY_SLOT_1) { + need_ephemeral_key = 1; + } + } + + if (need_ephemeral_key) { + /* Key is in slot 1 (for ECDSA), but ECDH requires ephemeral slot. + * Generate ephemeral key pair for ECDH. Note: This will overwrite any + * existing key in ephemeral slot, so for bidirectional ECDH, both keys + * should be generated in ephemeral slot from the start. */ + stse_ReturnCode_t ret; + byte ephemeralPubKey[STSAFE_MAX_PUBKEY_RAW_LEN]; + int key_sz = stsafe_get_key_size(curve_id); + slot = STSAFE_KEY_SLOT_EPHEMERAL; + + ret = stse_generate_ecc_key_pair(&g_stse_handler, slot, + (stse_ecc_key_type_t)curve_id, + STSAFEA_EPHEMERAL_KEY_USAGE_LIMIT, + ephemeralPubKey); + if (ret != STSE_OK) { + STSAFE_INTERFACE_PRINTF("stse_generate_ecc_key_pair (ephemeral for ECDH) error: %d\n", ret); + rc = (int)ret; + } else { + WOLFSSL_MSG("STSAFE: Generated ephemeral key for ECDH"); + /* Update devCtx to reflect ephemeral slot for this key */ + if (info->pk.ecdh.private_key != NULL) { + info->pk.ecdh.private_key->devCtx = STSAFE_SLOT_TO_DEVCXT(slot); + } + /* Update the public key in the key structure to match the new ephemeral key */ + if (info->pk.ecdh.private_key != NULL && rc == 0) { + void* saved_devCtx = info->pk.ecdh.private_key->devCtx; + rc = wc_ecc_import_unsigned(info->pk.ecdh.private_key, + ephemeralPubKey, &ephemeralPubKey[key_sz], + NULL, ecc_curve); + /* Restore devCtx in case import cleared it */ + if (saved_devCtx != NULL && info->pk.ecdh.private_key->devCtx != saved_devCtx) { + info->pk.ecdh.private_key->devCtx = saved_devCtx; + } + } + } + } else { + /* Key is already in ephemeral slot, use it */ + slot = STSAFE_KEY_SLOT_EPHEMERAL; + } + + if (rc == 0) { + STSAFE_INTERFACE_PRINTF("STSAFE: Computing shared secret with ephemeral slot %d, curve_id %d\n", + slot, curve_id); + rc = stsafe_shared_secret(slot, curve_id, + otherKeyX, otherKeyY, + info->pk.ecdh.out, (int32_t*)info->pk.ecdh.outlen); + if (rc != STSAFE_A_OK) { + WOLFSSL_MSG("STSAFE: stsafe_shared_secret failed"); + STSAFE_INTERFACE_PRINTF("stsafe_shared_secret error: %d (slot: %d, curve_id: %d)\n", + rc, slot, curve_id); + rc = WC_HW_E; + } + } } }