Trim FIPS comments and share one ML-DSA seed entry point

pull/11303/head
kaleb-himes 2026-08-31 16:57:13 -06:00
parent b5d90b9396
commit 4308349c01
6 changed files with 67 additions and 81 deletions

View File

@ -488,6 +488,13 @@ int test_wolfSSL_RSA_print(void)
return EXPECT_RESULT();
}
int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void)
{
EXPECT_DECLS;
#ifndef NO_RSA
#if defined(OPENSSL_ALL) && defined(WC_RSA_PSS) && !defined(WC_NO_RNG)
#if !defined(HAVE_FIPS) || (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION>2))
/* These ask for the longest salt the modulus allows: 222 bytes here, once
* long salts are compiled in. FIPS 186-5 sec 5.4(g) caps the salt at the
* hash length, so a v7 module must refuse it and a success is the defect.
@ -498,13 +505,6 @@ int test_wolfSSL_RSA_print(void)
#else
#define TEST_PSS_MAX_SALT_RESULT 1
#endif
int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void)
{
EXPECT_DECLS;
#ifndef NO_RSA
#if defined(OPENSSL_ALL) && defined(WC_RSA_PSS) && !defined(WC_NO_RNG)
#if !defined(HAVE_FIPS) || (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION>2))
RSA *rsa = NULL;
const unsigned char *derBuf = client_key_der_2048;
unsigned char em[256] = {0}; /* len = 2048/8 */
@ -570,6 +570,7 @@ int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void)
RSA_PSS_SALTLEN_MAX), TEST_PSS_MAX_SALT_RESULT);
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em,
RSA_PSS_SALTLEN_MAX), TEST_PSS_MAX_SALT_RESULT);
#undef TEST_PSS_MAX_SALT_RESULT
ExpectIntEQ(RSA_padding_add_PKCS1_PSS(rsa, em, mHash, EVP_sha256(), 10), 1);
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em, 10), 1);
@ -580,7 +581,6 @@ int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void)
#endif
return EXPECT_RESULT();
}
#undef TEST_PSS_MAX_SALT_RESULT
int test_wolfSSL_RSA_sign_sha3(void)
{

View File

@ -20,11 +20,10 @@
*/
/* Times the module's self-tests so an operator can budget start-up on slow
* hardware. Two kinds, costing very different amounts:
* per-algorithm known-answer tests, run once each (FIPS 140-3 IG 10.3.A)
* -p key-pair tests, run on EVERY key generation
* (ISO/IEC 19790:2012 sec 7.10.3.3)
*/
* hardware. Two kinds:
* known-answer tests, once per algorithm (FIPS 140-3 IG 10.3.A)
* -p key-pair tests, on EVERY key generation
* (ISO/IEC 19790:2012 sec 7.10.3.3) */
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -192,17 +191,13 @@ static int run_one_cast(int id, int iters,
}
/* Pairwise consistency tests.
*
* A CAST runs once at start-up. This test runs on every key generation, so
* the application keeps paying it and none of it shows in the CAST numbers.
/* Pairwise consistency tests. A CAST runs once at start-up; these run on
* every key generation, so none of the cost shows in the CAST numbers.
* ISO/IEC 19790:2012 sec 7.10.3.3.
*
* KeyGen+PCT what a caller pays today. MEASURED.
* PCT alone the same test repeated on the finished key. MEASURED.
* KeyGen raw the difference. DERIVED: no build generates a key without
* the test, so it cannot be measured directly.
*/
* KeyGen raw the difference. DERIVED: no build skips the test. */
#define BENCH_PCT_DEFAULT_ITERS 1
@ -599,14 +594,10 @@ static int bench_pct_slhdsa(int iters)
double a = a_s / (double)iters * 1000.0;
double c = c_s / (double)iters * 1000.0;
/* The elected test must be the cheap one. If someone puts sign
* and verify back inside key generation, KeyGen+PCT swallows it
* and stops being the smaller number.
*
* Demand a 2x margin rather than a bare comparison: the elected
* option measures 3.8x to 13.8x cheaper, while a reverted one
* costs about 1.1x sign+verify, so 2x separates them with room
* for a loaded machine to move both numbers. */
/* The elected test must stay the cheap one. A 2x margin, not a
* bare comparison: elected measures 3.8x to 13.8x cheaper and a
* reverted one costs about 1.1x sign+verify, so 2x separates
* them even on a loaded machine. */
if (a > (kg * 2.0)) {
printf("%-15s | %10.3f | %8.3f | %7.4f | %8.1fx\n",
names[t], kg, a, c, (kg > 0.0) ? (a / kg) : 0.0);

View File

@ -159,15 +159,11 @@ static void wc_RsaCleanup(RsaKey* key)
#ifndef WOLFSSL_RSA_PUBLIC_ONLY
#if FIPS_VERSION3_GE(7,0,0)
/* Erase the recovered plaintext on the way out, success or failure.
* SP 800-56B Rev2 sec 7.2.2.4.
*
* Only a buffer we allocated: when the caller supplies its own,
* key->data points at it (dataIsAlloc is 0) and erasing would destroy
* the answer. No key->type test: it only ever holds RSA_PRIVATE,
* RSA_PUBLIC or RSA_TYPE_UNKNOWN, never RSA_PRIVATE_DECRYPT (3) or
* RSA_PRIVATE_ENCRYPT (2), which belong to the operation-type family
* sharing that enum (rsa.h:176-183). Comparing against them is
* always false, so the buffer was being freed unwiped. */
* SP 800-56B Rev2 sec 7.2.2.4. Only a buffer we allocated: a
* caller-supplied one is the answer itself. No key->type test:
* it never holds RSA_PRIVATE_DECRYPT/ENCRYPT, which belong to the
* operation-type half of that enum (rsa.h:176-183), so the old test
* was always false and the buffer was freed unwiped. */
if (key->dataIsAlloc && key->data != NULL && key->dataLen > 0) {
ForceZero(key->data, key->dataLen);
}
@ -945,8 +941,9 @@ int wc_CheckRsaKey(RsaKey* key)
}
/* Primes: right size, coprime to e, far enough apart, and actually
* prime (steps 5a to 5g). p alone first, then p with q so the
* |p - q| separation can be tested. */
* prime (steps 5a to 5g). Two calls because steps 5f/5g want a
* primality test on each prime: the first tests p, the second tests
* q and the |p - q| separation. */
if (ret == 0) {
ret = _CheckProbablePrime(&key->p, NULL, &key->e, nBits, &isPrime,
rng);
@ -986,12 +983,10 @@ int wc_CheckRsaKey(RsaKey* key)
/* Check dP, dQ and u if they exist */
if (ret == 0 && !mp_iszero(&key->dP)) {
#if FIPS_VERSION3_GE(7,0,0)
/* Guarded on the version alone, unlike the block above: this needs
* no WOLFSSL_KEY_GEN because it calls no key-generation helper.
*
* Each CRT component must be greater than 1.
* SP 800-56B Rev2 sec 6.4.1.4.3 item F, steps 7a/7b/7c.
* Their upper bounds are checked just below. */
/* Each CRT component must be greater than 1; upper bounds are
* checked just below. SP 800-56B Rev2 sec 6.4.1.4.3 item F, steps
* 7a/7b/7c. No WOLFSSL_KEY_GEN in the guard: unlike the block
* above this calls no key-generation helper. */
if ((mp_cmp_d(&key->dP, 1) != MP_GT) ||
(mp_cmp_d(&key->dQ, 1) != MP_GT) ||
(mp_cmp_d(&key->u, 1) != MP_GT)) {

View File

@ -9120,8 +9120,7 @@ static int mldsa_make_key_from_seed(wc_MlDsaKey* key, const byte* seed)
key-pair test required by ISO/IEC 19790:2012 sec 7.10.3.3"
#endif
/* Test every new key pair by signing and verifying with it.
* ISO/IEC 19790:2012 sec 7.10.3.3. Called from both generation paths.
* Fixed rnd because the decode path has no RNG.
* ISO/IEC 19790:2012 sec 7.10.3.3. Fixed rnd: no RNG on this path.
*
* @param [in, out] key ML-DSA key pair to test. Freed on failure.
* @return 0 on success.
@ -11290,7 +11289,9 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng)
}
/* No key-pair test here: wc_MlDsaKey_MakeKeyFromSeed(), reached from
* mldsa_make_key() above, already runs it on every generation path. */
* mldsa_make_key() above, already runs it on every generation path.
* Guarded on the version, not HAVE_FIPS: src/include.am only compiles
* this file under BUILD_FIPS_V7_PLUS, so the two are equivalent here. */
return ret;
}
@ -11302,37 +11303,45 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng)
* @return 0 on success.
* @return BAD_FUNC_ARG when key or seed is NULL.
* @return BAD_STATE_E when the parameters have not been set.
* @return ML_DSA_PCT_E when the key pair fails its consistency test. The
* key is freed in that case and must be re-initialised before reuse.
* @return ML_DSA_PCT_E on a failed test; the key is freed and must be
* re-initialised before reuse.
*/
int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed)
/* Expand a seed into a key pair, validating arguments first.
*
* runPct is 0 for the ASN.1 decode path: FIPS 140-3 IG 10.3.A Additional
* Comment 1 does not require a key-pair test on a key imported from outside
* the module.
*/
static int mldsa_key_from_seed_checked(wc_MlDsaKey* key, const byte* seed,
int runPct)
{
int ret = 0;
/* Validate parameters. */
if ((key == NULL) || (seed == NULL)) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Check the level or parameters have been set. */
if (key->params == NULL) {
ret = BAD_STATE_E;
}
else {
/* Make the key. */
ret = mldsa_make_key_from_seed(key, seed);
}
else if (key->params == NULL) {
ret = BAD_STATE_E;
}
else {
ret = mldsa_make_key_from_seed(key, seed);
}
#if FIPS_VERSION3_GE(7,0,0)
if (ret == 0) {
if ((ret == 0) && runPct) {
ret = mldsa_pct(key);
}
#else
(void)runPct;
#endif
return ret;
}
int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed)
{
return mldsa_key_from_seed_checked(key, seed, 1);
}
#endif
#ifndef WOLFSSL_MLDSA_NO_SIGN
@ -13441,18 +13450,8 @@ int wc_MlDsaKey_PrivateKeyDecode(wc_MlDsaKey* key, const byte* input,
if (seedLen != 0) {
#if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY)
if (seedLen == MLDSA_SEED_SZ) {
/* No key-pair test: FIPS 140-3 IG 10.3.A Additional Comment 1
* does not require one for a key imported from outside the
* module. */
if (seed == NULL) {
ret = BAD_FUNC_ARG;
}
else if (key->params == NULL) {
ret = BAD_STATE_E;
}
else {
ret = mldsa_make_key_from_seed(key, seed);
}
/* runPct 0: this is an import, not a generation. */
ret = mldsa_key_from_seed_checked(key, seed, 0);
}
else {
ret = ASN_PARSE_E;

View File

@ -708,7 +708,9 @@ int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng)
}
/* No key-pair test here: wc_MlKemKey_MakeKeyWithRandom(), called above,
* already runs it on every generation path. */
* already runs it on every generation path. Guarded on the version, not
* HAVE_FIPS: src/include.am only compiles this file under
* BUILD_FIPS_V7_PLUS, so the two are equivalent here. */
/* Ensure seeds are zeroized. */
ForceZero((void*)rand, (word32)sizeof(rand));

View File

@ -7133,11 +7133,10 @@ int wc_SlhDsaKey_MakeKeyWithRandom(SlhDsaKey* key, const byte* sk_seed,
/* Test every new key pair. ISO/IEC 19790:2012 sec 7.10.3.3. Here
* because every generation path reaches this function.
*
* The PK.SEED check is the one FIPS 140-3 IG 10.3.A Additional Comment 1
* names for SLH-DSA, but it always passes here: the public key is a
* slice of the private one. So the root recompute does the real work.
* It is the cheapest check that can fail; signing and verifying costs
* about ten times the key generation. */
* PK.SEED is the check FIPS 140-3 IG 10.3.A Additional Comment 1 names,
* but it always passes here: the public key is a slice of the private
* one. So the root recompute does the real work, at a tenth the cost of
* signing and verifying. */
if (ret == 0) {
byte n = key->params->n;
byte pct_root[SLHDSA_MAX_N];