Skip to content

Commit 7c0c87a

Browse files
committed
SecurityReview FND 40.2 + 36.1 + 6.4 + 10.1 + 15.1 + 26.7 + 11.3: integrity, PCT, zeroize, CMAC/SHAKE/AES-KW CASTs, DH PCT + configurable DRBG_SHA512_SEED_LEN
Seven findings from the v7.0.0 security review, squashed into one commit per the Part3 branch invariant. FND 40.2 (in-core integrity HMAC upgraded to SHA-512) - wolfssl/wolfcrypt/fips_test.h: add v7+ branch that selects SHA-512 / 64-byte digest / 512-bit key / 64-byte verify-size. Older versions (v5.3, v6.x) keep HMAC-SHA-256. - fips-hash.sh: drop the hardcoded cut -c1-64 so the script works for both the 32-byte (v5.3 / v6.x) and 64-byte (v7+) digests. FND 36.1 (SLH-DSA PCT per FIPS 140-3 IG 10.3.B) wolfcrypt/src/wc_slhdsa.c wc_SlhDsaKey_MakeKey: after key generation performs a sign+verify pairwise consistency test on a fixed prehashed message, returning SLH_DSA_PCT_E on failure. Companion fips.c DEGRADE_STATE entry is in the kh-fork-fips commit. FND 6.4 (AES-GCM zeroization on the assembly fast path) wolfcrypt/src/aes.c AES_GCM_decrypt_C: zero the partial block before the early return when the auth-tag check fails, not after. FND 10.1 (AES-CMAC dedicated CAST: companion error code) wolfssl/wolfcrypt/error-crypt.h: add CMAC_KAT_FIPS_E = -1020 with a comment citing the IG 10.3.A authenticated-mode allowance and the vendor-elected enhancement rationale. wolfcrypt/src/error.c: description string for CMAC_KAT_FIPS_E. FND 15.1 (SHAKE dedicated CAST: companion error code) wolfssl/wolfcrypt/error-crypt.h: add SHAKE_KAT_FIPS_E = -1021 with the IG 10.3.B comment. wolfcrypt/src/error.c: description string for SHAKE_KAT_FIPS_E. FND 26.7 (DH KeyGen PCT per SP 800-56A r3 sec 5.6.2.1.4) wolfcrypt/src/dh.c wc_DhGenerateKeyPair: under HAVE_FIPS, after the underlying make-key returns the function regenerates the public key from the private key via the math primitives and ConstantCompare's it against the supplied public, returning DH_PCT_E on mismatch. wolfssl/wolfcrypt/error-crypt.h: add DH_PCT_E = -1022 with comment citing SP 800-56A r3 sec 5.6.2.1.4 / FIPS 140-3 IG 10.3.B. wolfcrypt/src/error.c: description string for DH_PCT_E. FND 11.3 (AES-KW dedicated CAST per SP 800-38F sec 6.2 / RFC 3394) wolfssl/wolfcrypt/fips_test.h: add FIPS_CAST_AES_KW = 28 and bump FIPS_CAST_COUNT to 29. wolfssl/wolfcrypt/error-crypt.h: add AES_KW_KAT_FIPS_E = -1023 with comment citing SP 800-38F sec 6.2 / RFC 3394 (vendor-elected enhancement). WC_SPAN2_LAST_E / WC_LAST_E updated. wolfcrypt/src/error.c: description string for AES_KW_KAT_FIPS_E. Companion changes (kh-fork-fips): - fips_test.c: AesKw_KnownAnswerTest helper; new DoCAST case FIPS_CAST_AES_KW with RFC 3394 sec 4.6 (AES-256 KEK / 256-bit plaintext) vector exercising the full SP 800-38F sec 6.2 wrap composition (6n=24 iteration counter, semiblock concatenation, 0xA6A6A6A6A6A6A6A6 IV check). - fips.c: wc_AesKeyWrap_fips/_ex_fips/_UnWrap_fips/_UnWrap_ex_fips refactored to gate on FIPS_CAST_AES_KW; CastIdToStr entry; DEGRADE_STATE on AES_KW_KAT_FIPS_E (only the KW CAST is degraded - underlying AES CASTs remain independent); explicit AES-KW entry in wc_RunAllCast_fips alongside AES-CBC/GCM/ECB. AES-KWP (SP 800-38F sec 6.3 internal padding) is intentionally out of scope - the module does not implement padding for any AES mode and requires inputs to be 8-byte aligned at the boundary. This is a voluntary enhancement exceeding the IG 10.3.A minimum. Companion paperwork updates (tracked changes) live in Final_Submission_Paperwork/PL-R34-...-Security-Policy.docx and PL-R36-...-Compliance-Summary.docx (CAST count 28 -> 29, AES-KW coverage paragraph, no-padding policy clarification). DRBG_SHA512_SEED_LEN configurability (carried forward from the prior Part3 commit): wolfssl/wolfcrypt/random.h exposes the seed-length constant as a configurable macro for OE-specific entropy claims. Verified: POST passes, make check passes (5 pass, 3 skip, 0 fail) with the default --enable-fips=v7 configure, AND with the CI-representative configure (9 pass, 3 skip, 0 fail).
1 parent 1c9555c commit 7c0c87a

