Skip to content

Commit 9af5352

Browse files
committed
SecurityReview FND 40.2 + 36.1 + 6.4 + 10.1 + 15.1: integrity, PCT, zeroize, CMAC/SHAKE CASTs
Five 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 SHA-512 (128 hex chars) as well as SHA-256. Companion fips changes update verifyCore placeholder, coreKey, and static_assert on sizeof(verifyCore). PL-R34 section 5.1 tracked- changes edit says HMAC-SHA2-512 with a 512-bit key. FND 36.1 (SLH-DSA KeyGen now runs a Pairwise Consistency Test) - wolfssl/wolfcrypt/error-crypt.h: add SLH_DSA_PCT_E = -1013. - wolfcrypt/src/error.c: description string. - wolfcrypt/src/wc_slhdsa.c wc_SlhDsaKey_MakeKey: sign with SignDeterministic + verify under HAVE_FIPS. Heap-allocated sig buffer (SLH-DSA sigs can be ~50 KB), ForceZero + free on failure. Companion fips: DEGRADE_STATE handler + optest case_1013. FND 6.4 (AES-GCM decrypt zeroizes output on authentication failure) - wolfcrypt/src/aes.c wc_AesGcmDecrypt: ForceZero(out, sz) after VECTOR_REGISTERS_POP when ret == AES_GCM_AUTH_E. All software sub-implementations funnel through that ret. - wc_AesGcmDecryptFinal: comment pointing callers at PL-R34 §2.7 operational rule on streaming API. PL-R34 §2.7 tracked-changes paragraph documents the new rule. FND 10.1 (AES-CMAC vendor-elected dedicated CAST) The v7.0.0 module now performs a dedicated AES-CMAC KAT at POST. FIPS 140-3 IG 10.3.A permits a single authenticated-mode KAT (AES-GCM, the "more complex" composition) to cover AES-CMAC as well and the prior v6.0.0 validation was approved on that basis. For v7.0.0 the vendor determined that the CMAC subkey-derivation path (K1/K2 via GF(2^128) doubling, final-block padding) is structurally distinct from GHASH and wished to exercise it with a dedicated CAST for additional assurance. Voluntary enhancement exceeding the IG 10.3.A minimum. - wolfssl/wolfcrypt/fips_test.h: FIPS_CAST_AES_CMAC = 26. - wolfssl/wolfcrypt/error-crypt.h: CMAC_KAT_FIPS_E = -1014. - wolfcrypt/src/error.c: description string. Companion fips: AesCmac_KnownAnswerTest (SP 800-38B D.1 empty-msg KAT), FIPS_CAST_AES_CMAC case in DoCAST, DEGRADE_STATE handler, CastIdToStr entry, optest case_1014. PL-R36 tracked-changes paragraph documents the vendor-elected framing. FND 15.1 (SHAKE vendor-elected dedicated CAST) SHAKE was an approved algorithm in the v6.0.0 module; self-testing was covered by the SHA3-256 CAST (embedded in HMAC-SHA3-256 CAST) under IG 10.3.B via the shared Keccak-f[1600] permutation. For v7.0.0 the vendor has elected to refactor SHAKE self-testing into a dedicated FIPS_CAST_SHAKE that exercises SHAKE's distinct 0x1F domain separator (vs SHA3's 0x06) and arbitrary-length squeeze. A single CAST identifier covers both SHAKE-128 and SHAKE-256 to minimize code footprint. Voluntary enhancement exceeding the IG 10.3.B minimum. - wolfssl/wolfcrypt/fips_test.h: FIPS_CAST_SHAKE = 27, bump FIPS_CAST_COUNT = 28. - wolfssl/wolfcrypt/error-crypt.h: SHAKE_KAT_FIPS_E = -1015. - wolfcrypt/src/error.c: description string. Companion fips: Shake_KnownAnswerTest (variant + msg + outLen + expected parameters), FIPS_CAST_SHAKE case exercising SHAKE-128 and SHAKE-256 with ACVTS vectors (vsId 2836391 tcId 1 for SHAKE-128, vsId 2836392 tcId 1 for SHAKE-256), DEGRADE_STATE handler, CastIdToStr entry, optest case_1015. All 8 SHAKE wrappers in fips.c (Shake128/256 Update/Final/Absorb/SqueezeBlocks) now gate on FIPS_CAST_SHAKE and return SHAKE_KAT_FIPS_E on failure; a block comment above the SHAKE wrapper section records the v6->v7 vendor-elected refactor rationale. PL-R36 tracked-changes paragraph documents the framing and the updated CAST tally (28 total: 26 baseline + 2 vendor-elected). Verified: make + fips-hash.sh + make; make check all pass (5 pass, 3 skip, 0 fail) with default configure, AND with the CI-representative configure: --enable-fips=ready --enable-opensslall --enable-opensslextra --enable-crl --enable-harden --enable-asio --enable-certreq --enable-certgen --enable-certext --enable-aesni CFLAGS="-DHAVE_EX_DATA -DOPENSSL_COMPATIBLE_DEFAULTS -DWOLFSSL_ALT_NAMES -DWC_RNG_SEED_CB -DWOLFSSL_CUSTOM_OID -DWOLFSSL_FPKI -DASN_TEMPLATE_SKIP_ISCA_CHECK" (9 pass, 3 skip, 0 fail).
1 parent 13d5cd9 commit 9af5352

