diff --git a/doc/dox_comments/header_files/ecc.h b/doc/dox_comments/header_files/ecc.h index 90497dac13..74b3937528 100644 --- a/doc/dox_comments/header_files/ecc.h +++ b/doc/dox_comments/header_files/ecc.h @@ -1326,31 +1326,35 @@ int wc_ecc_import_private_key(const byte* priv, word32 privSz, const byte* pub, a chip-bound wrapped blob together with the 256-bit derivation seed; the plaintext scalar is never imported. The key must be bound to the STM32 DHUK crypto-callback device (init with wc_ecc_init_ex(&key, heap, WC_DHUK_DEVID) - after registering the device with wc_Stm32_DhukRegister). Available only on + after registering the device with wc_Stm32_DhukRegister). The curve is set + from curve_id, so the key is ready to sign on return. Available only on STM32 builds with WOLFSSL_DHUK and a DHUK-capable SAES (WC_STM32_HAS_DHUK). \return 0 Returned on success. \return BAD_FUNC_ARG Returned if key, seed, or wrapped is NULL; if seedSz is not 32; if wrappedLen is zero or not a multiple of the AES block size; if wrappedLen exceeds the on-key blob buffer; if plainLen is zero or larger - than wrappedLen; or if wrappedLen is larger than plainLen padded to a full - AES block. + than wrappedLen; if wrappedLen is larger than plainLen padded to a full + AES block; or if plainLen does not match the scalar size of curve_id. + \return <0 An error from the curve lookup if curve_id is not supported. \param key pointer to the ecc_key (bound to WC_DHUK_DEVID) to import into. + \param curve_id curve the scalar belongs to, e.g. ECC_SECP256R1. \param seed pointer to the 256-bit (32-byte) per-key DHUK derivation seed. \param seedSz length of seed in bytes; must be 32. \param wrapped pointer to the DHUK-wrapped private scalar blob. \param wrappedLen length of the wrapped blob; a non-zero multiple of the AES block size, no larger than the on-key buffer. - \param plainLen length in bytes of the plaintext scalar inside the blob. + \param plainLen length in bytes of the plaintext scalar inside the blob; + must equal the scalar size of curve_id (32 for P-256, 48 for P-384). _Example_ \code ecc_key key; wc_Stm32_DhukRegister(WC_DHUK_DEVID); wc_ecc_init_ex(&key, NULL, WC_DHUK_DEVID); - if (wc_ecc_import_wrapped_private(&key, seed, 32, wrapped, wrappedLen, - plainLen) == 0) { + if (wc_ecc_import_wrapped_private(&key, ECC_SECP256R1, seed, 32, wrapped, + wrappedLen, plainLen) == 0) { wc_ecc_sign_hash(hash, hashLen, sig, &sigLen, &rng, &key); } wc_ecc_free(&key); @@ -1360,7 +1364,8 @@ int wc_ecc_import_private_key(const byte* priv, word32 privSz, const byte* pub, \sa wc_ecc_sign_hash \sa wc_ecc_init_ex */ -int wc_ecc_import_wrapped_private(ecc_key* key, const byte* seed, word32 seedSz, +int wc_ecc_import_wrapped_private(ecc_key* key, int curve_id, + const byte* seed, word32 seedSz, const byte* wrapped, word32 wrappedLen, word32 plainLen); diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 078b288e16..b091f3bc32 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -8307,10 +8307,13 @@ int wc_ecc_sign_set_k(const byte* k, word32 klen, ecc_key* key) * at sign time it is decrypted into a short-lived buffer. The devId is NOT set * here -- enable the device by setting devId at init * (wc_ecc_init_ex(&key, heap, WC_DHUK_DEVID)). See ecc.h for the contract. */ -int wc_ecc_import_wrapped_private(ecc_key* key, const byte* seed, word32 seedSz, +int wc_ecc_import_wrapped_private(ecc_key* key, int curve_id, + const byte* seed, word32 seedSz, const byte* wrapped, word32 wrappedLen, word32 plainLen) { + int ret; + if (key == NULL || seed == NULL || wrapped == NULL) { return BAD_FUNC_ARG; } @@ -8335,6 +8338,25 @@ int wc_ecc_import_wrapped_private(ecc_key* key, const byte* seed, word32 seedSz, if (wrappedLen > ((plainLen + 15u) & ~15u)) { return BAD_FUNC_ARG; } + /* Validate the scalar size against the curve before touching the key, so a + * rejected import leaves no curve behind on a key that has no blob. */ + ret = wc_ecc_get_curve_size_from_id(curve_id); + if (ret < 0) { + return ret; + } + if ((word32)ret != plainLen) { + return BAD_FUNC_ARG; + } + + /* The sign path needs the domain parameters to drive the PKA and returns + * ECC_BAD_ARG_E without them, and a caller starting from a bare + * wc_ecc_init() has no other way to supply them for a key that never holds + * its scalar in software. */ + ret = wc_ecc_set_curve(key, (int)plainLen, curve_id); + if (ret != 0) { + return ret; + } + XMEMCPY(key->dhuk_wrapped_priv, wrapped, wrappedLen); XMEMCPY(key->dhuk_seed, seed, seedSz); key->dhuk_wrapped_priv_len = wrappedLen; diff --git a/wolfcrypt/src/port/st/README.md b/wolfcrypt/src/port/st/README.md index b92981c126..cfc0b18a32 100644 --- a/wolfcrypt/src/port/st/README.md +++ b/wolfcrypt/src/port/st/README.md @@ -174,7 +174,7 @@ wc_AesFree(&aes); wc_Stm32_DhukUnRegister(WC_DHUK_DEVID); ``` -ECDSA mirrors this: init the key with `wc_ecc_init_ex(&key, NULL, WC_DHUK_DEVID)`, import the wrapped private scalar plus its derivation seed with `wc_ecc_import_wrapped_private(&key, seed, seedSz, wrapped, wrappedLen, plainLen)`, then call the normal `wc_ecc_sign_hash()`; verification uses the in-clear public key unchanged. The seed reaches the device as the AES key bytes (`aes->devKey`, set by the normal `wc_AesSetKey` / `wc_AesGcmSetKey`) or, for ECC, on the `ecc_key`; the STM32 callback reads it and derives the working key inside SAES. +ECDSA mirrors this: init the key with `wc_ecc_init_ex(&key, NULL, WC_DHUK_DEVID)`, import the wrapped private scalar plus its derivation seed with `wc_ecc_import_wrapped_private(&key, ECC_SECP256R1, seed, seedSz, wrapped, wrappedLen, plainLen)`, then call the normal `wc_ecc_sign_hash()`. The curve argument is required: it is what gives the sign path the domain parameters to drive the PKA, and the key is ready to sign on return. Verification uses the in-clear public key unchanged. The seed reaches the device as the AES key bytes (`aes->devKey`, set by the normal `wc_AesSetKey` / `wc_AesGcmSetKey`) or, for ECC, on the `ecc_key`; the STM32 callback reads it and derives the working key inside SAES. Worked example: [`STM32_Bare_Test/src/main_dhuk.c`](https://github.com/wolfSSL/wolfssl-examples-stm32/blob/master/STM32_Bare_Test/src/main_dhuk.c) drives `wc_Stm32_DhukRegister` through transparent GMAC, AES-ECB, and ECDSA, and exercises the `wc_ecc_import_wrapped_private` argument validation in its `test_ecc_dhuk_setter()` block. diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index 072910a574..64b56fee9c 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -801,22 +801,29 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng, #if defined(WOLFSSL_DHUK) && defined(WC_STM32_HAS_DHUK) && \ (defined(WOLFSSL_STM32_BARE) || defined(WOLFSSL_STM32_CUBEMX)) /* DHUK ECC sign: import a hardware-wrapped ECC private scalar + its derivation - * seed onto the ecc_key for the crypto-callback sign path. The caller MUST also - * populate key->pubkey (via wc_ecc_import_x963) so verify can use the - * in-clear public counterpart, and enable the device by setting devId at init - * (wc_ecc_init_ex(&key, heap, WC_DHUK_DEVID)). + * seed onto the ecc_key for the crypto-callback sign path. Sets the curve, so + * the key is ready to sign on return. Enable the device by setting devId at + * init (wc_ecc_init_ex(&key, heap, WC_DHUK_DEVID)). To verify with this same + * key, also populate key->pubkey (via wc_ecc_import_x963) -- verify uses the + * in-clear public counterpart and does not touch the wrapped scalar. + * curve_id -- curve the scalar belongs to (e.g. ECC_SECP256R1); without + * it the sign path has no parameters to drive the PKA and + * returns ECC_BAD_ARG_E * seed -- 256-bit derivation seed (mixed with the silicon DHUK to * derive the key that unwraps the scalar) * seedSz -- seed length, must be 32 * wrapped -- ECC scalar AES-encrypted with the SAES-derived device key; * length is a multiple of 16, <= 96 * wrappedLen -- length of the wrapped blob - * plainLen -- actual scalar size (e.g. 32 for P-256) + * plainLen -- actual scalar size, and must match the curve (32 for + * P-256, 48 for P-384) * - * On success: stores seed + blob + lengths, returns 0 (does NOT set devId). - * On failure: BAD_FUNC_ARG. */ + * On success: sets the curve, stores seed + blob + lengths, returns 0 (does + * NOT set devId). + * On failure: BAD_FUNC_ARG, or an error from the curve lookup. */ WOLFSSL_API -int wc_ecc_import_wrapped_private(ecc_key* key, const byte* seed, word32 seedSz, +int wc_ecc_import_wrapped_private(ecc_key* key, int curve_id, + const byte* seed, word32 seedSz, const byte* wrapped, word32 wrappedLen, word32 plainLen); #endif