8 files changed

Lines changed: 138 additions & 12 deletions

File tree

fips-hash.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ then
1313
fi
1414

1515
OUT=$(./wolfcrypt/test/testwolfcrypt | sed -n 's/hash = \(.*\)/\1/p')
16-
NEWHASH=$(echo "$OUT" | cut -c1-64)
16+
# FIPS v7.0.0+ uses HMAC-SHA-512 (128 hex chars); older FIPS versions
17+
# use HMAC-SHA-256 (64 hex chars). Take the whole captured hash; the
18+
# static_assert on sizeof(verifyCore) guards against wrong length at
19+
# compile time after this script runs.
20+
NEWHASH=$(echo "$OUT" | head -n1 | tr -d '[:space:]')
1721
if test -n "$NEWHASH"
1822
then
1923
cp wolfcrypt/src/fips_test.c wolfcrypt/src/fips_test.c.bak

wolfcrypt/src/aes.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10962,6 +10962,16 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
1096210962

1096310963
VECTOR_REGISTERS_POP;
1096410964

10965+
/* FIPS 140-3 / SP 800-38D: on authentication failure, the decrypted-but-
10966+
* unauthenticated plaintext in `out` must not be released to the caller.
10967+
* Wipe it here so a caller that ignores the return value cannot observe
10968+
* plaintext derived from forged ciphertext. All software paths (AES-NI,
10969+
* AVX1/2, ARM HW/NEON, C fallback) funnel through `ret` here, so this
10970+
* single guard covers every sub-implementation. */
10971+
if (ret == WC_NO_ERR_TRACE(AES_GCM_AUTH_E) && out != NULL && sz > 0) {
10972+
ForceZero(out, sz);
10973+
}
10974+
1096510975
return ret;
1096610976
}
1096710977
#endif
@@ -12665,6 +12675,10 @@ int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz)
1266512675
}
1266612676
}
1266712677

12678+
/* Streaming decrypt cannot zeroize prior Update output buffers from here
12679+
* (Final does not see them). On AES_GCM_AUTH_E, the caller is responsible
12680+
* for treating all Update-produced plaintext as invalid and wiping it.
12681+
* See PL-R34 Security Policy section 8 (Operational Rules). */
1266812682
return ret;
1266912683
}
1267012684
#endif /* HAVE_AES_DECRYPT || HAVE_AESGCM_DECRYPT */

wolfcrypt/src/dh.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,8 +1400,20 @@ int wc_DhGeneratePublic(DhKey* key, byte* priv, word32 privSz,
14001400
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
14011401
if (ret == 0)
14021402
ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
1403-
if (ret == 0)
1404-
ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, privSz);
1403+
if (ret == 0) {
1404+
/* Pairwise Consistency Test per SP 800-56A r3 sec 5.6.2.1.4
1405+
* (FFC key pair). FIPS 140-3 IG 10.3.B requires a PCT after
1406+
* KeyGen for key-establishment algorithms; on failure under a
1407+
* FIPS build the error is remapped to DH_PCT_E so the FIPS
1408+
* module's DEGRADE_STATE handler transitions FIPS_CAST_DH_
1409+
* PRIMITIVE_Z to the error state. */
1410+
ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv,
1411+
privSz);
1412+
#ifdef HAVE_FIPS
1413+
if (ret != 0)
1414+
ret = DH_PCT_E;
1415+
#endif
1416+
}
14051417
#endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */
14061418