6 files changed

Lines changed: 93 additions & 7 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
@@ -10959,6 +10959,16 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
1095910959

1096010960
VECTOR_REGISTERS_POP;
1096110961

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

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

wolfcrypt/src/error.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,15 @@ 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+
695704
case SEQ_OVERFLOW_E:
696705
return "Sequence counter would overflow";
697706

wolfcrypt/src/wc_slhdsa.c

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

6573+
#ifdef HAVE_FIPS
6574+
/* Pairwise Consistency Test (PCT) per FIPS 140-3 IG 10.3.A (TE10.35.02):
6575+
* sign with the new sk, verify with the matching pk. SLH-DSA is a
6576+
* stateless hash-based signature scheme (FIPS 205), so the relaxed PCT
6577+
* rule for stateful HBS (LMS/XMSS) does not apply -- PCT runs on every
6578+
* KeyGen. SignDeterministic avoids consuming RNG state; heap allocation
6579+
* is used because SLH-DSA signatures can reach ~50 KB. */
6580+
if (ret == 0) {
6581+
static const byte pct_msg[] = "wolfSSL SLH-DSA PCT";
6582+
byte* pct_sig = (byte*)XMALLOC(WC_SLHDSA_MAX_SIG_LEN, NULL,
6583+
DYNAMIC_TYPE_TMP_BUFFER);
6584+
word32 pct_sigSz = WC_SLHDSA_MAX_SIG_LEN;
6585+
6586+
if (pct_sig == NULL) {
6587+
ret = MEMORY_E;
6588+
}
6589+
if (ret == 0) {
6590+
ret = wc_SlhDsaKey_SignDeterministic(key, NULL, 0,
6591+
pct_msg, sizeof(pct_msg), pct_sig, &pct_sigSz);
6592+
}
6593+
if (ret == 0) {
6594+
ret = wc_SlhDsaKey_Verify(key, NULL, 0,
6595+
pct_msg, sizeof(pct_msg), pct_sig, pct_sigSz);
6596+
if (ret != 0) {
6597+
ret = SLH_DSA_PCT_E;
6598+
}
6599+
}
6600+
if (pct_sig != NULL) {
6601+
ForceZero(pct_sig, WC_SLHDSA_MAX_SIG_LEN);
6602+
XFREE(pct_sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
6603+
}
6604+
/* IG 10.3.A (TE10.35.02): a key pair that fails the PCT must be
6605+
* rendered unusable. */
6606+
if (ret != 0) {
6607+
wc_SlhDsaKey_Free(key);
6608+
}
6609+
}
6610+
#endif /* HAVE_FIPS */
6611+
65736612
return ret;
65746613
}
65756614

wolfssl/wolfcrypt/error-crypt.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,12 @@ enum wolfCrypt_ErrorCodes {
319319
DRBG_SHA512_KAT_FIPS_E = -1010, /* SHA-512 DRBG KAT failure */
320320

321321
SEQ_OVERFLOW_E = -1011, /* Sequence counter would overflow */
322-
SLH_DSA_KAT_FIPS_E = -1012, /* SLH-DSA CAST KAT failure */
323-
WC_SPAN2_LAST_E = -1012, /* Update to indicate last used error code */
324-
WC_LAST_E = -1012, /* the last code used either here or in
322+
SLH_DSA_KAT_FIPS_E = -1012, /* SLH-DSA CAST KAT failure */
323+
SLH_DSA_PCT_E = -1013, /* SLH-DSA Pairwise Consistency Test failure */
324+
CMAC_KAT_FIPS_E = -1014, /* AES-CMAC KAT failure (vendor-elected) */
325+
SHAKE_KAT_FIPS_E = -1015, /* SHAKE KAT failure (vendor-elected) */
326+
WC_SPAN2_LAST_E = -1015, /* Update to indicate last used error code */
327+
WC_LAST_E = -1015, /* the last code used either here or in
325328
* error-ssl.h */
326329

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

wolfssl/wolfcrypt/fips_test.h

Lines changed: 20 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,9 @@ 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_COUNT = 28
84101
};
85102

86103
enum FipsCastStateId {

0 commit comments

Comments
 (0)