From 461899ee6f3a71626520bfe2049f15a3a1ecf2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 5 Nov 2026 18:22:39 +0100 Subject: [PATCH 1/2] Blind ECC private keys across the full order width With WOLFSSL_ECC_BLIND_K the private scalar is stored XORed with a random mask, and the XORs were dp->size bytes wide. On secp160r1, secp160r2, secp160k1 and secp224k1 the order has one bit more than the prime, so a valid scalar of 2^(8*size) or more lost its top bit when blinded at import or key generation. The key then acted as d - 2^(8*size): the public key, ECDH results and signatures were all wrong. Size the mask from ECC_KEY_MAX_BITS, which already includes the extra order bit. These curves are compiled out with FP_ECC, which --enable-all turns on, so the existing BLIND_K CI entry never reached them. Add an os-check entry with BLIND_K, FP_ECC off and a 160-bit minimum key size, and a test that uses d = n - 1, whose ECDH result with G must be Gx. Fixes F-16894. --- .github/configs/os-check-linux.json | 6 +- tests/api/test_ecc.c | 95 +++++++++++++++++++++++++++++ tests/api/test_ecc.h | 2 + wolfcrypt/src/ecc.c | 14 +++-- 4 files changed, 111 insertions(+), 6 deletions(-) diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index 2dcbdfba8d2..83ca118fc8e 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -39,9 +39,13 @@ "configure": ["--enable-all", "--enable-dtls13", "--enable-dtls-frag-ch", "--disable-mlkem"]}, {"name": "all-ecc-blind-k", "minutes": 8.0, - "comment": "Only entry that sets WOLFSSL_ECC_BLIND_K (the blind-private-key entry sets WOLFSSL_BLIND_PRIVATE_KEY, which does not imply it). Keeps the read-only wc_ecc_key_get_priv() contract exercised in CI. pkcs11 is on because wc_pkcs11.c is not compiled anywhere else in this matrix.", + "comment": "WOLFSSL_ECC_BLIND_K with FP_ECC (the blind-private-key entry sets WOLFSSL_BLIND_PRIVATE_KEY, which does not imply it). Keeps the read-only wc_ecc_key_get_priv() contract exercised in CI. pkcs11 is on because wc_pkcs11.c is not compiled anywhere else in this matrix.", "configure": ["--enable-all", "--enable-pkcs11", "CPPFLAGS=-DWOLFSSL_ECC_BLIND_K"]}, +{"name": "ecc-blind-k-small-curves", "minutes": 8.0, + "comment": "WOLFSSL_ECC_BLIND_K with the curves whose order is longer than the prime (secp160*, secp224k1), which FP_ECC compiles out.", + "configure": ["--enable-all", "--disable-fpecc", "--with-eccminsz=160", + "CPPFLAGS=-DWOLFSSL_ECC_BLIND_K"]}, {"name": "all-check-mem-zero", "minutes": 7.9, "configure": ["--enable-all", "CPPFLAGS=-DWOLFSSL_CHECK_MEM_ZERO"]}, {"name": "all-faultharden-pk-privkey", "minutes": 7.8, diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index b3936ec0545..0e6c92b6aba 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -711,6 +711,101 @@ int test_wc_ecc_shared_secret(void) return EXPECT_RESULT(); } /* END tests_wc_ecc_shared_secret */ +/* + * Testing the private key n - 1 on curves whose order has one bit more than + * the field. [n - 1]G is -G, so its x-ordinate is Gx. + */ +int test_wc_ecc_order_wider_than_field(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \ + defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \ + !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_VALIDATE_ECC_IMPORT) && \ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(HAVE_SELFTEST) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) + static const int curves[] = { + ECC_SECP160R1, ECC_SECP160R2, ECC_SECP160K1, ECC_SECP224K1 + }; + const ecc_set_type* dp = NULL; + ecc_key key; + ecc_key peer; + WC_RNG rng; + char d[2 * (MAX_ECC_BYTES + 1) + 1]; + byte secret[MAX_ECC_BYTES + 1]; + byte gx[MAX_ECC_BYTES + 1]; + byte gy[MAX_ECC_BYTES + 1]; + byte x[MAX_ECC_BYTES + 1]; + byte y[MAX_ECC_BYTES + 1]; + word32 secretSz; + word32 gxSz; + word32 gySz; + word32 xSz; + word32 ySz; + size_t len = 0; + int i; + + XMEMSET(&rng, 0, sizeof(rng)); + ExpectIntEQ(wc_InitRng(&rng), 0); + + for (i = 0; i < (int)(sizeof(curves) / sizeof(curves[0])); i++) { + if (wc_ecc_get_curve_idx(curves[i]) < 0) { + continue; + } + ExpectNotNull(dp = wc_ecc_get_curve_params( + wc_ecc_get_curve_idx(curves[i]))); + if (dp == NULL) { + break; + } + /* The order is prime, so n - 1 only changes the last hex digit. */ + if (dp != NULL) { + len = XSTRLEN(dp->order); + } + ExpectIntGT(len, 0); + ExpectIntLT(len, sizeof(d)); + if (EXPECT_SUCCESS()) { + XMEMCPY(d, dp->order, len + 1); + d[len - 1]--; + } + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&peer, 0, sizeof(peer)); + ExpectIntEQ(wc_ecc_init(&key), 0); + ExpectIntEQ(wc_ecc_init(&peer), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&peer, dp->Gx, dp->Gy, NULL, + curves[i]), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&key, dp->Gx, dp->Gy, d, curves[i]), + 0); + #ifdef ECC_TIMING_RESISTANT + ExpectIntEQ(wc_ecc_set_rng(&key, &rng), 0); + #endif + gxSz = (word32)sizeof(gx); + gySz = (word32)sizeof(gy); + ExpectIntEQ(wc_ecc_export_public_raw(&peer, gx, &gxSz, gy, &gySz), 0); + + secretSz = (word32)sizeof(secret); + ExpectIntEQ(wc_ecc_shared_secret(&key, &peer, secret, &secretSz), 0); + ExpectIntEQ(secretSz, gxSz); + ExpectBufEQ(secret, gx, gxSz); + + /* The public key made from n - 1 is -G: same x, other y. */ + ExpectIntEQ(wc_ecc_make_pub(&key, NULL), 0); + xSz = (word32)sizeof(x); + ySz = (word32)sizeof(y); + ExpectIntEQ(wc_ecc_export_public_raw(&key, x, &xSz, y, &ySz), 0); + ExpectIntEQ(xSz, gxSz); + ExpectBufEQ(x, gx, gxSz); + ExpectIntEQ(ySz, gySz); + ExpectBufNE(y, gy, gySz); + + wc_ecc_free(&peer); + wc_ecc_free(&key); + } + + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} /* END test_wc_ecc_order_wider_than_field */ + /* ECC_INF_E rejection is not present in the frozen ecc.c of older * FIPS-certified modules, so restrict to non-FIPS or FIPS v7 and later, and * skip the CAVP selftest build. The hardware ports listed here either do not diff --git a/tests/api/test_ecc.h b/tests/api/test_ecc.h index 17b463b2720..7e30235ae54 100644 --- a/tests/api/test_ecc.h +++ b/tests/api/test_ecc.h @@ -38,6 +38,7 @@ int test_wc_ecc_size(void); int test_wc_ecc_params(void); int test_wc_ecc_signVerify_hash(void); int test_wc_ecc_shared_secret(void); +int test_wc_ecc_order_wider_than_field(void); int test_wc_ecc_shared_secret_at_infinity(void); int test_wc_ecc_shared_secret_ex_peer_point(void); int test_wc_ecc_shared_secret_size_bounds(void); @@ -103,6 +104,7 @@ int test_wc_ecc_make_key_id_dev(void); TEST_DECL_GROUP("ecc", test_wc_ecc_params), \ TEST_DECL_GROUP("ecc", test_wc_ecc_signVerify_hash), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret), \ + TEST_DECL_GROUP("ecc", test_wc_ecc_order_wider_than_field), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_at_infinity), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ex_peer_point), \ TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_size_bounds), \ diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 312753f8008..8f93f1e46c7 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -383,9 +383,13 @@ ECC Curve Sizes: ((unsigned)((key)->dp->size * 8 + MAX_ECC_BITS_EXTRA))) #ifdef WOLFSSL_ECC_BLIND_K +/* Width of the XORs below. It covers the order, which can be a bit longer + * than the prime. */ +#define ECC_BLIND_K_BYTES(key) \ + ((int)((ECC_KEY_MAX_BITS_NONULLCHECK(key) + 7) / 8)) /* Number of digits covered by the fixed-width XORs below. */ #define ECC_BLIND_K_DIGITS(key) \ - ((int)(((key)->dp->size + sizeof(mp_digit) - 1) / sizeof(mp_digit))) + ((int)((ECC_BLIND_K_BYTES(key) + sizeof(mp_digit) - 1) / sizeof(mp_digit))) /* The XORs read this many whole digits regardless of each operand's current * length, so operands written at partial width (e.g. by mp_copy()) must be @@ -398,7 +402,7 @@ mp_int* ecc_get_k(ecc_key* key) mp_forcezero(key->ku); } else { - mp_xor_ct(key->k, key->kb, key->dp->size, key->ku); + mp_xor_ct(key->k, key->kb, ECC_BLIND_K_BYTES(key), key->ku); } return key->ku; } @@ -407,8 +411,8 @@ void ecc_blind_k(ecc_key* key, mp_int* b) if ((mp_grow(key->k, ECC_BLIND_K_DIGITS(key)) == MP_OKAY) && (mp_grow(key->kb, ECC_BLIND_K_DIGITS(key)) == MP_OKAY) && (mp_grow(b, ECC_BLIND_K_DIGITS(key)) == MP_OKAY)) { - mp_xor_ct(key->k, b, key->dp->size, key->k); - mp_xor_ct(key->kb, b, key->dp->size, key->kb); + mp_xor_ct(key->k, b, ECC_BLIND_K_BYTES(key), key->k); + mp_xor_ct(key->kb, b, ECC_BLIND_K_BYTES(key), key->kb); } } int ecc_blind_k_rng(ecc_key* key, WC_RNG* rng) @@ -433,7 +437,7 @@ int ecc_blind_k_rng(ecc_key* key, WC_RNG* rng) ret = mp_grow(key->k, ECC_BLIND_K_DIGITS(key)); } if (ret == 0) { - mp_xor_ct(key->k, key->kb, key->dp->size, key->k); + mp_xor_ct(key->k, key->kb, ECC_BLIND_K_BYTES(key), key->k); } else { /* No blind installed - keep the stored pair consistent. */ From ad39fa8a783c5c8021bd1cb2098ffd802e3ea22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 5 Nov 2026 18:42:07 +0100 Subject: [PATCH 2/2] Encode ECC private keys in DER at the size of the curve order The DER encoders wrote the private scalar at dp->size bytes. On secp160r1, secp160r2, secp160k1 and secp224k1 the order has one bit more than the prime, so a valid scalar of 2^(8*size) or more failed to encode with BUFFER_E. RFC 5915 encodes the private key at the size of the order, as OpenSSL does. Add wc_ecc_export_private_order(), an internal export that zero-pads the scalar to that size. With a NULL buffer it returns LENGTH_ONLY_E and the size it needs, the order size or the black-key size for CAAM, without reading the private key, so a key exported through a crypto callback need not hold it locally. The DER encoders use it. FIPS and selftest builds, whose ecc.c does not have it, keep wc_ecc_export_private_only() and dp->size, plus the MAC size for a QNX CAAM black key in asn_orig.c. The order size comes from the curve's order string, with no allocation or failure path. Private keys in DER on these curves are now one byte longer. wc_ecc_export_private_only(), wc_ecc_export_ex() and wc_ecc_export_private_raw() keep d at dp->size. They pair with wc_ecc_import_unsigned(), which reads d at that size with no length, so a wider d would silently load d >> 8, and a scalar that does not fit still fails to export through them. _ecc_export_ex() takes the size as a new argument, which the Microchip branch ignores since it cannot export d, and the ECC white-box test passes it. Importing a private key without a curve id picks the curve by size, and an order-size key, as the DER now holds, matched no curve. A size one byte longer than every curve now selects the curve whose order has that size. Without ASN.1, signatures are raw r || s at dp->size. An r or s of 2^(8*size) or more was truncated with the error ignored, and *outlen was set without checking it against the buffer. Check *outlen before signing, check the conversions so such a signature fails instead of being truncated, and set *outlen only when they succeed. Verification also accepts r and s at the size of the order, initializes r and s for asynchronous builds and checks their reads, and no longer leaks them on a wrong size. Crypto-callback-only builds, which never use the raw signature size, no longer declare it, so they build again without ASN.1. Add a test of both sizes to wolfcrypt/test, guard its explicit-curve decode tests so the NO_ASN build compiles, and add os-check entries for NO_ASN and for the original ASN.1 code with these curves. Fixes F-16898. --- .github/configs/os-check-linux.json | 9 ++ tests/api/test_ecc.c | 122 +++++++++++++++-- tests/unit-mcdc/test_ecc_whitebox.c | 6 +- wolfcrypt/src/asn.c | 15 +- wolfcrypt/src/asn_orig.c | 25 ++-- wolfcrypt/src/ecc.c | 205 ++++++++++++++++++++++------ wolfcrypt/test/test.c | 121 ++++++++++++++++ wolfssl/wolfcrypt/ecc.h | 2 + 8 files changed, 437 insertions(+), 68 deletions(-) diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index 83ca118fc8e..49220689a3d 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -295,6 +295,10 @@ {"name": "no-client-auth", "minutes": 1.8, "configure": ["CPPFLAGS=-DWOLFSSL_NO_CLIENT_AUTH"]}, {"name": "harden-tls", "minutes": 1.7, "configure": ["--enable-harden-tls"]}, +{"name": "ecc-asn-original-small-curves", "minutes": 1.7, + "comment": "Original ASN.1 code on the curves whose order is longer than the prime.", + "configure": ["--enable-asn=original", "--with-eccminsz=160", + "--enable-ecccustcurves=all"]}, {"name": "no-sni-ecc-tls13-scr-info", "minutes": 1.7, "configure": ["--disable-sni", "--disable-ecc", "--disable-tls13", "--disable-secure-renegotiation-info"]}, @@ -546,6 +550,11 @@ "comment": "RSA verify-only with ECC cofactor ECDH (HAVE_ECC_CDH, set by --enable-ecccustcurves=all), whose wc_ecc_shared_secret_gen_sync() calls mp_mul_d. SAKKE stays off, so the HAVE_ECC_CDH term in settings.h is the only reason sp_mul_d is built. This gives link-time coverage of that term only: the mp_mul_d call runs only when cofactor != 1, and no curve at or above ECC_MIN_KEY_SZ has another cofactor.", "configure": ["--enable-cryptonly", "--enable-rsavfy", "--enable-ecccustcurves=all", "--disable-examples"]}, +{"name": "ecc-no-asn-small-curves", "minutes": 0.5, + "comment": "Raw r||s ECDSA signatures (NO_ASN) on the curves whose order is longer than the prime.", + "configure": ["--enable-cryptonly", "--enable-asn=no", "--disable-rsa", + "--disable-dh", "--disable-fpecc", "--with-eccminsz=160", + "--enable-ecccustcurves=all"]}, {"name": "pkcs12-pbkdf-mp-api", "minutes": 1.0, "comment": "WC_PKCS12_PBKDF_USING_MP_API selects a second wc_PKCS12_PBKDF_ex implementation that no configure option reaches.", "configure": ["--enable-pkcs12", "--enable-des3", "--enable-opensslextra", diff --git a/tests/api/test_ecc.c b/tests/api/test_ecc.c index 0e6c92b6aba..f7e5cfa952a 100644 --- a/tests/api/test_ecc.c +++ b/tests/api/test_ecc.c @@ -711,6 +711,52 @@ int test_wc_ecc_shared_secret(void) return EXPECT_RESULT(); } /* END tests_wc_ecc_shared_secret */ +#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \ + defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \ + !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_VALIDATE_ECC_IMPORT) && \ + !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(HAVE_SELFTEST) && \ + !defined(HAVE_FIPS) && !defined(NO_ECC_MAKE_PUB) +#define TEST_ECC_ORDER_WIDER_THAN_FIELD +/* Load a private key from raw bytes or DER and check its ECDH result with + * peer is expected. */ +static int test_ecc_order_key_ecdh(const byte* priv, word32 privSz, + const byte* der, word32 derSz, ecc_key* peer, int curveId, WC_RNG* rng, + const byte* expected, word32 expectedSz) +{ + EXPECT_DECLS; + ecc_key key; + byte secret[MAX_ECC_ORDER_BYTES]; + word32 secretSz = (word32)sizeof(secret); + word32 idx = 0; + + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_ecc_init(&key), 0); + if (der == NULL) { + ExpectIntEQ(wc_ecc_import_private_key_ex(priv, privSz, NULL, 0, &key, + curveId), 0); + } +#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT) + else { + ExpectIntEQ(wc_EccPrivateKeyDecode(der, &idx, &key, derSz), 0); + } +#else + (void)derSz; + (void)idx; +#endif +#ifdef ECC_TIMING_RESISTANT + ExpectIntEQ(wc_ecc_set_rng(&key, rng), 0); +#else + (void)rng; +#endif + ExpectIntEQ(wc_ecc_shared_secret(&key, peer, secret, &secretSz), 0); + ExpectIntEQ(secretSz, expectedSz); + ExpectBufEQ(secret, expected, expectedSz); + wc_ecc_free(&key); + + return EXPECT_RESULT(); +} +#endif + /* * Testing the private key n - 1 on curves whose order has one bit more than * the field. [n - 1]G is -G, so its x-ordinate is Gx. @@ -718,11 +764,7 @@ int test_wc_ecc_shared_secret(void) int test_wc_ecc_order_wider_than_field(void) { EXPECT_DECLS; -#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \ - defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \ - !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_VALIDATE_ECC_IMPORT) && \ - !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(HAVE_SELFTEST) && \ - (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) +#ifdef TEST_ECC_ORDER_WIDER_THAN_FIELD static const int curves[] = { ECC_SECP160R1, ECC_SECP160R2, ECC_SECP160K1, ECC_SECP224K1 }; @@ -730,12 +772,18 @@ int test_wc_ecc_order_wider_than_field(void) ecc_key key; ecc_key peer; WC_RNG rng; - char d[2 * (MAX_ECC_BYTES + 1) + 1]; - byte secret[MAX_ECC_BYTES + 1]; - byte gx[MAX_ECC_BYTES + 1]; - byte gy[MAX_ECC_BYTES + 1]; - byte x[MAX_ECC_BYTES + 1]; - byte y[MAX_ECC_BYTES + 1]; + char d[2 * MAX_ECC_ORDER_BYTES + 1]; + byte secret[MAX_ECC_ORDER_BYTES]; + byte gx[MAX_ECC_ORDER_BYTES]; + byte gy[MAX_ECC_ORDER_BYTES]; + byte x[MAX_ECC_ORDER_BYTES]; + byte y[MAX_ECC_ORDER_BYTES]; + byte priv[MAX_ECC_ORDER_BYTES]; +#if !defined(NO_ASN) && !defined(NO_ASN_CRYPT) + byte der[ECC_BUFSIZE]; + int derSz = 0; +#endif + word32 privSz; word32 secretSz; word32 gxSz; word32 gySz; @@ -797,6 +845,58 @@ int test_wc_ecc_order_wider_than_field(void) ExpectIntEQ(ySz, gySz); ExpectBufNE(y, gy, gySz); + /* The raw export stays at the prime size, which n - 1 exceeds. */ + privSz = (word32)sizeof(priv); + ExpectIntEQ(wc_ecc_export_private_only(&key, priv, &privSz), + WC_NO_ERR_TRACE(BUFFER_E)); + + #if !defined(NO_ASN) && !defined(NO_ASN_CRYPT) + /* DER holds the private key at the size of the order. */ + ExpectIntGT(derSz = wc_EccKeyToDer(&key, der, (word32)sizeof(der)), 0); + ExpectIntEQ(wc_EccKeyDerSize(&key, 1), derSz); + ExpectIntEQ(test_ecc_order_key_ecdh(NULL, 0, der, (word32)derSz, + &peer, curves[i], &rng, gx, gxSz), TEST_SUCCESS); + ExpectIntGT(derSz = wc_EccPrivateKeyToDer(&key, der, + (word32)sizeof(der)), 0); + ExpectIntEQ(wc_EccKeyDerSize(&key, 0), derSz); + ExpectIntEQ(der[5], ASN_OCTET_STRING); + ExpectIntEQ(der[6], gxSz + 1); + ExpectIntEQ(test_ecc_order_key_ecdh(NULL, 0, der, (word32)derSz, + &peer, curves[i], &rng, gx, gxSz), TEST_SUCCESS); + /* Without a curve id the order size still selects the curve. */ + if ((curves[i] == ECC_SECP160R1) || (curves[i] == ECC_SECP224K1)) { + ExpectIntEQ(test_ecc_order_key_ecdh(der + 7, gxSz + 1, NULL, 0, + &peer, ECC_CURVE_DEF, &rng, gx, gxSz), TEST_SUCCESS); + } + ExpectIntLT(wc_EccPrivateKeyToDer(&key, der, (word32)derSz - 1), 0); + #endif + + /* A small key exports raw at the prime size, which + * wc_ecc_import_unsigned() reads, and in DER at the order size. */ + wc_ecc_free(&key); + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_ecc_init(&key), 0); + ExpectIntEQ(wc_ecc_import_raw_ex(&key, dp->Gx, dp->Gy, "01", + curves[i]), 0); + privSz = (word32)sizeof(priv); + ExpectIntEQ(wc_ecc_export_private_only(&key, priv, &privSz), 0); + ExpectIntEQ(privSz, gxSz); + ExpectIntEQ(priv[privSz - 1], 1); + ExpectIntEQ(test_ecc_order_key_ecdh(priv, privSz, NULL, 0, &peer, + curves[i], &rng, gx, gxSz), TEST_SUCCESS); + xSz = (word32)sizeof(x); + ySz = (word32)sizeof(y); + privSz = (word32)sizeof(priv); + ExpectIntEQ(wc_ecc_export_private_raw(&key, x, &xSz, y, &ySz, priv, + &privSz), 0); + ExpectIntEQ(privSz, gxSz); + ExpectIntEQ(priv[privSz - 1], 1); + #if !defined(NO_ASN) && !defined(NO_ASN_CRYPT) + ExpectIntGT(wc_EccPrivateKeyToDer(&key, der, (word32)sizeof(der)), 0); + ExpectIntEQ(der[6], gxSz + 1); + ExpectIntEQ(der[7], 0); + #endif + wc_ecc_free(&peer); wc_ecc_free(&key); } diff --git a/tests/unit-mcdc/test_ecc_whitebox.c b/tests/unit-mcdc/test_ecc_whitebox.c index c87c8bb6402..c128b66069c 100644 --- a/tests/unit-mcdc/test_ecc_whitebox.c +++ b/tests/unit-mcdc/test_ecc_whitebox.c @@ -1437,19 +1437,19 @@ static void wb_idx_dp_guard_export_paths(void) key.idx = 9999; key.dp = cs; qxLen = sizeof(qx); qyLen = sizeof(qy); ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL, - WC_TYPE_UNSIGNED_BIN); + WC_TYPE_UNSIGNED_BIN, 0); if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; } key.idx = idx; key.dp = NULL; qxLen = sizeof(qx); qyLen = sizeof(qy); ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL, - WC_TYPE_UNSIGNED_BIN); + WC_TYPE_UNSIGNED_BIN, 0); if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; } key.idx = idx; key.dp = cs; /* baseline: real export */ qxLen = sizeof(qx); qyLen = sizeof(qy); ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL, - WC_TYPE_UNSIGNED_BIN); + WC_TYPE_UNSIGNED_BIN, 0); if (ret != MP_OKAY) { wb_fail = 1; } /* ---- wc_ecc_export_public_raw: qx/qxLen/qy/qyLen NULL guard, diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 46090474893..1da2ecba673 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -35728,6 +35728,12 @@ int wc_EccPublicKeyDecode(const byte* input, word32* inOutIdx, } #endif /* WOLFSSL_ASN_TEMPLATE */ #ifdef HAVE_ECC_KEY_EXPORT +#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + /* The private key is encoded at the size of the order (RFC 5915). */ + #define EccExportPrivateDer wc_ecc_export_private_order +#else + #define EccExportPrivateDer wc_ecc_export_private_only +#endif /* build DER formatted ECC key, include optional public key if requested, * return length on success, negative on error */ #ifdef WOLFSSL_ASN_TEMPLATE @@ -35755,8 +35761,11 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *outLen, CALLOC_ASNSETDATA(dataASN, eccKeyASN_Length, ret, key->heap); if (ret == 0) { - /* Private key size is the curve size. */ - privSz = (word32)key->dp->size; + /* Private key size is reported by the export, else the curve size. */ + if (EccExportPrivateDer(key, NULL, &privSz) != + WC_NO_ERR_TRACE(LENGTH_ONLY_E)) { + privSz = (word32)key->dp->size; + } if (pubIn) { /* Get the length of the public key. */ PRIVATE_KEY_UNLOCK(); @@ -35830,7 +35839,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *outLen, /* Export the private value into the buffer. */ PRIVATE_KEY_UNLOCK(); /* safe cast -- the pointer is actually inside the output buffer. */ - ret = wc_ecc_export_private_only( + ret = EccExportPrivateDer( key, (byte*)(wc_ptr_t) dataASN[ECCKEYASN_IDX_PKEY].data.buffer.data, diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c index 088d38b3339..8cf6d5f9e09 100644 --- a/wolfcrypt/src/asn_orig.c +++ b/wolfcrypt/src/asn_orig.c @@ -8278,7 +8278,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen, int privHdrSz = ASN_ECC_HEADER_SZ; int pubHdrSz = ASN_ECC_CONTEXT_SZ + ASN_ECC_HEADER_SZ; #ifdef WOLFSSL_NO_MALLOC - byte prv[MAX_ECC_BYTES + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ]; + byte prv[MAX_ECC_BYTES + 1 + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ]; byte pub[(MAX_ECC_BYTES * 2) + 1 + ASN_ECC_CONTEXT_SZ + ASN_ECC_HEADER_SZ + MAX_SEQ_SZ]; #else @@ -8288,7 +8288,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen, word32 idx = 0, prvidx = 0, pubidx = 0, curveidx = 0; word32 seqSz, privSz, pubSz = ECC_BUFSIZE; - if (key == NULL || (output == NULL && inLen == NULL)) + if (key == NULL || key->dp == NULL || (output == NULL && inLen == NULL)) return BAD_FUNC_ARG; if (curveIn) { @@ -8303,15 +8303,18 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen, curveidx += (word32)curveSz; } - /* private */ - privSz = (word32)key->dp->size; - -#ifdef WOLFSSL_QNX_CAAM - /* check if is a black key, and add MAC size if needed */ - if (key->blackKey > 0 && key->blackKey != CAAM_BLACK_KEY_ECB) { - privSz = privSz + WC_CAAM_MAC_SZ; + /* private: the export reports its size, which is the size of the order or + * of a black key. */ + if (EccExportPrivateDer(key, NULL, &privSz) != + WC_NO_ERR_TRACE(LENGTH_ONLY_E)) { + privSz = (word32)key->dp->size; + #ifdef WOLFSSL_QNX_CAAM + /* check if is a black key, and add MAC size if needed */ + if (key->blackKey > 0 && key->blackKey != CAAM_BLACK_KEY_ECB) { + privSz = privSz + WC_CAAM_MAC_SZ; + } + #endif } -#endif #ifndef WOLFSSL_NO_MALLOC prv = (byte*)XMALLOC(privSz + (word32)privHdrSz + MAX_SEQ_SZ, @@ -8331,7 +8334,7 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen, prvidx += SetOctetString(privSz, &prv[prvidx]); } PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_export_private_only(key, prv + prvidx, &privSz); + ret = EccExportPrivateDer(key, prv + prvidx, &privSz); PRIVATE_KEY_LOCK(); if (ret < 0) { #ifndef WOLFSSL_NO_MALLOC diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 8f93f1e46c7..b292118f6de 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -6899,6 +6899,27 @@ static int wc_ecc_get_curve_order_bit_count(const ecc_set_type* dp) return orderBits; } +#if defined(HAVE_ECC_KEY_EXPORT) || defined(HAVE_ECC_KEY_IMPORT) || \ + (defined(NO_ASN) && defined(HAVE_ECC_VERIFY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_ECC)) +/* Size in bytes of a value modulo the order: the private key, r and s. */ +static word32 ecc_order_size(const ecc_set_type* dp) +{ + word32 sz = (word32)dp->size; + word32 i = 0; + word32 digits; + + while (dp->order[i] == '0') { + i++; + } + digits = (word32)XSTRLEN(dp->order + i); + if ((digits + 1) / 2 > sz) { + sz = (digits + 1) / 2; + } + return sz; +} +#endif + #ifdef HAVE_ECC_SIGN #if defined(WOLFSSL_ATECC508A) || defined(WOLFSSL_ATECC608A) || \ @@ -7209,7 +7230,7 @@ int wc_ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, DECL_MP_INT_SIZE_DYN(r, ECC_KEY_MAX_BITS(key), MAX_ECC_BITS_USE); DECL_MP_INT_SIZE_DYN(s, ECC_KEY_MAX_BITS(key), MAX_ECC_BITS_USE); #endif -#ifdef NO_ASN +#if defined(NO_ASN) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) word32 keySz; #endif @@ -7246,6 +7267,22 @@ int wc_ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, (void)err; return NO_VALID_DEVID; #else /* !WOLF_CRYPTO_CB_ONLY_ECC */ +#ifdef NO_ASN + /* No support for DSA ASN.1 header. + * Signature will be r+s directly. */ + keySz = 0; + if (key->dp != NULL) { + keySz = (word32)key->dp->size; + } + if (keySz == 0) { + WOLFSSL_MSG("Error: ECDSA sign raw signature size"); + return WC_NO_ERR_TRACE(ECC_BAD_ARG_E); + } + if (*outlen < keySz * 2) { + *outlen = keySz * 2; + return BUFFER_E; + } +#endif if (rng == NULL) { WOLFSSL_MSG("ECC sign RNG missing"); return ECC_BAD_ARG_E; @@ -7311,21 +7348,14 @@ int wc_ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, /* encoded with DSA header */ err = StoreECC_DSA_Sig(out, outlen, r, s); #else - /* No support for DSA ASN.1 header. - * Signature will be r+s directly. */ - keySz = 0; - if (key->dp != NULL) { - keySz = (word32)key->dp->size; + /* Export signature into r,s */ + err = mp_to_unsigned_bin_len(r, out, (int)keySz); + if (err == MP_OKAY) { + err = mp_to_unsigned_bin_len(s, out + keySz, (int)keySz); } - if (keySz <= 0) { - WOLFSSL_MSG("Error: ECDSA sign raw signature size"); - return WC_NO_ERR_TRACE(ECC_BAD_ARG_E); + if (err == MP_OKAY) { + *outlen = keySz * 2; } - *outlen = keySz * 2; - - /* Export signature into r,s */ - mp_to_unsigned_bin_len(r, out, keySz); - mp_to_unsigned_bin_len(s, out + keySz, keySz); #endif /* !NO_ASN */ /* cleanup */ @@ -9466,7 +9496,7 @@ int wc_ecc_verify_hash(const byte* sig, word32 siglen, const byte* hash, #ifdef WOLFSSL_ASYNC_CRYPT int isPrivateKeyOnly = 0; #endif -#ifdef NO_ASN +#if defined(NO_ASN) && !defined(WOLF_CRYPTO_CB_ONLY_ECC) word32 keySz; #endif @@ -9568,17 +9598,31 @@ int wc_ecc_verify_hash(const byte* sig, word32 siglen, const byte* hash, keySz = 0; if (key->dp != NULL) { keySz = (word32)key->dp->size; + /* Also accept r and s at the size of the order. */ + if (siglen == ecc_order_size(key->dp) * 2) { + keySz = siglen / 2; + } } - if (siglen != keySz * 2) { + if ((keySz == 0) || (siglen != keySz * 2)) { WOLFSSL_MSG("Error: ECDSA Verify raw signature size"); - return WC_NO_ERR_TRACE(ECC_BAD_ARG_E); + err = WC_NO_ERR_TRACE(ECC_BAD_ARG_E); + break; } /* Import signature into r,s */ - mp_init(r); - mp_init(s); - mp_read_unsigned_bin(r, sig, keySz); - mp_read_unsigned_bin(s, sig + keySz, keySz); + #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC) + err = mp_init_multi(r, s, NULL, NULL, NULL, NULL); + if (err != MP_OKAY) { + break; + } + #endif + err = mp_read_unsigned_bin(r, sig, keySz); + if (err == MP_OKAY) { + err = mp_read_unsigned_bin(s, sig + keySz, keySz); + } + if (err != MP_OKAY) { + break; + } #endif /* !NO_ASN */ FALL_THROUGH; @@ -12276,7 +12320,7 @@ int wc_ecc_import_x963(const byte* in, word32 inLen, ecc_key* key) * This internal helper avoids recursion when called from the EXPORT_KEY path. */ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, byte* qy, word32* qyLen, byte* d, word32* dLen, - int encType) + int encType, int dOrderSz) { int err = 0; word32 keySz; @@ -12299,6 +12343,7 @@ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, #if defined(WOLFSSL_ATECC508A) || defined(WOLFSSL_ATECC608A) || \ defined(WOLFSSL_MICROCHIP_TA100) /* Hardware cannot export private portion */ + (void)dOrderSz; return NOT_COMPILED_IN; #else #if defined(WOLFSSL_SECO_CAAM) @@ -12338,7 +12383,8 @@ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, else #endif { - err = wc_export_int(ecc_get_k(key), d, dLen, keySz, encType); + err = wc_export_int(ecc_get_k(key), d, dLen, + dOrderSz ? ecc_order_size(key->dp) : keySz, encType); ecc_put_k(key); if (err != MP_OKAY) return err; @@ -12370,11 +12416,10 @@ static int _ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, } -/* export ecc key to component form, d is optional if only exporting public - * encType is WC_TYPE_UNSIGNED_BIN or WC_TYPE_HEX_STR - * return MP_OKAY on success */ -int wc_ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, - byte* qy, word32* qyLen, byte* d, word32* dLen, int encType) +/* Export with d at the size of the order when dOrderSz is set. */ +static int ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, + byte* qy, word32* qyLen, byte* d, word32* dLen, int encType, + int dOrderSz) { #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_EXPORT_KEY) int err; @@ -12407,7 +12452,7 @@ int wc_ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, if (err == 0) { /* Call software helper (no callback recursion) */ err = _ecc_export_ex(tmpKey, qx, qxLen, qy, qyLen, d, dLen, - encType); + encType, dOrderSz); } wc_ecc_free(tmpKey); @@ -12419,15 +12464,27 @@ int wc_ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, } #endif /* WOLF_CRYPTO_CB && WOLF_CRYPTO_CB_EXPORT_KEY */ - return _ecc_export_ex(key, qx, qxLen, qy, qyLen, d, dLen, encType); + return _ecc_export_ex(key, qx, qxLen, qy, qyLen, d, dLen, encType, + dOrderSz); } -/* export ecc private key only raw, outLen is in/out size as unsigned bin - return MP_OKAY on success */ -WOLFSSL_ABI -int wc_ecc_export_private_only(ecc_key* key, byte* out, word32* outLen) +/* export ecc key to component form, d is optional if only exporting public + * encType is WC_TYPE_UNSIGNED_BIN or WC_TYPE_HEX_STR + * return MP_OKAY on success */ +int wc_ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, + byte* qy, word32* qyLen, byte* d, word32* dLen, int encType) { - if (out == NULL || outLen == NULL) { + return ecc_export_ex(key, qx, qxLen, qy, qyLen, d, dLen, encType, 0); +} + +/* Export the private key only, with d at the size of the order when dOrderSz + * is set. */ +static int ecc_export_private(ecc_key* key, byte* out, word32* outLen, + int dOrderSz) +{ + int encType = WC_TYPE_UNSIGNED_BIN; + + if (key == NULL) { return BAD_FUNC_ARG; } @@ -12435,13 +12492,57 @@ int wc_ecc_export_private_only(ecc_key* key, byte* out, word32* outLen) /* check if black key in secure memory */ if ((key->blackKey != CAAM_BLACK_KEY_CCM && key->blackKey != CAAM_BLACK_KEY_ECB) && key->blackKey > 0) { - return wc_ecc_export_ex(key, NULL, NULL, NULL, NULL, out, outLen, - WC_TYPE_BLACK_KEY); + encType = WC_TYPE_BLACK_KEY; } #endif - return wc_ecc_export_ex(key, NULL, NULL, NULL, NULL, out, outLen, - WC_TYPE_UNSIGNED_BIN); + return ecc_export_ex(key, NULL, NULL, NULL, NULL, out, outLen, encType, + dOrderSz); +} + +/* export ecc private key only raw, outLen is in/out size as unsigned bin + return MP_OKAY on success */ +WOLFSSL_ABI +int wc_ecc_export_private_only(ecc_key* key, byte* out, word32* outLen) +{ + if (out == NULL || outLen == NULL) { + return BAD_FUNC_ARG; + } + + return ecc_export_private(key, out, outLen, 0); +} + +/* Export the private key zero-padded to the size of the order, as RFC 5915 + * encodes it. A NULL out returns LENGTH_ONLY_E with the size needed. */ +int wc_ecc_export_private_order(ecc_key* key, byte* out, word32* outLen) +{ + if ((key == NULL) || (outLen == NULL)) { + return BAD_FUNC_ARG; + } + + if (out == NULL) { + if ((key->dp == NULL) || (wc_ecc_is_valid_idx(key->idx) == 0)) { + return ECC_BAD_ARG_E; + } + #if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_EXPORT_KEY) + if (key->devId == INVALID_DEVID) + #endif + { + if ((key->type != ECC_PRIVATEKEY) && + (key->type != ECC_PRIVATEKEY_ONLY)) { + return BAD_FUNC_ARG; + } + } + *outLen = ecc_order_size(key->dp); + #if defined(WOLFSSL_QNX_CAAM) || defined(WOLFSSL_IMXRT1170_CAAM) + if ((key->blackKey > 0) && (key->blackKey != CAAM_BLACK_KEY_ECB)) { + *outLen = (word32)key->dp->size + WC_CAAM_MAC_SZ; + } + #endif + return WC_NO_ERR_TRACE(LENGTH_ONLY_E); + } + + return ecc_export_private(key, out, outLen, 1); } /* export public key to raw elements including public (Qx,Qy) as unsigned bin @@ -12538,6 +12639,29 @@ static int ecc_check_privkey_range(ecc_key* key) return ret; } +/* Without a curve id, a private key whose size no curve has, but which is the + * order size of a curve one byte shorter, is taken as that curve's. */ +static int ecc_priv_curve_id(word32 privSz, int curve_id) +{ + int x; + + if (curve_id > ECC_CURVE_DEF) { + return curve_id; + } + for (x = 0; ecc_sets[x].size != 0; x++) { + if ((word32)ecc_sets[x].size == privSz) { + return curve_id; + } + } + for (x = 0; ecc_sets[x].size != 0; x++) { + if (((word32)ecc_sets[x].size + 1 == privSz) && + (ecc_order_size(&ecc_sets[x]) == privSz)) { + return ecc_sets[x].id; + } + } + return curve_id; +} + /* Software-only import of private key, public part optional. * This internal helper avoids recursion when called from the SETKEY path. */ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz, @@ -12569,7 +12693,8 @@ static int _ecc_import_private_key_ex(const byte* priv, word32 privSz, /* set key size */ /* NOTE: FIPS v6.0.0 or greater, no restriction on imported keys, only * on created keys or signatures */ - ret = wc_ecc_set_curve(key, (int)privSz, curve_id); + ret = wc_ecc_set_curve(key, (int)privSz, + ecc_priv_curve_id(privSz, curve_id)); key->type = ECC_PRIVATEKEY_ONLY; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 75f2d90b683..32b91711b4c 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -56217,6 +56217,7 @@ static wc_test_ret_t ecc_decode_test(void) #endif /* WOLFSSL_CERT_EXT */ #ifdef WOLFSSL_CUSTOM_CURVES +#ifndef NO_ASN static const byte eccKeyExplicitCurve[] = { 0x30, 0x81, 0xf5, 0x30, 0x81, 0xae, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x30, @@ -56286,11 +56287,14 @@ static const byte eccKeyExplicitCurveAlgIdLenLong[] = { 0x38, 0x96, 0x29, 0x7d, 0x9c, 0xe1, 0x50, 0xbe, 0xac, 0xf0, 0x1d, 0x86, 0xf4, 0x2f, 0x65, 0x0b }; +#endif /* !NO_ASN */ static wc_test_ret_t ecc_test_custom_curves(WC_RNG* rng) { wc_test_ret_t ret; +#ifndef NO_ASN word32 inOutIdx; +#endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) ecc_key *key = (ecc_key *)XMALLOC(sizeof *key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); #else @@ -56369,6 +56373,7 @@ static wc_test_ret_t ecc_test_custom_curves(WC_RNG* rng) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); +#ifndef NO_ASN inOutIdx = 0; ret = wc_EccPublicKeyDecode(eccKeyExplicitCurve, &inOutIdx, key, sizeof(eccKeyExplicitCurve)); @@ -56387,6 +56392,7 @@ static wc_test_ret_t ecc_test_custom_curves(WC_RNG* rng) ret = WC_TEST_RET_ENC_EC(ret); goto done; } +#endif /* !NO_ASN */ ret = 0; done: @@ -58659,6 +58665,110 @@ static wc_test_ret_t ecc_test_neg_rs(WC_RNG* rng) } #endif +#if defined(NO_ASN) && defined(HAVE_ECC_SIGN) && defined(HAVE_ECC_VERIFY) && \ + (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 160 && !defined(NO_ECC_SECP) && !defined(FP_ECC) && \ + !defined(WOLFSSL_SP_MATH) && !defined(WC_NO_RNG) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) +/* Raw r || s signatures on secp160r1, whose order is a byte longer than the + * prime: written at the size of the prime, read at either size. */ +static wc_test_ret_t ecc_test_raw_sig_size(WC_RNG* rng) +{ + wc_test_ret_t ret; + WC_DECLARE_VAR(key, ecc_key, 1, HEAP_HINT); + byte hash[WC_MAX_DIGEST_SIZE]; + byte sig[2 * 20]; + byte wideSig[2 * 21]; + word32 sigSz; + int verify = 0; + int keyInit = 0; + + WC_ALLOC_VAR(key, ecc_key, 1, HEAP_HINT); + if (!WC_VAR_OK(key)) + return WC_TEST_RET_ENC_EC(MEMORY_E); + XMEMSET(hash, 0x5a, sizeof(hash)); + + ret = wc_ecc_init_ex(key, HEAP_HINT, devId); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + keyInit = 1; + ret = wc_ecc_make_key_ex(rng, 20, key, ECC_SECP160R1); +#if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_NONE); +#endif +#ifdef WOLF_CRYPTO_CB_ONLY_ECC + if (ret == WC_NO_ERR_TRACE(NO_VALID_DEVID)) { + ret = 0; + goto done; /* no software case */ + } +#endif + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); +#ifdef ECC_TIMING_RESISTANT + ret = wc_ecc_set_rng(key, rng); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); +#endif + + sigSz = (word32)sizeof(sig) - 1; + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigSz, rng, key); + if ((ret != WC_NO_ERR_TRACE(BUFFER_E)) || (sigSz != sizeof(sig))) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + + sigSz = (word32)sizeof(sig); + ret = 0; + do { + #if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN); + #endif + if (ret == 0) + ret = wc_ecc_sign_hash(hash, sizeof(hash), sig, &sigSz, rng, key); + } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E)); + if ((ret != 0) || (sigSz != sizeof(sig))) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + ret = 0; + do { + #if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN); + #endif + if (ret == 0) + ret = wc_ecc_verify_hash(sig, sigSz, hash, sizeof(hash), &verify, + key); + } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E)); + if ((ret != 0) || (verify != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, done); + + /* r and s at the size of the order. */ + XMEMSET(wideSig, 0, sizeof(wideSig)); + XMEMCPY(wideSig + 1, sig, 20); + XMEMCPY(wideSig + 22, sig + 20, 20); + verify = 0; + ret = 0; + do { + #if defined(WOLFSSL_ASYNC_CRYPT) + ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN); + #endif + if (ret == 0) + ret = wc_ecc_verify_hash(wideSig, sizeof(wideSig), hash, + sizeof(hash), &verify, key); + } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E)); + if ((ret != 0) || (verify != 1)) + ERROR_OUT(WC_TEST_RET_ENC_NC, done); + + ret = wc_ecc_verify_hash(sig, sizeof(sig) - 1, hash, sizeof(hash), + &verify, key); + if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + ret = 0; + +done: + if (keyInit) + wc_ecc_free(key); + WC_FREE_VAR(key, HEAP_HINT); + return ret; +} +#endif + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void) { wc_test_ret_t ret; @@ -58720,6 +58830,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ecc_test(void) goto done; } #endif +#if defined(NO_ASN) && defined(HAVE_ECC_SIGN) && defined(HAVE_ECC_VERIFY) && \ + (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 160 && !defined(NO_ECC_SECP) && !defined(FP_ECC) && \ + !defined(WOLFSSL_SP_MATH) && !defined(WC_NO_RNG) && !defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST) + ret = ecc_test_raw_sig_size(&rng); + if (ret != 0) { + printf("Raw signature size\n"); + goto done; + } +#endif #if defined(WOLFSSL_CUSTOM_CURVES) && defined(WOLFSSL_ASN_TEMPLATE) && \ defined(HAVE_ECC_KEY_IMPORT) && !defined(WOLFSSL_NO_MALLOC) && \ diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index bd69a436cba..df67e620f65 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -1038,6 +1038,8 @@ int wc_ecc_export_ex(ecc_key* key, byte* qx, word32* qxLen, int encType); WOLFSSL_ABI WOLFSSL_API int wc_ecc_export_private_only(ecc_key* key, byte* out, word32* outLen); +WOLFSSL_LOCAL +int wc_ecc_export_private_order(ecc_key* key, byte* out, word32* outLen); WOLFSSL_API int wc_ecc_export_public_raw(ecc_key* key, byte* qx, word32* qxLen, byte* qy, word32* qyLen);