14071419
RESTORE_VECTOR_REGISTERS();
@@ -1428,8 +1440,20 @@ static int wc_DhGenerateKeyPair_Sync(DhKey* key, WC_RNG* rng,
14281440
#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_DH_KEYGEN)
14291441
if (ret == 0)
14301442
ret = _ffc_validate_public_key(key, pub, *pubSz, NULL, 0, 0);
1431-
if (ret == 0)
1432-
ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv, *privSz);
1443+
if (ret == 0) {
1444+
/* Pairwise Consistency Test per SP 800-56A r3 sec 5.6.2.1.4
1445+
* (FFC key pair). FIPS 140-3 IG 10.3.B requires a PCT after
1446+
* KeyGen for key-establishment algorithms; on failure under a
1447+
* FIPS build the error is remapped to DH_PCT_E so the FIPS
1448+
* module's DEGRADE_STATE handler transitions FIPS_CAST_DH_
1449+
* PRIMITIVE_Z to the error state. */
1450+
ret = _ffc_pairwise_consistency_test(key, pub, *pubSz, priv,
1451+
*privSz);
1452+
#ifdef HAVE_FIPS
1453+
if (ret != 0)
1454+
ret = DH_PCT_E;
1455+
#endif
1456+
}
14331457
#endif /* FIPS V5 or later || WOLFSSL_VALIDATE_DH_KEYGEN */
14341458

14351459

wolfcrypt/src/error.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,21 @@ const char* wc_GetErrorString(int error)
692692
case SLH_DSA_KAT_FIPS_E:
693693
return "SLH-DSA Known Answer Test check FIPS error";
694694

695+
case SLH_DSA_PCT_E:
696+
return "wolfcrypt SLH-DSA Pairwise Consistency Test Failure";
697+
698+
case CMAC_KAT_FIPS_E:
699+
return "AES-CMAC Known Answer Test FIPS error";
700+
701+
case SHAKE_KAT_FIPS_E:
702+
return "SHAKE Known Answer Test FIPS error";
703+
704+
case DH_PCT_E:
705+
return "wolfcrypt DH (FFC) Pairwise Consistency Test Failure";
706+
707+
case AES_KW_KAT_FIPS_E:
708+
return "AES-KW Known Answer Test FIPS error";
709+
695710
case SEQ_OVERFLOW_E:
696711
return "Sequence counter would overflow";
697712

wolfcrypt/src/wc_slhdsa.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6681,6 +6681,45 @@ int wc_SlhDsaKey_MakeKey(SlhDsaKey* key, WC_RNG* rng)
66816681
key->sk + 2 * n, n);
66826682
}
66836683

6684+
#ifdef HAVE_FIPS
6685+
/* Pairwise Consistency Test (PCT) per FIPS 140-3 IG 10.3.A (TE10.35.02):
6686+
* sign with the new sk, verify with the matching pk. SLH-DSA is a
6687+
* stateless hash-based signature scheme (FIPS 205), so the relaxed PCT
6688+
* rule for stateful HBS (LMS/XMSS) does not apply -- PCT runs on every
6689+
* KeyGen. SignDeterministic avoids consuming RNG state; heap allocation
6690+
* is used because SLH-DSA signatures can reach ~50 KB. */
6691+
if (ret == 0) {
6692+
static const byte pct_msg[] = "wolfSSL SLH-DSA PCT";
6693+
byte* pct_sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL,
6694+
DYNAMIC_TYPE_TMP_BUFFER);
6695+
word32 pct_sigSz = WC_SLHDSA_MAX_SIG_LEN;
6696+
6697+
if (pct_sig == NULL) {
6698+
ret = MEMORY_E;
6699+
}
6700+
if (ret == 0) {
6701+
ret = wc_SlhDsaKey_SignDeterministic(key, NULL, 0,
6702+
pct_msg, sizeof(pct_msg), pct_sig, &pct_sigSz);
6703+
}
6704+
if (ret == 0) {
6705+
ret = wc_SlhDsaKey_Verify(key, NULL, 0,
6706+
pct_msg, sizeof(pct_msg), pct_sig, pct_sigSz);
6707+
if (ret != 0) {
6708+
ret = SLH_DSA_PCT_E;
6709+
}
6710+
}
6711+
if (pct_sig != NULL) {
6712+
ForceZero(pct_sig, WC_SLHDSA_MAX_SIG_LEN);
6713+
XFREE(pct_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
6714+
}
6715+
/* IG 10.3.A (TE10.35.02): a key pair that fails the PCT must be
6716+
* rendered unusable. */
6717+
if (ret != 0) {
6718+
wc_SlhDsaKey_Free(key);
6719+
}
6720+
}
6721+
#endif /* HAVE_FIPS */
6722+
66846723
return ret;
66856724
}
66866725

