Skip to content

Commit 2527a37

Browse files
author
Emma Stensland
committed
Fix ML-DSA missing public key guards and memory allocation, add ECC pubkey derivation logs
1 parent 4c68523 commit 2527a37

9 files changed

Lines changed: 1090 additions & 191 deletions

File tree

doc/dox_comments/header_files/wc_mldsa.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,25 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng);
257257
*/
258258
int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed);
259259

260+
/*!
261+
\ingroup ML_DSA
262+
263+
\brief Derives and caches the public key (t1 and the public seed)
264+
for a wc_MlDsaKey that already has its private key set, e.g. after
265+
wc_MlDsaKey_ImportPrivRaw() or decoding a private-key-only DER. A
266+
no-op returning 0 if the public key is already set.
267+
268+
\return 0 on success, or if the public key was already set.
269+
\return BAD_FUNC_ARG if key is NULL or the private key is not set.
270+
\return MEMORY_E on allocation failure.
271+
272+
\param [in,out] key Pointer to a wc_MlDsaKey with the private key set.
273+
274+
\sa wc_MlDsaKey_ImportPrivRaw
275+
\sa wc_MlDsaKey_MakeKey
276+
*/
277+
int wc_MlDsaKey_MakePublicKey(wc_MlDsaKey* key);
278+
260279
/*!
261280
\ingroup ML_DSA
262281

tests/api/test_asn.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,3 +2481,81 @@ int test_wc_AsnFeatureCoverage(void)
24812481
#endif /* !NO_ASN && HAVE_ECC && USE_CERT_BUFFERS_256 && !HAVE_FIPS */
24822482
return EXPECT_RESULT();
24832483
}
2484+
2485+
/* wc_EccPrivateKeyDecode should derive and cache the public point (best
2486+
* effort) when it decodes a SEC1 private key whose optional public point
2487+
* was omitted, so the key comes out fully usable. */
2488+
int test_wc_EccPrivateKeyDecode_derive_pub(void)
2489+
{
2490+
EXPECT_DECLS;
2491+
#if !defined(NO_ASN) && defined(HAVE_ECC) && !defined(NO_ECC_MAKE_PUB) && \
2492+
defined(USE_CERT_BUFFERS_256) && !defined(HAVE_FIPS) && \
2493+
!defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
2494+
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_CRYPTOCELL) && \
2495+
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLFSSL_KCAPI_ECC) && \
2496+
!defined(WOLFSSL_QNX_CAAM) && !defined(WOLFSSL_IMXRT1170_CAAM)
2497+
ecc_key fullKey;
2498+
ecc_key privOnlyKey;
2499+
WC_RNG rng;
2500+
word32 idx;
2501+
byte privOnlyDer[256];
2502+
int privOnlyDerSz;
2503+
byte fullPub[256];
2504+
word32 fullPubSz = sizeof(fullPub);
2505+
byte derivedPub[256];
2506+
word32 derivedPubSz = sizeof(derivedPub);
2507+
2508+
XMEMSET(&fullKey, 0, sizeof(fullKey));
2509+
XMEMSET(&privOnlyKey, 0, sizeof(privOnlyKey));
2510+
2511+
ExpectIntEQ(wc_InitRng(&rng), 0);
2512+
2513+
ExpectIntEQ(wc_ecc_init(&fullKey), 0);
2514+
idx = 0;
2515+
ExpectIntEQ(wc_EccPrivateKeyDecode(ecc_clikey_der_256, &idx, &fullKey,
2516+
sizeof_ecc_clikey_der_256), 0);
2517+
ExpectIntEQ(fullKey.type, ECC_PRIVATEKEY);
2518+
PRIVATE_KEY_UNLOCK();
2519+
ExpectIntEQ(wc_ecc_export_x963(&fullKey, fullPub, &fullPubSz), 0);
2520+
PRIVATE_KEY_LOCK();
2521+
2522+
/* Re-encode as a private-key-only SEC1 DER (no public point). */
2523+
ExpectIntGT(privOnlyDerSz = wc_EccPrivateKeyToDer(&fullKey, privOnlyDer,
2524+
sizeof(privOnlyDer)), 0);
2525+
2526+
#ifdef ECC_TIMING_RESISTANT
2527+
/* Without an RNG set, decode must not derive unblinded: the key is
2528+
* left as ECC_PRIVATEKEY_ONLY, same as before auto-derivation existed. */
2529+
ExpectIntEQ(wc_ecc_init(&privOnlyKey), 0);
2530+
idx = 0;
2531+
ExpectIntEQ(wc_EccPrivateKeyDecode(privOnlyDer, &idx, &privOnlyKey,
2532+
(word32)privOnlyDerSz), 0);
2533+
ExpectIntEQ(privOnlyKey.type, ECC_PRIVATEKEY_ONLY);
2534+
wc_ecc_free(&privOnlyKey);
2535+
#endif
2536+
2537+
/* Opting into blinding (as every other blinded ECC op in this library
2538+
* requires) makes decode derive and cache the public point. */
2539+
ExpectIntEQ(wc_ecc_init(&privOnlyKey), 0);
2540+
ExpectIntEQ(wc_ecc_set_rng(&privOnlyKey, &rng), 0);
2541+
idx = 0;
2542+
ExpectIntEQ(wc_EccPrivateKeyDecode(privOnlyDer, &idx, &privOnlyKey,
2543+
(word32)privOnlyDerSz), 0);
2544+
2545+
/* The public point should have been derived automatically, making the
2546+
* key fully usable rather than left as ECC_PRIVATEKEY_ONLY. */
2547+
ExpectIntEQ(privOnlyKey.type, ECC_PRIVATEKEY);
2548+
PRIVATE_KEY_UNLOCK();
2549+
ExpectIntEQ(wc_ecc_export_x963(&privOnlyKey, derivedPub, &derivedPubSz),
2550+
0);
2551+
PRIVATE_KEY_LOCK();
2552+
ExpectIntEQ(derivedPubSz, fullPubSz);
2553+
ExpectBufEQ(derivedPub, fullPub, fullPubSz);
2554+
2555+
wc_ecc_free(&privOnlyKey);
2556+
wc_ecc_free(&fullKey);
2557+
wc_FreeRng(&rng);
2558+
#endif /* !NO_ASN && HAVE_ECC && !NO_ECC_MAKE_PUB && USE_CERT_BUFFERS_256 &&
2559+
* !HAVE_FIPS */
2560+
return EXPECT_RESULT();
2561+
}

tests/api/test_asn.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ int test_ToTraditional_ex_negative(void);
4444
int test_ToTraditional_ex_mldsa_bad_params(void);
4545
int test_wc_AsnDecisionCoverage(void);
4646
int test_wc_AsnFeatureCoverage(void);
47+
int test_wc_EccPrivateKeyDecode_derive_pub(void);
4748

4849
#define TEST_ASN_DECLS \
4950
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
@@ -65,6 +66,7 @@ int test_wc_AsnFeatureCoverage(void);
6566
TEST_DECL_GROUP("asn", test_ToTraditional_ex_negative), \
6667
TEST_DECL_GROUP("asn", test_ToTraditional_ex_mldsa_bad_params), \
6768
TEST_DECL_GROUP("asn", test_wc_AsnDecisionCoverage), \
68-
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage)
69+
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage), \
70+
TEST_DECL_GROUP("asn", test_wc_EccPrivateKeyDecode_derive_pub)
6971

7072
#endif /* WOLFCRYPT_TEST_ASN_H */

0 commit comments

Comments
 (0)