From 6ecf87ca9a9b334e0e1ea0e3dd3880f57c894aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Fri, 6 Nov 2026 10:20:03 +0100 Subject: [PATCH] crypto: keep the ECC private-only key update at the prime size wh_Crypto_EccUpdatePrivateOnlyKeyDer() exports the private scalar with wc_ecc_export_private_only() and re-imports it, with the public key from the server's response, through wc_ecc_import_unsigned(). That import reads d at the size of the prime and takes no length. wolfSSL is changing wc_ecc_export_private_only() to write the scalar zero-padded to the size of the curve order, as RFC 5915 and OpenSSL do. On secp160r1, secp160r2, secp160k1 and secp224k1 the order is one byte longer than the prime, so the export would be 21 or 29 bytes and the import would read the first 20 or 28: after a client verify the private-only key would silently hold a different scalar. Export with wc_ecc_export_private_raw() instead. It writes d at the size of the prime in current wolfSSL and in every release wolfHSM builds with, so the import always gets the layout it expects. Nothing tested this path: no test verified with a private-only key. Add one in test-refactor that does so on P-256 and on secp224k1, whose order is a byte longer than its prime, and checks that the key gains its public point and keeps its scalar. The test configuration enables Koblitz curves for it. --- src/wh_crypto.c | 3 +- .../client-server/wh_test_crypto_ecc.c | 95 +++++++++++++++++++ test/config/user_settings.h | 2 + 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/wh_crypto.c b/src/wh_crypto.c index 91d100999..3ae36e824 100644 --- a/src/wh_crypto.c +++ b/src/wh_crypto.c @@ -202,7 +202,8 @@ int wh_Crypto_EccUpdatePrivateOnlyKeyDer(ecc_key* key, uint16_t pub_size, } /* TODO: Find more efficient way to doing this */ if (ret == 0) { - ret = wc_ecc_export_private_only(key, d, &d_len); + /* wc_ecc_import_unsigned() reads d at the size of the prime */ + ret = wc_ecc_export_private_raw(key, NULL, NULL, NULL, NULL, d, &d_len); } if (ret == 0) { ret = wc_ecc_init(pub_only); diff --git a/test-refactor/client-server/wh_test_crypto_ecc.c b/test-refactor/client-server/wh_test_crypto_ecc.c index e6d798367..fae7a0343 100644 --- a/test-refactor/client-server/wh_test_crypto_ecc.c +++ b/test-refactor/client-server/wh_test_crypto_ecc.c @@ -2674,6 +2674,98 @@ static int _whTest_CryptoEccExportPublicKeyDma(whClientContext* ctx) } #endif /* WOLFHSM_CFG_DMA */ +#if !defined(NO_ECC_MAKE_PUB) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) +/* Verify with a private-only key, which rebuilds the key from the public point + * the server returns, and check that the private scalar is unchanged. */ +static int _whTest_CryptoEccPrivOnlyVerify(whClientContext* ctx) +{ + /* secp224k1 has an order one byte longer than its prime */ + static const int curves[] = {ECC_SECP256R1, ECC_SECP224K1}; + int devId = WH_CLIENT_DEVID(ctx); + int ret = 0; + size_t i; + WC_RNG rng[1]; + ecc_key refKey[1]; + ecc_key privOnlyKey[1]; + uint8_t hash[TEST_ECC_KEYSIZE]; + uint8_t sig[ECC_MAX_SIG_SIZE]; + uint8_t refD[ECC_MAXSIZE]; + uint8_t d[ECC_MAXSIZE]; + + memset(hash, 0x5a, sizeof(hash)); + ret = wc_InitRng_ex(rng, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_InitRng_ex %d\n", ret); + return ret; + } + + for (i = 0; (ret == 0) && (i < sizeof(curves) / sizeof(curves[0])); i++) { + int keySz = wc_ecc_get_curve_size_from_id(curves[i]); + int verify = 0; + word32 sigLen = sizeof(sig); + word32 refDLen = sizeof(refD); + word32 dLen = sizeof(d); + + if (wc_ecc_get_curve_idx(curves[i]) < 0) { + continue; + } + + ret = wc_ecc_init_ex(refKey, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_ecc_make_key_ex(rng, keySz, refKey, curves[i]); + if (ret == 0) { + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigLen, rng, + refKey); + } + if (ret == 0) { + ret = wc_ecc_export_private_raw(refKey, NULL, NULL, NULL, NULL, + refD, &refDLen); + } + wc_ecc_free(refKey); + } + if (ret != 0) { + WH_ERROR_PRINT("Failed to make ECC reference key %d\n", ret); + break; + } + + ret = wc_ecc_init_ex(privOnlyKey, NULL, devId); + if (ret == 0) { + ret = wc_ecc_import_private_key_ex(refD, refDLen, NULL, 0, + privOnlyKey, curves[i]); + if (ret == 0) { + ret = wc_ecc_verify_hash(sig, sigLen, hash, sizeof(hash), + &verify, privOnlyKey); + } + if ((ret == 0) && + ((verify != 1) || (privOnlyKey->type != ECC_PRIVATEKEY))) { + WH_ERROR_PRINT("Private-only ECC verify did not update key\n"); + ret = -1; + } + if (ret == 0) { + ret = wc_ecc_export_private_raw(privOnlyKey, NULL, NULL, NULL, + NULL, d, &dLen); + } + if ((ret == 0) && + ((dLen != refDLen) || (memcmp(d, refD, dLen) != 0))) { + WH_ERROR_PRINT("Private-only ECC verify changed the scalar\n"); + ret = -1; + } + wc_ecc_free(privOnlyKey); + } + if (ret != 0) { + WH_ERROR_PRINT("Private-only ECC verify failed on curve %d: %d\n", + curves[i], ret); + } + } + + (void)wc_FreeRng(rng); + if (ret == 0) { + WH_TEST_PRINT("ECC PRIVATE-ONLY VERIFY SUCCESS\n"); + } + return ret; +} +#endif /* !NO_ECC_MAKE_PUB && !WOLF_CRYPTO_CB_ONLY_ECC */ + int whTest_Crypto_Ecc(whClientContext* ctx) { WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEcc(ctx)); @@ -2687,6 +2779,9 @@ int whTest_Crypto_Ecc(whClientContext* ctx) #if defined(HAVE_ECC_CHECK_KEY) && defined(WH_TEST_ECC_LOCAL_KEYGEN) WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEccCheckPubKey(ctx)); #endif /* HAVE_ECC_CHECK_KEY && WH_TEST_ECC_LOCAL_KEYGEN */ +#if !defined(NO_ECC_MAKE_PUB) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) + WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEccPrivOnlyVerify(ctx)); +#endif /* !NO_ECC_MAKE_PUB && !WOLF_CRYPTO_CB_ONLY_ECC */ #if !defined(WOLF_CRYPTO_CB_ONLY_ECC) WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEccCrossVerify(ctx)); WH_TEST_RETURN_ON_FAIL(_whTest_CryptoEccAsync(ctx)); diff --git a/test/config/user_settings.h b/test/config/user_settings.h index 669706220..22a0f0114 100644 --- a/test/config/user_settings.h +++ b/test/config/user_settings.h @@ -108,6 +108,8 @@ #define HAVE_ECC #define TFM_ECC256 #define ECC_SHAMIR +#define HAVE_ECC_KOBLITZ +#define WOLFSSL_CUSTOM_CURVES /** Curve25519 Options */ #define HAVE_CURVE25519