wolfssl/wolfcrypt/error-crypt.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,17 @@ enum wolfCrypt_ErrorCodes {
327327
ML_DSA_PCT_E = -1016, /* ML-DSA Pairwise Consistency Test failure */
328328
DRBG_SHA512_KAT_FIPS_E = -1017, /* SHA-512 DRBG KAT failure */
329329
SLH_DSA_KAT_FIPS_E = -1018, /* SLH-DSA CAST KAT failure */
330-
331-
WC_SPAN2_LAST_E = -1018, /* Update to indicate last used error code */
332-
WC_LAST_E = -1018, /* the last code used either here or in
330+
SLH_DSA_PCT_E = -1019, /* SLH-DSA Pairwise Consistency Test failure */
331+
CMAC_KAT_FIPS_E = -1020, /* AES-CMAC KAT failure (vendor-elected) */
332+
SHAKE_KAT_FIPS_E = -1021, /* SHAKE KAT failure (vendor-elected) */
333+
DH_PCT_E = -1022, /* DH (FFC) Pairwise Consistency Test
334+
* failure (SP 800-56A r3 sec 5.6.2.1.4,
335+
* FIPS 140-3 IG 10.3.B) */
336+
AES_KW_KAT_FIPS_E = -1023, /* AES-KW KAT failure (vendor-elected,
337+
* SP 800-38F sec 6.2 / RFC 3394) */
338+
339+
WC_SPAN2_LAST_E = -1023, /* Update to indicate last used error code */
340+
WC_LAST_E = -1023, /* the last code used either here or in
333341
* error-ssl.h */
334342

335343
WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */

wolfssl/wolfcrypt/fips_test.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,23 @@
3131
extern "C" {
3232
#endif
3333

34-
/* Added for FIPS v5.3 or later */
35-
#if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)
34+
/* Added for FIPS v5.3 or later.
35+
*
36+
* v7.0.0 and later upgrade the in-core integrity HMAC to SHA-512 (with a
37+
* 512-bit key) for NSA 2.0 compliance. Customers that must avoid SHA-256
38+
* anywhere in the validated module can therefore use the v7 module without
39+
* residual SHA-256 integrity material. v5.3 and v6.x retain HMAC-SHA-256.
40+
*/
41+
#if defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(7,0)
42+
#ifdef WOLFSSL_SHA512
43+
#define FIPS_IN_CORE_DIGEST_SIZE 64
44+
#define FIPS_IN_CORE_HASH_TYPE WC_SHA512
45+
#define FIPS_IN_CORE_KEY_SZ 64
46+
#define FIPS_IN_CORE_VERIFY_SZ FIPS_IN_CORE_KEY_SZ
47+
#else
48+
#error FIPS v7+ integrity test requires WOLFSSL_SHA512
49+
#endif
50+
#elif defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)
3651
/* Determine FIPS in core hash type and size */
3752
#ifndef NO_SHA256
3853
#define FIPS_IN_CORE_DIGEST_SIZE 32
@@ -80,7 +95,10 @@ enum FipsCastId {
8095
FIPS_CAST_XMSS = 23,
8196
FIPS_CAST_DRBG_SHA512 = 24,
8297
FIPS_CAST_SLH_DSA = 25,
83-
FIPS_CAST_COUNT = 26
98+
FIPS_CAST_AES_CMAC = 26,
99+
FIPS_CAST_SHAKE = 27,
100+
FIPS_CAST_AES_KW = 28,
101+
FIPS_CAST_COUNT = 29
84102
};
85103

86104
enum FipsCastStateId {

wolfssl/wolfcrypt/random.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@
5757
#define DRBG_SEED_LEN (440/8)
5858
#endif
5959

60+
/* Size of the DRBG seed (SHA-512) */
6061
#ifdef WOLFSSL_DRBG_SHA512
61-
#define DRBG_SHA512_SEED_LEN (888/8) /* 111 bytes per SP 800-90A Table 2 */
62+
#ifndef DRBG_SHA512_SEED_LEN
63+
#define DRBG_SHA512_SEED_LEN (888/8) /* 111 bytes per SP 800-90A
64+
* Table 2 */
65+
#endif
6266
#endif
6367

6468

0 commit comments

Comments
 (0)