Added ECDHE support

This commit is contained in:
David Garske
2026-01-12 22:56:13 +00:00
parent 09c75f25de
commit 02c3086e00

View File

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