diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index c4cb0986d7a..f01e692eba5 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -13,6 +13,13 @@ {"name": "all-public-mp", "minutes": 8.0, "comment": "--enable-all plus WOLFSSL_PUBLIC_MP. The shared-secret-at-infinity regression assertion needs direct mp access to plant an out-of-range scalar, because the import path itself now rejects one; without this the assertion is compiled out of every mainstream build.", "configure": ["--enable-all", "CPPFLAGS=-DWOLFSSL_PUBLIC_MP"]}, +{"name": "disable-memory-falcon", "minutes": 0.8, + "comment": "--disable-memory omits memory.c, which used to hold wc_ForceZero(); Falcon needs it.", + "configure": ["--disable-memory", "--enable-falcon", "--enable-experimental"]}, +{"name": "user-settings-wolfcrypt-chacha-min", "minutes": 0.3, + "comment": "Minimal WOLFCRYPT_ONLY build that settings.h used to give WOLFSSL_NO_FORCE_ZERO/NO_CONST_CMP, so it failed to link: SHA-1 needs ForceZero(), ChaCha20-Poly1305 ConstantCompare(). testwolfcrypt runs both.", + "configure": ["--enable-usersettings", "--disable-examples"], "check": false, "run": [["wolfcrypt/test/testwolfcrypt"]], + "prepare": [["sh", "-c", "printf '#define %s\\n' WOLFCRYPT_ONLY NO_AES WC_NO_RNG WC_NO_HASHDRBG NO_SHA256 NO_BIG_INT NO_RSA NO_DH NO_DSA NO_ASN NO_CODING NO_PWDBASED NO_HMAC NO_DES3 NO_MD5 NO_OLD_TLS WC_NO_RSA_OAEP HAVE_CHACHA HAVE_POLY1305 > user_settings.h"]]}, {"name": "all-dilithium-cryptocb", "minutes": 8.9, "configure": ["--enable-all", "--enable-dilithium", "--enable-cryptocb", "--enable-cryptocbutils", "--enable-pkcallbacks"]}, diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index fb302c4c8e1..c84b772cc85 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1086,6 +1086,7 @@ WOLFSSL_NO_CHACHA20_POLY1305_FUSED_IFMA WOLFSSL_NO_CHACHA20_POLY1305_SHORT WOLFSSL_NO_CHACHA20_POLY1305_SMALL_ASM WOLFSSL_NO_CLIENT_CERT_ERROR +WOLFSSL_NO_CONST_CMP WOLFSSL_NO_COPY_CERT WOLFSSL_NO_COPY_KEY WOLFSSL_NO_CRL_DATE_CHECK @@ -1104,6 +1105,7 @@ WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK WOLFSSL_NO_ETM_ALERT WOLFSSL_NO_FCHMOD WOLFSSL_NO_FENCE +WOLFSSL_NO_FORCE_ZERO WOLFSSL_NO_ISSUERHASH_TDPEER WOLFSSL_NO_KCAPI_AES_CBC WOLFSSL_NO_KCAPI_HMAC_SHA1 @@ -1268,7 +1270,6 @@ WOLFSSL_USER_MUTEX WOLFSSL_USER_THREADING WOLFSSL_USE_ESP32C3_CRYPT_HASH_HW WOLFSSL_USE_FLASHMEM -WOLFSSL_USE_FORCE_ZERO WOLFSSL_VA416X0_TRNG WOLFSSL_VALIDATE_DH_KEYGEN WOLFSSL_VALIDATE_MLDSA_KEYGEN diff --git a/ChangeLog.md b/ChangeLog.md index 822e5347213..ab5da636b52 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -101,6 +101,15 @@ leak must stop doing so, and the returned key is shared with the certificate, so it must be treated as read only. +* **Behavioral change (new internal symbol `wc_BarrierDataSink()`)**: now + exported on every build, since code that includes `misc.c` picks its + `WC_BARRIER_DATA()` arm from its own compiler and flags. Only the portable + arm (non-GCC compilers such as MSVC and IAR, or `WOLFSSL_NO_ASM`) calls it; + `WC_BARRIER_DATA_USES_SINK` marks that arm. + +* **Behavioral change (`secure_zero_memory()` removed from `blake2-impl.h`)**: + BLAKE2 now wipes with `ForceZero()`; use `wc_ForceZero()` instead. + ## Post-Quantum Cryptography (PQC) * Added opt-in per-key Falcon signing caches (`--enable-falcon=cache-key`, `cache-basis`), roughly doubling signing speed with the default integer fpr backend. by @Frauschi @@ -154,6 +163,17 @@ now list `WOLFSSL_CRYPTOCELL` alongside the other ports that replace the software curve arithmetic. +* **Fix (`wc_ForceZero()`/`wc_ConstantCompare()` undefined without + `memory.c`)**: `--disable-memory`, `--enable-leanpsk` and `--enable-leantls` + builds could not link them. Both moved to `wc_port.c`, declared in + `wc_port.h`. No ABI change. + +* **Fix (minimal builds failed to link `ForceZero()`/`ConstantCompare()`)**: + `settings.h` no longer defines `WOLFSSL_NO_FORCE_ZERO` or + `WOLFSSL_NO_CONST_CMP` for small `WOLFCRYPT_ONLY` builds; SHA-1 and + ChaCha20-Poly1305 still call them. Integrators who supply their own must + now define the macro themselves. `WOLFSSL_USE_FORCE_ZERO` is a no-op. + ## Fixes * ML-KEM and ML-DSA now keep the key's device id on their internal SHAKE hashing, so a registered crypto callback sees the SHAKE-256 Update and Final calls. by @Frauschi (PR 11544) diff --git a/tests/api/test_port.c b/tests/api/test_port.c index 66e3c76f122..1b6d5541bd1 100644 --- a/tests/api/test_port.c +++ b/tests/api/test_port.c @@ -236,3 +236,45 @@ int test_wc_PortDecisionCoverage(void) return EXPECT_RESULT(); } + +/* The exported wrappers live in wc_port.c so builds without memory.c link + * them; calling them here checks that. */ +int test_wc_ForceZero_ConstantCompare(void) +{ + EXPECT_DECLS; + +#ifndef WOLFSSL_NO_FORCE_ZERO + { + /* 34 bytes at u.b + 1 (u.b word-aligned) run the head, word loop + * and tail; the guard bytes catch overruns. */ + union { + unsigned long align; + byte b[36]; + } u; + byte zero[sizeof(u.b) - 2]; + + XMEMSET(u.b, 0xA5, sizeof(u.b)); + XMEMSET(zero, 0, sizeof(zero)); + wc_ForceZero(u.b + 1, sizeof(u.b) - 2); + ExpectIntEQ(u.b[0], 0xA5); + ExpectIntEQ(u.b[sizeof(u.b) - 1], 0xA5); + ExpectIntEQ(XMEMCMP(u.b + 1, zero, sizeof(zero)), 0); + + /* NULL is allowed when len is 0. */ + wc_ForceZero(NULL, 0); + } +#endif /* !WOLFSSL_NO_FORCE_ZERO */ + +#ifndef WOLFSSL_NO_CONST_CMP + { + static const unsigned char a[] = { 0x01, 0x02, 0x03 }; + + ExpectIntEQ(wc_ConstantCompare(a, a, (int)sizeof(a)), 0); + } +#endif /* !WOLFSSL_NO_CONST_CMP */ + + /* Exported on every build; asm-arm builds never call it, so link it. */ + wc_BarrierDataSink(NULL); + + return EXPECT_RESULT(); +} diff --git a/tests/api/test_port.h b/tests/api/test_port.h index b16a8f71705..f28d0fc6fba 100644 --- a/tests/api/test_port.h +++ b/tests/api/test_port.h @@ -25,8 +25,10 @@ #include int test_wc_PortDecisionCoverage(void); +int test_wc_ForceZero_ConstantCompare(void); #define TEST_PORT_DECLS \ - TEST_DECL_GROUP("port", test_wc_PortDecisionCoverage) + TEST_DECL_GROUP("port", test_wc_PortDecisionCoverage), \ + TEST_DECL_GROUP("port", test_wc_ForceZero_ConstantCompare) #endif /* WOLFCRYPT_TEST_PORT_H */ diff --git a/wolfcrypt/src/blake2b.c b/wolfcrypt/src/blake2b.c index 4456fe8c06a..d438c9a9119 100644 --- a/wolfcrypt/src/blake2b.c +++ b/wolfcrypt/src/blake2b.c @@ -167,8 +167,7 @@ int blake2b_init_key( blake2b_state *S, const byte outlen, const void *key, XMEMSET( block, 0, BLAKE2B_BLOCKBYTES ); XMEMCPY( block, key, keylen ); ret = blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from */ - /* memory */ + ForceZero( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from memory */ WC_FREE_VAR_EX(block, NULL, DYNAMIC_TYPE_TMP_BUFFER); } diff --git a/wolfcrypt/src/blake2s.c b/wolfcrypt/src/blake2s.c index 860268a624a..9ad41061a6a 100644 --- a/wolfcrypt/src/blake2s.c +++ b/wolfcrypt/src/blake2s.c @@ -166,8 +166,7 @@ int blake2s_init_key( blake2s_state *S, const byte outlen, const void *key, XMEMSET( block, 0, BLAKE2S_BLOCKBYTES ); XMEMCPY( block, key, keylen ); ret = blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from */ - /* memory */ + ForceZero( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from memory */ WC_FREE_VAR_EX(block, NULL, DYNAMIC_TYPE_TMP_BUFFER); } diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index a2df2258ebe..908da0290f7 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -1729,23 +1729,6 @@ void __attribute__((no_instrument_function)) } #endif -#ifndef WOLFSSL_NO_FORCE_ZERO -/* Exported version of ForceZero(). */ -void wc_ForceZero(void *mem, size_t len) -{ - ForceZero(mem, len); -} -#endif - -#ifndef WOLFSSL_NO_CONST_CMP -/* Exported version of ConstantCompare(). */ -int wc_ConstantCompare(const byte* a, const byte* b, int length) - -{ - return ConstantCompare(a, b, length); -} -#endif - #ifdef WC_DEBUG_CIPHER_LIFECYCLE static const byte wc_debug_cipher_lifecycle_tag_value[] = { 'W', 'o', 'l', 'f' }; diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 20263890bc1..2be6b1c5c59 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -189,6 +189,13 @@ Threading/Mutex options: #include #endif +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + #if defined(WOLFSSL_CAAM) #include #endif @@ -785,7 +792,6 @@ WOLFSSL_API void wc_ForkLock_SetBroken(wc_ForkLock* lock, int broken) } #endif -#ifdef WC_BARRIER_DATA_USES_SINK /* Callee of the portable WC_BARRIER_DATA() arm. It is reached through a * pointer read with a volatile load, so link time optimization cannot see * that the body is empty and drop the escape of the buffer address. */ @@ -801,7 +807,22 @@ WC_NO_INLINE void wc_BarrierDataSink(void* p) *(void (* const volatile *)(void*))&wc_BarrierDataSinkPtr; fn(p); } -#endif /* WC_BARRIER_DATA_USES_SINK */ + +/* Exported wrappers; here rather than memory.c, which some builds omit. */ +#ifndef WOLFSSL_NO_FORCE_ZERO +void wc_ForceZero(void *mem, size_t len) +{ + ForceZero(mem, len); +} +#endif /* !WOLFSSL_NO_FORCE_ZERO */ + +#ifndef WOLFSSL_NO_CONST_CMP +int wc_ConstantCompare(const unsigned char* a, + const unsigned char* b, int length) +{ + return ConstantCompare(a, b, length); +} +#endif /* !WOLFSSL_NO_CONST_CMP */ /* Used to initialize state for wolfcrypt return 0 on success diff --git a/wolfssl/wolfcrypt/blake2-impl.h b/wolfssl/wolfcrypt/blake2-impl.h index 63fd8eef59b..b7b1791ba8f 100644 --- a/wolfssl/wolfcrypt/blake2-impl.h +++ b/wolfssl/wolfcrypt/blake2-impl.h @@ -147,13 +147,5 @@ static WC_INLINE word64 rotr64( const word64 w, const unsigned c ) return ( w >> c ) | ( w << ( 64 - c ) ); } -/* prevents compiler optimizing out memset() */ -static WC_INLINE void secure_zero_memory( void *v, word64 n ) -{ - volatile byte *p = ( volatile byte * )v; - - while( n-- ) *p++ = 0; -} - #endif /* WOLFCRYPT_BLAKE2_IMPL_H */ diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 91200566acb..da33b787524 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -331,13 +331,7 @@ WOLFSSL_LOCAL void wc_MemZero_Add(const char* name, const void* addr, WOLFSSL_LOCAL void wc_MemZero_Check(void* addr, size_t len); #endif -#ifndef WOLFSSL_NO_FORCE_ZERO -WOLFSSL_API void wc_ForceZero(void *mem, size_t len); -#endif - -#ifndef WOLFSSL_NO_CONST_CMP -WOLFSSL_API int wc_ConstantCompare(const byte* a, const byte* b, int length); -#endif +/* wc_ForceZero()/wc_ConstantCompare() are declared in wc_port.h. */ #ifdef WC_DEBUG_CIPHER_LIFECYCLE #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 8f7dfb3804b..f757f5f8789 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5324,21 +5324,6 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #define WOLFSSL_NO_CT_OPS #endif -#if defined(WOLFCRYPT_ONLY) && defined(NO_AES) && !defined(HAVE_CURVE25519) && \ - !defined(HAVE_CURVE448) && defined(WC_NO_RNG) && defined(WC_NO_RSA_OAEP) - #undef WOLFSSL_NO_CONST_CMP - #define WOLFSSL_NO_CONST_CMP -#endif - -#if defined(WOLFCRYPT_ONLY) && defined(NO_AES) && !defined(WOLFSSL_SHA384) && \ - !defined(WOLFSSL_SHA512) && defined(WC_NO_RNG) && \ - !defined(WOLFSSL_SP_MATH) && !defined(WOLFSSL_SP_MATH_ALL) \ - && !defined(USE_FAST_MATH) && defined(NO_SHA256) && \ - !defined(WOLFSSL_USE_FORCE_ZERO) - #undef WOLFSSL_NO_FORCE_ZERO - #define WOLFSSL_NO_FORCE_ZERO -#endif - /* Detect old cryptodev name */ #if defined(WOLF_CRYPTO_DEV) && !defined(WOLF_CRYPTO_CB) #define WOLF_CRYPTO_CB diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index d61ee49f6cb..a3c3a568af8 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -44,6 +44,10 @@ /* GENERIC INCLUDE SECTION */ +#ifndef NO_STDDEF_H + #include /* for size_t */ +#endif + #if defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) #include #if (defined(MQX_USE_IO_OLD) && MQX_USE_IO_OLD) || \ @@ -2047,9 +2051,6 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); #if (!defined(WOLFSSL_LEANPSK) && !defined(STRING_USER)) || \ defined(USE_WOLF_STRNSTR) - #ifndef NO_STDDEF_H - #include /* for size_t */ - #endif /* NO_STDDEF_H */ WOLFSSL_TEST_VIS char* wolfSSL_strnstr(const char* s1, const char* s2, size_t n); #endif @@ -2171,12 +2172,29 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); } while(0) #endif +/* Defined in wc_port.c. */ +#ifndef WOLFSSL_NO_FORCE_ZERO +/* Zero len bytes at mem. mem may be NULL when len is 0. */ +WOLFSSL_API void wc_ForceZero(void *mem, size_t len); +#endif + +#ifndef WOLFSSL_NO_CONST_CMP +/* unsigned char: byte is not typedef'd yet. */ +WOLFSSL_API int wc_ConstantCompare(const unsigned char* a, + const unsigned char* b, int length); +#endif + /* Compiler barrier that also treats the memory at ptr as read, so a wipe of * that memory cannot be dropped as a dead store. The GNU form emits no CPU * fence; cross-thread ordering is the caller's job. Without GNU asm (other * compilers, or WOLFSSL_NO_ASM) ptr escapes to an out-of-line call in * wc_port.c, since a bare compiler barrier does not cover a local whose * address never escaped. */ + +/* Internal to WC_BARRIER_DATA(). ptr is never dereferenced. Declared on + * every build: code that includes misc.c may take a different arm. */ +WOLFSSL_API void wc_BarrierDataSink(void* ptr); + #ifdef WC_BARRIER_DATA /* use user-supplied WC_BARRIER_DATA() definition. */ #elif defined(__CC_ARM) @@ -2188,8 +2206,6 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); __asm__ __volatile__("" : : "r"(ptr) : "memory") #else #define WC_BARRIER_DATA_USES_SINK - /* Internal to WC_BARRIER_DATA(). ptr is never dereferenced. */ - WOLFSSL_API void wc_BarrierDataSink(void* ptr); #define WC_BARRIER_DATA(ptr) do { wc_BarrierDataSink((ptr)); } while (0) #endif