From cf2a6fa2d4a235be7297fada91ac09ddb7b956a9 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 1 Oct 2026 16:35:46 -0700 Subject: [PATCH 1/4] Add a wolfCrypt-only CMake option and fix stray defines in options.h --- .github/workflows/cmake.yml | 61 ++++++++++++++++++++++++ CMakeLists.txt | 93 +++++++++++++++++++++++++++++++++---- cmake/functions.cmake | 14 +++--- cmake/options.h.in | 2 + 4 files changed, 155 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 7a42c7e70cf..80b414e8679 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -111,6 +111,67 @@ jobs: cd .. rm -rf build +# Option plumbing: a declared option must reach both the library and +# options.h, and a -D that is not an option must reach neither. + - name: Check option to options.h plumbing + run: | + mkdir build + cd build + cmake -DWOLFSSL_CRYPT_ONLY=yes .. 2>&1 | tee cfg.log + grep -q '^#define WOLFCRYPT_ONLY$' wolfssl/options.h + grep -q '^#define NO_TLS$' wolfssl/options.h + # TLS-layer options default off, as with --enable-cryptonly, but the + # TLS 1.3 KDFs stay. + ! grep -q '^#define HAVE_SNI$' wolfssl/options.h + ! grep -q '^#define WOLFSSL_DTLS$' wolfssl/options.h + grep -q '^#define WOLFSSL_TLS13$' wolfssl/options.h + ! grep -q 'is not a wolfSSL build option' cfg.log + cmake --build . + + cd .. + rm -rf build + mkdir build + cd build + # Adding cryptonly to a directory configured without it must take the + # TLS layer out too, not leave the previous defaults cached. + cmake .. > /dev/null + grep -q '^#define HAVE_SNI$' wolfssl/options.h + cmake -DWOLFSSL_CRYPT_ONLY=yes .. > /dev/null + ! grep -q '^#define HAVE_SNI$' wolfssl/options.h + grep -q '^#define NO_TLS$' wolfssl/options.h + + cd .. + rm -rf build + mkdir build + cd build + # Cryptonly wins over a TLS-layer option asked for alongside it. + cmake -DWOLFSSL_CRYPT_ONLY=yes -DWOLFSSL_DTLS=yes .. > /dev/null + ! grep -q '^#define WOLFSSL_DTLS$' wolfssl/options.h + + cd .. + rm -rf build + mkdir build + cd build + # An option declared with a raw CACHE entry rather than add_option must + # survive the stray-define guard, including across a reconfigure. + cmake -DWOLFSSL_HARDEN_TLS=128 .. 2>&1 | tee cfg.log + grep -q '^#define WOLFSSL_HARDEN_TLS 128$' wolfssl/options.h + ! grep -q 'is not a wolfSSL build option' cfg.log + cmake . > /dev/null + grep -q '^#define WOLFSSL_HARDEN_TLS 128$' wolfssl/options.h + + cd .. + rm -rf build + mkdir build + cd build + # WOLFSSL_STATICMEMORY is the option; this spelling is not one. + cmake -DWOLFSSL_STATIC_MEMORY=yes .. 2>&1 | tee cfg.log + grep -q 'WOLFSSL_STATIC_MEMORY is not a wolfSSL build option' cfg.log + ! grep -q '^#define WOLFSSL_STATIC_MEMORY$' wolfssl/options.h + + cd .. + rm -rf build + # CMake build with user_settings.h - name: Build wolfssl with user_settings.h run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 70ebdf69c19..79412c1e0df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -500,14 +500,6 @@ if(WOLFSSL_SCEP) list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_X963_KDF" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_HAVE_WOLFSCEP" "-DWOLFSSL_KEY_GEN") endif() -add_option("WOLFSSL_MCAST" "Enable DTLS multicast support (default: disabled)" "no" "yes;no") -if(WOLFSSL_MCAST) - foreach(_o WOLFSSL_DTLS) - force_option(${_o} "yes") - endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_NULL_CIPHER" "-DWOLFSSL_MULTICAST") -endif() - add_option("WOLFSSL_WPAS_DPP" "Enable wpa_supplicant DPP support (default: disabled)" "no" "yes;no") if(WOLFSSL_WPAS_DPP) foreach(_o WOLFSSL_AES WOLFSSL_ARC4 WOLFSSL_CRL WOLFSSL_DES3 WOLFSSL_DSA WOLFSSL_MD4 WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_OCSPSTAPLING WOLFSSL_OCSPSTAPLING_V2 WOLFSSL_PKCS7) @@ -632,6 +624,39 @@ if(WOLFSSL_DEBUG) endif() +# wolfCrypt only (no TLS). Declared ahead of the TLS-layer options so the +# forces below land before their add_option() calls. +add_option("WOLFSSL_CRYPT_ONLY" + "Enable wolfCrypt Only build (default: disabled)" + "no" "yes;no") + +if(WOLFSSL_CRYPT_ONLY) + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFCRYPT_ONLY") + # Mirror --enable-cryptonly so options.h describes the library built. + # Forced, not defaulted: a reconfigure cannot tell a cache entry from an + # explicit setting. WOLFSSL_TLS carries -DNO_TLS and the TLS-version + # checks read it. TLS 1.2 and 1.3 stay on for their wolfCrypt-layer KDFs. + foreach(_o WOLFSSL_TLS + WOLFSSL_ALPN WOLFSSL_CRL_MONITOR WOLFSSL_DTLS WOLFSSL_DTLS13 + WOLFSSL_DTLS_CH_FRAG WOLFSSL_DTLS_CID WOLFSSL_DTLS_MTU + WOLFSSL_EARLYDATA WOLFSSL_ECH WOLFSSL_MCAST WOLFSSL_OCSP + WOLFSSL_OCSPSTAPLING WOLFSSL_OCSPSTAPLING_V2 + WOLFSSL_PKCALLBACKS WOLFSSL_QUIC + WOLFSSL_RENEGOTIATION_INDICATION WOLFSSL_SECURE_RENEGOTIATION + WOLFSSL_SNI WOLFSSL_SRTP WOLFSSL_TLSX) + force_option(${_o} "no") + endforeach() + message(STATUS "WOLFSSL_CRYPT_ONLY: TLS layer off, including its options") +endif() + +add_option("WOLFSSL_MCAST" "Enable DTLS multicast support (default: disabled)" "no" "yes;no") +if(WOLFSSL_MCAST) + foreach(_o WOLFSSL_DTLS) + force_option(${_o} "yes") + endforeach() + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_NULL_CIPHER" "-DWOLFSSL_MULTICAST") +endif() + # Single threaded add_option("WOLFSSL_SINGLE_THREADED" "Enable wolfSSL single threaded (default: disabled)" @@ -4576,6 +4601,58 @@ endforeach() # both emitting the same feature define). list(REMOVE_DUPLICATES WOLFSSL_DEFINITIONS) +# Matches configure.ac: cryptonly and opensslall are mutually incompatible. +# The application bundles (nginx, haproxy, ...) each append -DOPENSSL_ALL +# directly, which forcing their leaf options off does not retract, so test the +# definition list as well as the option. +if(WOLFSSL_CRYPT_ONLY) + if(WOLFSSL_OPENSSLALL OR "-DOPENSSL_ALL" IN_LIST WOLFSSL_DEFINITIONS) + message(FATAL_ERROR + "cryptonly and opensslall are mutually incompatible.") + endif() +endif() + +# A -D that is not a build option still satisfies the matching #cmakedefine +# below, so options.h would claim features the library lacks (for example +# WOLFSSL_STATIC_MEMORY for the WOLFSSL_STATICMEMORY option). Drop those. +get_property(WOLFSSL_DECLARED_OPTIONS GLOBAL PROPERTY WOLFSSL_DECLARED_OPTIONS) +get_cmake_property(WOLFSSL_CACHE_VARS CACHE_VARIABLES) +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/options.h.in" OPTIONS_H_LINES + REGEX "^#cmakedefine[ \t]+[A-Za-z_]") +foreach(LINE IN LISTS OPTIONS_H_LINES) + string(REGEX REPLACE "^#cmakedefine[ \t]+([A-Za-z_][A-Za-z0-9_]*).*$" "\\1" + MACRO_NAME "${LINE}") + # wolfSSL's namespace only; the rest are system probes we set ourselves. + if(NOT MACRO_NAME MATCHES "^WOLF") + continue() + endif() + if(MACRO_NAME IN_LIST WOLFSSL_DECLARED_OPTIONS) + continue() + endif() + if(NOT MACRO_NAME IN_LIST WOLFSSL_CACHE_VARS) + continue() + endif() + # Already compiled in, so a real option however its cache entry was made + # (WOLFSSL_HARDEN_TLS uses a raw CACHE STRING to keep a bad value). + set(MACRO_IN_DEFS FALSE) + foreach(DEF IN LISTS WOLFSSL_DEFINITIONS) + if(DEF MATCHES "^-D${MACRO_NAME}(=.*)?$") + set(MACRO_IN_DEFS TRUE) + break() + endif() + endforeach() + if(MACRO_IN_DEFS) + continue() + endif() + if(${MACRO_NAME}) + message(WARNING "${MACRO_NAME} is not a wolfSSL build option; ignoring " + "it so that wolfssl/options.h matches the library. Run " + "`cmake -LH` for the option list.") + unset(${MACRO_NAME}) + unset(${MACRO_NAME} CACHE) + endif() +endforeach() + foreach(DEF IN LISTS WOLFSSL_DEFINITIONS) string(REGEX MATCH "^(-D)?([^=]+)(=(.*))?$" DEF_MATCH ${DEF}) if (NOT "${CMAKE_MATCH_4}" STREQUAL "") diff --git a/cmake/functions.cmake b/cmake/functions.cmake index 43834cf39ae..144d82d9386 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -17,9 +17,8 @@ endfunction() # explicit rather than relying on cross-file cache-precedence side effects. function(force_option NAME VALUE) set_property(GLOBAL PROPERTY "WOLFSSL_FORCE_${NAME}" "${VALUE}") - # Track pending forces so an unconsumed one (no matching add_option) can be - # reported by wolfssl_warn_unconsumed_forces() -- a force on an option that - # is not declared via add_option() would otherwise be silently ignored. + # Track pending forces so wolfssl_warn_unconsumed_forces() can report one + # with no matching add_option(), which would otherwise be ignored. set_property(GLOBAL APPEND PROPERTY WOLFSSL_FORCE_PENDING "${NAME}") endfunction() @@ -39,6 +38,9 @@ function(wolfssl_warn_unconsumed_forces) endfunction() function(add_option NAME HELP_STRING DEFAULT VALUES) + # Record the name for the options.h.in guard in CMakeLists.txt. + set_property(GLOBAL APPEND PROPERTY WOLFSSL_DECLARED_OPTIONS "${NAME}") + if(VALUES STREQUAL "yes;no") # Set the default value for the option. set(${NAME} ${DEFAULT} CACHE BOOL ${HELP_STRING}) @@ -49,10 +51,8 @@ function(add_option NAME HELP_STRING DEFAULT VALUES) set_property(CACHE ${NAME} PROPERTY STRINGS ${VALUES}) endif() - # Apply any dependency force recorded via force_option(). Done after the - # cache entry is created (so its BOOL/STRING type and help are preserved) - # and before the reduction below, so the forced value drives this option's - # own define/source emission just as an explicit setting would. + # Apply any force_option(), after the cache entry exists so its type and + # help survive, and before the reduction below. get_property(_wolfssl_forced GLOBAL PROPERTY "WOLFSSL_FORCE_${NAME}" SET) if(_wolfssl_forced) get_property(_wolfssl_force_val GLOBAL PROPERTY "WOLFSSL_FORCE_${NAME}") diff --git a/cmake/options.h.in b/cmake/options.h.in index 30dc2982e7a..879196355db 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -707,6 +707,8 @@ extern "C" { #cmakedefine WOLFSSL_STATIC_MEMORY_LEAN #undef WOLFSSL_STATIC_MEMORY_DEBUG_CALLBACK #cmakedefine WOLFSSL_STATIC_MEMORY_DEBUG_CALLBACK +#undef WOLFCRYPT_ONLY +#cmakedefine WOLFCRYPT_ONLY #undef NO_TLS #cmakedefine NO_TLS #undef NO_SHA256 From c831c730d7904d2338e5d78eb5a753dccc8e6bb3 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 1 Oct 2026 16:35:46 -0700 Subject: [PATCH 2/4] Encode certificate names and extensions without an allocator --- .github/workflows/no-malloc.yml | 3 +- wolfcrypt/src/asn.c | 130 ++++++++++++++++++++++++++++---- wolfcrypt/test/test.c | 97 ++++++++++++++++++++++++ wolfssl/wolfcrypt/asn.h | 3 +- wolfssl/wolfcrypt/asn_public.h | 2 +- 5 files changed, 218 insertions(+), 17 deletions(-) diff --git a/.github/workflows/no-malloc.yml b/.github/workflows/no-malloc.yml index 0fc8fa38ab2..68e31060a81 100644 --- a/.github/workflows/no-malloc.yml +++ b/.github/workflows/no-malloc.yml @@ -99,7 +99,8 @@ jobs: "run": [["./wolfcrypt/test/testwolfcrypt"]]}, {"name": "no-heap-cert", "minutes": 0.8, "configure": ["--enable-rsa", "--enable-keygen", "--enable-ecc", - "--enable-acert", "--disable-dh", "--disable-filesystem", + "--enable-acert", "--enable-certgen", "--enable-certreq", + "--enable-certext", "--disable-dh", "--disable-filesystem", "CFLAGS=-DWOLFSSL_NO_MALLOC -DNO_WOLFSSL_MEMORY -DRSA_MIN_SIZE=1024 -DWOLFSSL_TEST_CERT -DUSE_CERT_BUFFERS_2048 -DUSE_CERT_BUFFERS_256 -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"], "check": false, "run": [["./wolfcrypt/test/testwolfcrypt"]]} diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 3d458e6df0f..4f32a77b032 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -194,10 +194,11 @@ ASN Options: * WOLFSSL_X509_TINY: Minimal-extension profile. Compiles out optional X.509 extension decoders behind per-feature WOLFSSL_X509_TINY_ add-back macros. Requires WOLFSSL_ASN_TEMPLATE (enforced with #error). - * WC_ASN_NO_HEAP: Zero-allocation cert parse: reference key/alt-name - data in the source DER instead of heap copies, so the source buffer must - outlive the DecodedCert. Auto-defined when WOLFSSL_NO_MALLOC and - NO_WOLFSSL_MEMORY are set without XMALLOC_USER or WOLFSSL_STATIC_MEMORY. + * WC_ASN_NO_HEAP: Zero-allocation cert parse and encode. Parsing + references key/alt-name data in the source DER instead of heap copies, so + the source buffer must outlive the DecodedCert; encoding works from the + stack. Auto-defined when WOLFSSL_NO_MALLOC and NO_WOLFSSL_MEMORY are set + without XMALLOC_USER or WOLFSSL_STATIC_MEMORY. Limitation: IP and registeredID SAN entries need a parsed string form that has no in-place source, so such certs are rejected with ASN_PARSE_E. SAN DNS_entry.name is NOT NUL-terminated in this mode; only .len is authoritative. @@ -277,6 +278,7 @@ ASN Options: #include #include + #ifdef NO_INLINE #include #else @@ -28706,6 +28708,12 @@ static int wc_SetCert_LoadDer(Cert* cert, const byte* der, word32 derSz, #endif /* WOLFSSL_CERT_GEN */ +/* Bound for wc_SetExtKeyUsage()'s value. Every known usage name, comma + * separated, is 78 bytes today. */ +#ifndef WC_ASN_EKU_STR_MAX +#define WC_ASN_EKU_STR_MAX 128 +#endif + #ifdef WOLFSSL_CERT_GEN #ifndef NO_ASN_TIME @@ -28941,6 +28949,15 @@ struct { #define EKU_OID_LO 1 #define EKU_OID_HI 6 + +#ifdef WC_ASN_NO_HEAP +/* EKU template items: the SEQUENCE, one per known usage, plus OID slots. */ +#ifdef WOLFSSL_EKU_OID + #define WC_ASN_EKU_MAX_ITEMS (1 + EKU_OID_HI + CTC_MAX_EKU_NB) +#else + #define WC_ASN_EKU_MAX_ITEMS (1 + EKU_OID_HI) +#endif +#endif #endif /* WOLFSSL_ASN_TEMPLATE */ /* encode Extended Key Usage (RFC 5280 4.2.1.12), return total bytes written */ @@ -28949,6 +28966,11 @@ static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input) { /* TODO: consider calculating size of OBJECT_IDs, setting length into * SEQUENCE, encode SEQUENCE, encode OBJECT_IDs into buffer. */ +#ifdef WC_ASN_NO_HEAP + /* cnt below is a compile-time bound, so these fit on the stack. */ + ASNSetData dataASNbuf[WC_ASN_EKU_MAX_ITEMS]; + ASNItem extKuASNbuf[WC_ASN_EKU_MAX_ITEMS]; +#endif ASNSetData* dataASN; ASNItem* extKuASN = NULL; int asnIdx = 1; @@ -28961,6 +28983,10 @@ static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input) cnt += CTC_MAX_EKU_NB; #endif +#ifdef WC_ASN_NO_HEAP + dataASN = dataASNbuf; + extKuASN = extKuASNbuf; +#else /* Allocate memory for dynamic data items. */ dataASN = (ASNSetData*)XMALLOC(cnt * sizeof(ASNSetData), cert->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -28975,6 +29001,7 @@ static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input) ret = MEMORY_E; } } +#endif if (ret == 0) { /* Copy Sequence into dynamic ASN.1 template. */ @@ -29043,9 +29070,11 @@ static int SetExtKeyUsage(Cert* cert, byte* output, word32 outSz, byte input) ret = (int)sz; } +#ifndef WC_ASN_NO_HEAP /* Dispose of allocated data. */ XFREE(extKuASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(dataASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif return ret; } @@ -29414,24 +29443,38 @@ int ParseExtKeyUsageStr(const char* value, byte* extKeyUsage, void* heap) char *token, *str, *ptr; word32 len = 0; byte usage = 0; +#ifdef WC_ASN_NO_HEAP + char strBuf[WC_ASN_EKU_STR_MAX + 1]; +#endif if (value == NULL || extKeyUsage == NULL) { return BAD_FUNC_ARG; } - /* duplicate string (including terminator) */ + /* duplicate string (including terminator); XSTRTOK writes into it */ len = (word32)XSTRLEN(value); +#ifdef WC_ASN_NO_HEAP + (void)heap; + if (len > WC_ASN_EKU_STR_MAX) { + return BUFFER_E; + } + str = strBuf; +#else str = (char*)XMALLOC(len + 1, heap, DYNAMIC_TYPE_TMP_BUFFER); if (str == NULL) { return MEMORY_E; } +#endif XMEMCPY(str, value, len + 1); /* parse value, and set corresponding Key Usage value */ if ((token = XSTRTOK(str, ",", &ptr)) == NULL) { + #ifndef WC_ASN_NO_HEAP XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif return EXTKEYUSAGE_E; } + /* Adding a usage name here may need WC_ASN_EKU_STR_MAX raised. */ while (token != NULL) { if (!XSTRCASECMP(token, "any")) usage |= EXTKEYUSE_ANY; @@ -29455,7 +29498,9 @@ int ParseExtKeyUsageStr(const char* value, byte* extKeyUsage, void* heap) token = XSTRTOK(NULL, ",", &ptr); } +#ifndef WC_ASN_NO_HEAP XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif if (ret == 0) { *extKeyUsage = usage; @@ -29523,6 +29568,20 @@ enum { /* Number of items in ASN.1 template for the SEQUENCE around the RDNs. */ #define nameASN_Length (sizeof(nameASN) / sizeof(ASNItem)) +#ifdef WC_ASN_NO_HEAP + /* Name components per certificate name. Lower to trade components for + * stack; SetNameEx() returns BUFFER_E when a name needs more. */ + #ifndef WC_ASN_MAX_NAME_ENTRIES + #ifdef WOLFSSL_MULTI_ATTRIB + #define WC_ASN_MAX_NAME_ENTRIES (NAME_ENTRIES + CTC_MAX_ATTRIB) + #else + #define WC_ASN_MAX_NAME_ENTRIES NAME_ENTRIES + #endif + #endif + #define WC_ASN_NAME_MAX_ITEMS \ + (nameASN_Length + rdnASN_Length * (word32)WC_ASN_MAX_NAME_ENTRIES) +#endif + static int SetNameRdnItems(ASNSetData* dataASN, ASNItem* namesASN, int maxIdx, CertName* name) { @@ -29654,11 +29713,16 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap) { /* TODO: consider calculating size of entries, putting length into * SEQUENCE, encode SEQUENCE, encode entries into buffer. */ - ASNSetData* dataASN = NULL; /* Can't use DECL_ASNSETDATA. Always dynamic. */ + /* Can't use DECL_ASNSETDATA: item count is only known at run time. */ + ASNSetData* dataASN = NULL; ASNItem* namesASN = NULL; word32 items = 0; int ret = 0; word32 sz = 0; +#ifdef WC_ASN_NO_HEAP + ASNSetData dataASNbuf[WC_ASN_NAME_MAX_ITEMS]; + ASNItem namesASNbuf[WC_ASN_NAME_MAX_ITEMS]; +#endif /* Calculate length of name entries and size for allocating. */ ret = SetNameRdnItems(NULL, NULL, 0, name); @@ -29674,6 +29738,14 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap) return 0; } +#ifdef WC_ASN_NO_HEAP + if (items > WC_ASN_NAME_MAX_ITEMS) { + WOLFSSL_MSG("Name needs more entries than WC_ASN_MAX_NAME_ENTRIES"); + return BUFFER_E; + } + dataASN = dataASNbuf; + namesASN = namesASNbuf; +#else /* Allocate dynamic data items. */ dataASN = (ASNSetData*)XMALLOC(items * sizeof(ASNSetData), heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -29688,6 +29760,7 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap) ret = MEMORY_E; } } +#endif if (ret == 0) { /* Clear the dynamic data. */ @@ -29722,8 +29795,10 @@ int SetNameEx(byte* output, word32 outputSz, CertName* name, void* heap) } } +#ifndef WC_ASN_NO_HEAP XFREE(namesASN, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(dataASN, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif (void)heap; return ret; } @@ -32904,6 +32979,9 @@ static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey, void* mlKemKey, int kid_type) { +#ifdef WC_ASN_NO_HEAP + byte bufOnStack[MAX_PUBLIC_KEY_SZ]; +#endif byte *buf; int bufferSz, ret; word32 bufSz = MAX_PUBLIC_KEY_SZ; @@ -32927,9 +33005,17 @@ static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey, bufSz = MLKEM_MAX_PUB_KEY_DER_SIZE; } #endif +#ifdef WC_ASN_NO_HEAP + if (bufSz > (word32)sizeof(bufOnStack)) { + /* The PQC keys above ask for more than the stack buffer holds. */ + return NOT_COMPILED_IN; + } + buf = bufOnStack; +#else buf = (byte *)XMALLOC(bufSz, cert->heap, DYNAMIC_TYPE_TMP_BUFFER); if (buf == NULL) return MEMORY_E; +#endif /* Public Key */ bufferSz = -1; @@ -32987,7 +33073,9 @@ static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey, #endif if (bufferSz <= 0) { + #ifndef WC_ASN_NO_HEAP XFREE(buf, cert->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif return PUBLIC_KEY_E; } @@ -33011,7 +33099,9 @@ static int SetKeyIdFromPublicKey(Cert *cert, RsaKey *rsakey, ecc_key *eckey, #endif } +#ifndef WC_ASN_NO_HEAP XFREE(buf, cert->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif return ret; } @@ -34014,17 +34104,29 @@ int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap) word32 idx = 0, nb_val; char *token, *str, *ptr; word32 len; + /* NULL when str is the stack buffer, so XFREE below is then a no-op */ + char *strAlloc = NULL; +#ifdef WC_ASN_NO_HEAP + char strBuf[CTC_MAX_CERTPOL_SZ]; +#endif (void)heap; if (out == NULL || outSz == NULL || *outSz < 2 || in == NULL) return BAD_FUNC_ARG; - /* duplicate string (including terminator) */ + /* duplicate string (including terminator); XSTRTOK writes into it */ len = (word32)XSTRLEN(in); - str = (char *)XMALLOC(len+1, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (str == NULL) +#ifdef WC_ASN_NO_HEAP + if (len >= sizeof(strBuf)) + return BUFFER_E; + str = strBuf; +#else + strAlloc = (char *)XMALLOC(len+1, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (strAlloc == NULL) return MEMORY_E; + str = strAlloc; +#endif XMEMCPY(str, in, len+1); nb_val = 0; @@ -34037,7 +34139,7 @@ int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap) if (nb_val == 0) { if (val > 2) { - XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(strAlloc, heap, DYNAMIC_TYPE_TMP_BUFFER); return ASN_OBJECT_ID_E; } @@ -34045,12 +34147,12 @@ int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap) } else if (nb_val == 1) { if (val > 127) { - XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(strAlloc, heap, DYNAMIC_TYPE_TMP_BUFFER); return ASN_OBJECT_ID_E; } if (idx > *outSz) { - XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(strAlloc, heap, DYNAMIC_TYPE_TMP_BUFFER); return BUFFER_E; } @@ -34069,7 +34171,7 @@ int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap) } if ((idx+(word32)i) >= *outSz) { - XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(strAlloc, heap, DYNAMIC_TYPE_TMP_BUFFER); return BUFFER_E; } @@ -34086,7 +34188,7 @@ int EncodePolicyOID(byte *out, word32 *outSz, const char *in, void* heap) *outSz = idx; - XFREE(str, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(strAlloc, heap, DYNAMIC_TYPE_TMP_BUFFER); return 0; } #endif /* WOLFSSL_CERT_EXT || OPENSSL_EXTRA */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 7c122b4ffd2..a802e7ec377 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1250,6 +1250,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_cts_test(void); #endif /* !WC_TEST_EXPORT_SUBTESTS */ +/* Declared here too: WC_TEST_EXPORT_SUBTESTS drops the block above. */ +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) && \ + defined(WOLFSSL_CERT_EXT) && defined(HAVE_ECC) && \ + defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) +static wc_test_ret_t certreq_no_malloc_test(void); +#endif + /* General big buffer size for many tests. */ #define FOURK_BUF 4096 @@ -3430,6 +3437,15 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("CERT NOMALLOC test passed!\n"); #endif +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) && \ + defined(WOLFSSL_CERT_EXT) && defined(HAVE_ECC) && \ + defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) + if ( (ret = certreq_no_malloc_test()) != 0) + TEST_FAIL("CERTREQ NOMALLOC test failed!\n", ret); + else + TEST_PASS("CERTREQ NOMALLOC test passed!\n"); +#endif + #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_TEST_CERT) && \ !defined(NO_FILESYSTEM) && !defined(NO_RSA) && defined(WOLFSSL_GEN_CERT) if ( (ret = certext_test()) != 0) @@ -39101,6 +39117,87 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t rsa_test(void) #endif /* !NO_RSA */ +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_REQ) && \ + defined(WOLFSSL_CERT_EXT) && defined(HAVE_ECC) && \ + defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) +/* Certificate request generation with the Cert on the stack. */ +static wc_test_ret_t certreq_no_malloc_test(void) +{ + /* Too large for one stack frame (linuxkm caps them at 4kB), and only + * ever one in flight. */ + static Cert req; + static ecc_key key; + static byte der[1024]; + word32 idx = 0; + int derSz; + wc_test_ret_t ret; + + WOLFSSL_ENTER("certreq_no_malloc_test"); + + ret = wc_ecc_init_ex(&key, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + + ret = wc_EccPrivateKeyDecode(ecc_key_der_256, &idx, &key, + (word32)sizeof_ecc_key_der_256); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + if (ret == 0) { + ret = wc_InitCert_ex(&req, HEAP_HINT, devId); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + XSTRNCPY(req.subject.country, "US", CTC_NAME_SIZE); + XSTRNCPY(req.subject.org, "wolfSSL", CTC_NAME_SIZE); + XSTRNCPY(req.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE); + req.version = 0; + req.sigType = CTC_SHA256wECDSA; + /* Covers the EKU string parser and encoder. */ + ret = wc_SetExtKeyUsage(&req, "clientAuth,codeSigning"); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + derSz = wc_MakeCertReq_ex(&req, der, (word32)sizeof(der), ECC_TYPE, + &key); + if (derSz <= 0) + ret = WC_TEST_RET_ENC_EC(derSz); + } + +#if !defined(NO_ASN_TIME) && !defined(WC_NO_RNG) + /* Policies encode only for a certificate, so EncodePolicyOID() needs + * wc_MakeCert(). */ + if (ret == 0) { + static WC_RNG rng; + + ret = wc_InitRng_ex(&rng, HEAP_HINT, devId); + if (ret != 0) { + ret = WC_TEST_RET_ENC_EC(ret); + } + else { + XMEMCPY(&req.issuer, &req.subject, sizeof(CertName)); + req.selfSigned = 1; + XSTRNCPY(req.certPolicies[0], "2.16.840.1.101.3.4.1.42", + CTC_MAX_CERTPOL_SZ); + req.certPoliciesNb = 1; + + derSz = wc_MakeCert(&req, der, (word32)sizeof(der), NULL, &key, + &rng); + if (derSz <= 0) + ret = WC_TEST_RET_ENC_EC(derSz); + wc_FreeRng(&rng); + } + } +#endif + + wc_ecc_free(&key); + return ret; +} +#endif /* WOLFSSL_CERT_GEN && WOLFSSL_CERT_REQ && WOLFSSL_CERT_EXT && + * HAVE_ECC && USE_CERT_BUFFERS_256 && !NO_SHA256 */ + + #if defined(WOLFSSL_TEST_CERT) && defined(HAVE_ECC) && \ !defined(NO_ECC256) && !defined(NO_ECC_SECP) /* Self-signed P-256 cert with a critical extension of unrecognized OID diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 91cb91afd47..455c6cc6a7f 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1528,7 +1528,8 @@ enum KeyIdType { #define WOLFSSL_IP6_ADDR_LEN 16 #endif /* OPENSSL_ALL || WOLFSSL_IP_ALT_NAME */ -/* No allocator: reference key/alt-name data in the source DER, not copies. */ +/* No allocator: parse by referencing key/alt-name data in the source DER + * rather than copies, and encode from the stack. */ #if defined(WOLFSSL_NO_MALLOC) && defined(NO_WOLFSSL_MEMORY) && \ !defined(XMALLOC_USER) && !defined(WOLFSSL_STATIC_MEMORY) #define WC_ASN_NO_HEAP diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 93933aac4d1..b834baf38c0 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -224,12 +224,12 @@ enum Ctc_Misc { CTC_FILETYPE_ASN1 = 2, CTC_FILETYPE_PEM = 1, CTC_FILETYPE_DEFAULT = 2, + CTC_MAX_CERTPOL_SZ = 200, /* RFC 5280 Section 4.2.1.4 */ #ifdef WOLFSSL_CERT_EXT /* AKID could contains: hash + (Option) AuthCertIssuer,AuthCertSerialNum * We support only hash */ CTC_MAX_SKID_SIZE = 32, /* SHA256_DIGEST_SIZE */ CTC_MAX_AKID_SIZE = 32, /* SHA256_DIGEST_SIZE */ - CTC_MAX_CERTPOL_SZ = 200, /* RFC 5280 Section 4.2.1.4 */ CTC_MAX_CERTPOL_NB = 2, /* Max number of Certificate Policy */ CTC_MAX_CRLINFO_SZ = WC_CTC_MAX_CRLINFO_SZ, /* Arbitrary size that should be * enough for at least two From 5dc53c507fcbdcc555523fb55d76a2fe45aa98c8 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 1 Oct 2026 16:35:46 -0700 Subject: [PATCH 3/4] Size key identifier buffers from the hash the build actually uses --- .github/configs/os-check-linux.json | 26 +++++- wolfcrypt/src/asn.c | 119 ++++++++++++++++++++-------- wolfcrypt/src/random.c | 2 + wolfcrypt/test/test.c | 78 +++++++++++++++++- wolfssl/wolfcrypt/asn.h | 41 +++------- wolfssl/wolfcrypt/asn_public.h | 38 ++++++++- 6 files changed, 236 insertions(+), 68 deletions(-) diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index d24797345d2..4fbba199c0f 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -541,5 +541,29 @@ "--disable-oldtls", "--disable-examples", "CPPFLAGS=-DWOLFSSL_NO_TLS12"]}, {"name": "tls13-sha512-runtime", "minutes": 1.6, "comment": "--enable-tls13-sha512 with TLS 1.2 left in, so the examples and unit tests build and run with WOLFSSL_HS_HASH_SHA512 set. No cipher suite sets mac_algorithm to sha512_mac, so the sha512_mac arms in src/tls13.c stay unreached; what this entry proves is that the option does not break an otherwise ordinary build, which the two compile-only entries above cannot show.", - "configure": ["--enable-tls13", "--enable-tls13-sha512", "--enable-sha512"]} + "configure": ["--enable-tls13", "--enable-tls13-sha512", "--enable-sha512"]}, +{"name": "cryptonly-sha3-keyid", "minutes": 0.8, + "comment": "SHA3 as the only hash family that can derive key identifiers: no SHA-1 and no SHA-256, so HashIdAlg()/CalcHashId_ex() must pick SHA3-256 and KEYID_SIZE must follow it. SHA-512 is kept only because the Hash DRBG needs it. The CERT KEYID subtest checks the SKID/AKID sizes the generator writes against CTC_MAX_SKID_SIZE.", + "configure": ["--enable-cryptonly", "--enable-ecc", "--enable-certgen", + "--enable-certreq", "--enable-certext", "--enable-sha3", "--enable-sha512", + "--disable-sha", "--disable-sha256", "--disable-sha224", "--disable-rsa", + "--disable-dh", + "CPPFLAGS=-DWOLFSSL_DRBG_SHA512 -DUSE_CERT_BUFFERS_256"]}, +{"name": "sha3-keyid-ocsp-crl", "minutes": 1.2, + "comment": "The SHA3-only key identifier hash of cryptonly-sha3-keyid, but with the TLS layer left in so src/ocsp.c and the Signer table compile. OCSP_DIGEST and OCSP_RESPONDER_ID_HASH_TYPE must name SHA3-256 and SIGNER_DIGEST_SIZE must match KEYID_SIZE, none of which a cryptonly build reaches. NO_SESSION_CACHE because HashObject() needs MD5, SHA-1 or SHA-256 and this build has none; no cipher suite survives either, so there is no handshake left to check.", + "configure": ["--enable-ocsp", "--enable-crl", "--enable-sha3", + "--enable-certgen", "--enable-certreq", "--enable-certext", + "--disable-sha", "--disable-sha256", "--disable-sha224", + "--disable-examples", "CPPFLAGS=-DNO_SESSION_CACHE"], + "check": false, + "run": [["./wolfcrypt/test/testwolfcrypt"]]}, +{"name": "sha3-384-keyid", "minutes": 0.8, + "comment": "WOLFSSL_NOSHA3_256 on top of the SHA3-only key identifier hash, so the selection falls through to SHA3-384 and WC_ASN_KEYID_SZ becomes 48. This is the only entry where the key identifier is larger than the 32-byte floor, so it is what proves CTC_MAX_SKID_SIZE widens and the Cert SKID/AKID buffers still hold what CalcHashId_ex() writes. ML-KEM is off because wc_mlkem_poly.c calls wc_InitSha3_256() unconditionally.", + "configure": ["--enable-cryptonly", "--enable-ecc", "--enable-certgen", + "--enable-certreq", "--enable-certext", "--enable-sha3", "--enable-sha512", + "--disable-sha", "--disable-sha256", "--disable-sha224", "--disable-rsa", + "--disable-dh", "--disable-mlkem", + "CPPFLAGS=-DWOLFSSL_DRBG_SHA512 -DUSE_CERT_BUFFERS_256 -DWOLFSSL_NOSHA3_256"], + "check": false, + "run": [["./wolfcrypt/test/testwolfcrypt"]]} ] diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 4f32a77b032..89ca4ec97ac 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -110,6 +110,7 @@ ASN Options: cost of taking up more memory. Adds initials, givenname, dnQualifer for example. * WC_ASN_HASH_SHA256: Force use of SHA2-256 for the internal hash ID calcs. + An OCSP CertID carries those hashes, so this selects its hash too. * WOLFSSL_ALLOW_ENCODING_CA_FALSE: Allow encoding BasicConstraints CA:FALSE * which is discouraged by X.690 specification - default values shall not * be encoded. @@ -14366,6 +14367,12 @@ static int GetCertKey(DecodedCert* cert, const byte* source, word32* inOutIdx, } #endif +#ifdef WOLFSSL_CERT_EXT +/* Cert key identifier buffers must hold what CalcHashId_ex() writes. */ +wc_static_assert((int)KEYID_SIZE <= (int)CTC_MAX_SKID_SIZE); +wc_static_assert((int)KEYID_SIZE <= (int)CTC_MAX_AKID_SIZE); +#endif + /* Return the hash algorithm to use with the signature algorithm. * * @param [in] oidSum Signature id. @@ -14383,11 +14390,7 @@ int HashIdAlg(word32 oidSum) return WC_SM3; } #endif -#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256)) - return WC_SHA256; -#else - return WC_SHA; -#endif + return WC_ASN_KEYID_HASH_TYPE; } /* Calculate hash of the id using the SHA-1 or SHA-256. @@ -14401,13 +14404,7 @@ int HashIdAlg(word32 oidSum) int CalcHashId(const byte* data, word32 len, byte* hash) { /* Use default hash algorithm. */ - return CalcHashId_ex(data, len, hash, -#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256)) - WC_SHA256 -#else - WC_SHA -#endif - ); + return CalcHashId_ex(data, len, hash, WC_ASN_KEYID_HASH_TYPE); } /* Calculate hash of the id using the SHA-1 or SHA-256. @@ -14418,41 +14415,93 @@ int CalcHashId(const byte* data, word32 len, byte* hash) * @return 0 on success. * @return MEMORY_E when dynamic memory allocation fails. */ +/* Zero the tail a digest shorter than the key identifier buffer leaves. Both + * operands are constants, so this folds away where the sizes match. */ +#define WC_ASN_KEYID_PAD(hash, digestSz) \ + do { \ + if ((int)KEYID_SIZE > (int)(digestSz)) { \ + XMEMSET((hash) + (digestSz), 0, \ + (size_t)((int)KEYID_SIZE - (int)(digestSz))); \ + } \ + } while (0) + int CalcHashId_ex(const byte* data, word32 len, byte* hash, int hashAlg) { int ret; +#ifndef NO_HASH_WRAPPER + /* Callers size hash at KEYID_SIZE, so a longer digest cannot be used. */ + if (wc_HashGetDigestSize(wc_HashTypeConvert(hashAlg)) > (int)KEYID_SIZE) { + return BUFFER_E; + } +#endif + + switch (hashAlg) { #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) - if (hashAlg == WC_SM3) { + case WC_SM3: + WC_ASN_KEYID_PAD(hash, WC_SM3_DIGEST_SIZE); ret = wc_Sm3Hash(data, len, hash); - } - else + break; #endif -#if defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256)) - if (hashAlg == WC_SHA256) { - ret = wc_Sha256Hash(data, len, hash); - } - else -#elif !defined(NO_SHA) - if (hashAlg == WC_SHA) { - #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) - XMEMSET(hash + WC_SHA_DIGEST_SIZE, 0, KEYID_SIZE - WC_SHA_DIGEST_SIZE); - #endif +#ifndef NO_SHA + case WC_SHA: + WC_ASN_KEYID_PAD(hash, WC_SHA_DIGEST_SIZE); ret = wc_ShaHash(data, len, hash); + break; +#endif +#ifndef NO_SHA256 + case WC_SHA256: + WC_ASN_KEYID_PAD(hash, WC_SHA256_DIGEST_SIZE); + ret = wc_Sha256Hash(data, len, hash); + break; +#endif +#if defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_256) + case WC_SHA3_256: + { + wc_Sha3 sha3[1]; /* on the stack: this path must not allocate */ + + WC_ASN_KEYID_PAD(hash, WC_SHA3_256_DIGEST_SIZE); + ret = wc_InitSha3_256(sha3, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha3_256_Update(sha3, data, len); + if (ret == 0) { + ret = wc_Sha3_256_Final(sha3, hash); + } + wc_Sha3_256_Free(sha3); + } + break; } - else -#else - (void)data; - (void)len; - (void)hash; #endif +#if defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_384) + case WC_SHA3_384: { + wc_Sha3 sha3[1]; /* on the stack: this path must not allocate */ + + WC_ASN_KEYID_PAD(hash, WC_SHA3_384_DIGEST_SIZE); + ret = wc_InitSha3_384(sha3, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha3_384_Update(sha3, data, len); + if (ret == 0) { + ret = wc_Sha3_384_Final(sha3, hash); + } + wc_Sha3_384_Free(sha3); + } + break; + } +#endif + default: + (void)data; + (void)len; + (void)hash; ret = NOT_COMPILED_IN; + break; } return ret; } +#undef WC_ASN_KEYID_PAD + #ifndef NO_CERTS /* Get the hash of the id using the SHA-1 or SHA-256. * @@ -38136,13 +38185,13 @@ int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, XMEMSET(req, 0, sizeof(OcspRequest)); req->heap = heap; -#ifdef NO_SHA - req->hashAlg = SHA256h; -#else - req->hashAlg = SHAh; -#endif + req->hashAlg = wc_HashGetOID(OCSP_DIGEST); if (cert) { + /* CertID.hashAlgorithm names the hash the issuer hashes were made + * with (RFC 6960 4.1.1), which is per certificate in an SM build. */ + req->hashAlg = wc_HashGetOID( + wc_HashTypeConvert(HashIdAlg(cert->signatureOID))); XMEMCPY(req->issuerHash, cert->issuerHash, KEYID_SIZE); XMEMCPY(req->issuerKeyHash, cert->issuerKeyHash, KEYID_SIZE); diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index 55d23d2e6e5..b81221e5790 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -1133,6 +1133,8 @@ static WARN_UNUSED_RESULT int Hash_DRBG_Reseed(WC_RNG* rng, const byte* seed, WOLFSSL_ATOMIC_STORE(rng->nextStirLen, WC_DRBG_NEXT_SEED_EMPTY); } +#else + (void)in_bracketed_consume; #endif ret = Hash512_DRBG_Reseed(drbg512, seed, seedSz, diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a802e7ec377..28931c1b896 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1256,6 +1256,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_cts_test(void); defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) static wc_test_ret_t certreq_no_malloc_test(void); #endif +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(HAVE_ECC) && defined(USE_CERT_BUFFERS_256) && \ + defined(WC_ASN_KEYID_HASH) +static wc_test_ret_t keyid_test(void); +#endif /* General big buffer size for many tests. */ #define FOURK_BUF 4096 @@ -3446,6 +3451,15 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("CERTREQ NOMALLOC test passed!\n"); #endif +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(HAVE_ECC) && defined(USE_CERT_BUFFERS_256) && \ + defined(WC_ASN_KEYID_HASH) + if ( (ret = keyid_test()) != 0) + TEST_FAIL("CERT KEYID test failed!\n", ret); + else + TEST_PASS("CERT KEYID test passed!\n"); +#endif + #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_TEST_CERT) && \ !defined(NO_FILESYSTEM) && !defined(NO_RSA) && defined(WOLFSSL_GEN_CERT) if ( (ret = certext_test()) != 0) @@ -39197,6 +39211,67 @@ static wc_test_ret_t certreq_no_malloc_test(void) #endif /* WOLFSSL_CERT_GEN && WOLFSSL_CERT_REQ && WOLFSSL_CERT_EXT && * HAVE_ECC && USE_CERT_BUFFERS_256 && !NO_SHA256 */ +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(HAVE_ECC) && defined(USE_CERT_BUFFERS_256) && \ + defined(WC_ASN_KEYID_HASH) +/* Cert SKID and AKID buffers are sized for the selected key identifier + * hash. */ +static wc_test_ret_t keyid_test(void) +{ + static Cert cert; + static ecc_key key; + word32 idx = 0; + wc_test_ret_t ret; + + WOLFSSL_ENTER("keyid_test"); + + ret = wc_ecc_init_ex(&key, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + + ret = wc_EccPrivateKeyDecode(ecc_key_der_256, &idx, &key, + (word32)sizeof_ecc_key_der_256); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + if (ret == 0) { + ret = wc_InitCert_ex(&cert, HEAP_HINT, devId); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + + if (ret == 0) { + ret = wc_SetSubjectKeyIdFromPublicKey(&cert, NULL, &key); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if ((ret == 0) && ((cert.skidSz <= 0) || + (cert.skidSz > (int)CTC_MAX_SKID_SIZE))) { + ret = WC_TEST_RET_ENC_NC; + } +#if !defined(WOLFSSL_SM2) || !defined(WOLFSSL_SM3) + /* SM builds size KEYID_SIZE for SM3 but may use another hash, so only + * check equality in the plain case. */ + if ((ret == 0) && (cert.skidSz != (int)KEYID_SIZE)) + ret = WC_TEST_RET_ENC_NC; +#endif + + if (ret == 0) { + ret = wc_SetAuthKeyIdFromPublicKey(&cert, NULL, &key); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if ((ret == 0) && ((cert.akidSz <= 0) || + (cert.akidSz > (int)CTC_MAX_AKID_SIZE))) { + ret = WC_TEST_RET_ENC_NC; + } + if ((ret == 0) && (cert.akidSz != cert.skidSz)) + ret = WC_TEST_RET_ENC_NC; + + wc_ecc_free(&key); + return ret; +} +#endif /* WOLFSSL_CERT_GEN && WOLFSSL_CERT_EXT && HAVE_ECC && + * USE_CERT_BUFFERS_256 && WC_ASN_KEYID_HASH */ #if defined(WOLFSSL_TEST_CERT) && defined(HAVE_ECC) && \ !defined(NO_ECC256) && !defined(NO_ECC_SECP) @@ -44648,9 +44723,8 @@ static wc_test_ret_t hkdf_test(void) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hkdf_test(void) #endif { - wc_test_ret_t ret = 0; - #if !defined(NO_SHA) || !defined(NO_SHA256) + wc_test_ret_t ret = 0; int L; byte prk[WC_MAX_DIGEST_SIZE]; byte okm1[42]; diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 455c6cc6a7f..b3d1b6747bb 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1339,11 +1339,12 @@ enum Misc_ASN { ASN_ECC_HEADER_SZ = 2, /* String type + 1 byte len */ ASN_ECC_CONTEXT_SZ = 2, /* Content specific type + 1 byte len */ #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) - KEYID_SIZE = WC_SM3_DIGEST_SIZE, -#elif defined(NO_SHA) || (!defined(NO_SHA256) && defined(WC_ASN_HASH_SHA256)) - KEYID_SIZE = WC_SHA256_DIGEST_SIZE, + /* SM builds use the fallback hash for non-SM signatures; hold the + * larger. */ + KEYID_SIZE = (WC_SM3_DIGEST_SIZE > WC_ASN_KEYID_SZ) ? + WC_SM3_DIGEST_SIZE : WC_ASN_KEYID_SZ, #else - KEYID_SIZE = WC_SHA_DIGEST_SIZE, + KEYID_SIZE = WC_ASN_KEYID_SZ, #endif RSA_INTS = 2 /* RSA ints in private key */ #ifndef WOLFSSL_RSA_PUBLIC_ONLY @@ -2291,13 +2292,8 @@ struct DecodedCert { #endif }; -#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) - #define SIGNER_DIGEST_SIZE WC_SM3_DIGEST_SIZE -#elif defined(NO_SHA) - #define SIGNER_DIGEST_SIZE WC_SHA256_DIGEST_SIZE -#else - #define SIGNER_DIGEST_SIZE WC_SHA_DIGEST_SIZE -#endif +/* Signer and CRL hashes come from CalcHashId*(). */ +#define SIGNER_DIGEST_SIZE KEYID_SIZE /* CA Signers */ /* if change layout change PERSIST_CERT_CACHE functions too */ @@ -3008,21 +3004,13 @@ struct CertStatus { typedef struct OcspEntry OcspEntry; +/* A CertID carries cert->issuerHash and issuerKeyHash, so name their hash. */ #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) -#define OCSP_DIGEST WC_HASH_TYPE_SM3 -#elif defined(NO_SHA) -#define OCSP_DIGEST WC_HASH_TYPE_SHA256 +#define OCSP_DIGEST WC_HASH_TYPE_SM3 #else -#define OCSP_DIGEST WC_HASH_TYPE_SHA -#endif - -#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) -#define OCSP_DIGEST_SIZE WC_SM3_DIGEST_SIZE -#elif defined(NO_SHA) -#define OCSP_DIGEST_SIZE WC_SHA256_DIGEST_SIZE -#else -#define OCSP_DIGEST_SIZE WC_SHA_DIGEST_SIZE +#define OCSP_DIGEST WC_ASN_KEYID_HASH_TYPE #endif +#define OCSP_DIGEST_SIZE KEYID_SIZE struct OcspEntry { @@ -3042,11 +3030,8 @@ struct OcspEntry }; #define OCSP_RESPONDER_ID_KEY_SZ 20 -#if !defined(NO_SHA) -#define OCSP_RESPONDER_ID_HASH_TYPE WC_SHA -#else -#define OCSP_RESPONDER_ID_HASH_TYPE WC_SHA256 -#endif +/* Passed to CalcHashId_ex() for a responder name. */ +#define OCSP_RESPONDER_ID_HASH_TYPE WC_ASN_KEYID_HASH_TYPE enum responderIdType { OCSP_RESPONDER_ID_INVALID = 0, OCSP_RESPONDER_ID_NAME = 1, diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index b834baf38c0..f3f4249fee7 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -128,6 +128,40 @@ enum EncPkcs8Types { ENC_PKCS8_ALG_DES3 = 652 }; +/* Hash for key identifiers (SKID, AKID, internal name and key hashes), as + * CalcHashId_ex() takes it. SHA-1 for interoperability while it is in the + * build, then SHA-256, then a SHA-3 size. */ +#if !defined(NO_SHA256) && (defined(NO_SHA) || defined(WC_ASN_HASH_SHA256)) + #define WC_ASN_KEYID_HASH_TYPE WC_HASH_TYPE_SHA256 + #define WC_ASN_KEYID_SZ 32 /* WC_SHA256_DIGEST_SIZE */ +#elif !defined(NO_SHA) + #define WC_ASN_KEYID_HASH_TYPE WC_HASH_TYPE_SHA + #define WC_ASN_KEYID_SZ 20 /* WC_SHA_DIGEST_SIZE */ +#elif defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_256) + #define WC_ASN_KEYID_HASH_TYPE WC_HASH_TYPE_SHA3_256 + #define WC_ASN_KEYID_SZ 32 /* WC_SHA3_256_DIGEST_SIZE */ +#elif defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_384) + #define WC_ASN_KEYID_HASH_TYPE WC_HASH_TYPE_SHA3_384 + #define WC_ASN_KEYID_SZ 48 /* WC_SHA3_384_DIGEST_SIZE */ +#else + /* No hash available; the size is only a buffer floor. */ + #define WC_ASN_KEYID_HASH_TYPE WC_HASH_TYPE_NONE + #define WC_ASN_KEYID_SZ 32 +#endif + +/* Set when a key identifier hash is available. */ +#if !defined(NO_SHA) || !defined(NO_SHA256) || (defined(WOLFSSL_SHA3) && \ + (!defined(WOLFSSL_NOSHA3_256) || !defined(WOLFSSL_NOSHA3_384))) + #define WC_ASN_KEYID_HASH +#endif + +/* Cert key identifier buffers: 32-byte floor, widened for a larger hash. */ +#if WC_ASN_KEYID_SZ > 32 + #define WC_CTC_MAX_KEYID_SIZE WC_ASN_KEYID_SZ +#else + #define WC_CTC_MAX_KEYID_SIZE 32 +#endif + /* Certificate file Type */ enum CertType { CERT_TYPE = 0, @@ -228,8 +262,8 @@ enum Ctc_Misc { #ifdef WOLFSSL_CERT_EXT /* AKID could contains: hash + (Option) AuthCertIssuer,AuthCertSerialNum * We support only hash */ - CTC_MAX_SKID_SIZE = 32, /* SHA256_DIGEST_SIZE */ - CTC_MAX_AKID_SIZE = 32, /* SHA256_DIGEST_SIZE */ + CTC_MAX_SKID_SIZE = WC_CTC_MAX_KEYID_SIZE, + CTC_MAX_AKID_SIZE = WC_CTC_MAX_KEYID_SIZE, CTC_MAX_CERTPOL_NB = 2, /* Max number of Certificate Policy */ CTC_MAX_CRLINFO_SZ = WC_CTC_MAX_CRLINFO_SZ, /* Arbitrary size that should be * enough for at least two From 1e5f9944cda5e927b057290b41b88be7e7d8bb39 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 1 Oct 2026 16:35:46 -0700 Subject: [PATCH 4/4] Provide XATOI under STRING_USER and in the platform templates --- IDE/GCC-ARM/Header/user_settings.h | 3 +++ IDE/SimplicityStudio/user_settings.h | 3 +++ IDE/WICED-STUDIO/user_settings.h | 3 +++ IDE/WINCE/user_settings.h | 3 +++ IDE/XCODE-FIPSv2/macOS-C++/Intel/user_settings.h | 3 +++ IDE/XCODE-FIPSv2/macOS-C++/M1/user_settings.h | 3 +++ IDE/XCODE-FIPSv2/user_settings.h | 3 +++ IDE/XCODE-FIPSv5/user_settings.h | 3 +++ IDE/XCODE-FIPSv6/user_settings.h | 3 +++ examples/configs/user_settings_template.h | 3 +++ wolfssl/wolfcrypt/types.h | 14 +++++++++++++- 11 files changed, 43 insertions(+), 1 deletion(-) diff --git a/IDE/GCC-ARM/Header/user_settings.h b/IDE/GCC-ARM/Header/user_settings.h index 971180c9bda..2ef79b4546f 100644 --- a/IDE/GCC-ARM/Header/user_settings.h +++ b/IDE/GCC-ARM/Header/user_settings.h @@ -537,6 +537,9 @@ extern unsigned int my_rng_seed_gen(void); #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/SimplicityStudio/user_settings.h b/IDE/SimplicityStudio/user_settings.h index 70f560357c4..e2ea3c91d3e 100644 --- a/IDE/SimplicityStudio/user_settings.h +++ b/IDE/SimplicityStudio/user_settings.h @@ -454,6 +454,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/WICED-STUDIO/user_settings.h b/IDE/WICED-STUDIO/user_settings.h index 686dacefee9..ac2f94b4446 100644 --- a/IDE/WICED-STUDIO/user_settings.h +++ b/IDE/WICED-STUDIO/user_settings.h @@ -531,6 +531,9 @@ extern unsigned int my_rng_seed_gen(void); #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/WINCE/user_settings.h b/IDE/WINCE/user_settings.h index f8a69633c22..8bb10ec2a1d 100644 --- a/IDE/WINCE/user_settings.h +++ b/IDE/WINCE/user_settings.h @@ -662,6 +662,9 @@ C149F3285397DFBD0C6720E14818475C3A50B10880EF9619463173A6D5ED15E7 #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/XCODE-FIPSv2/macOS-C++/Intel/user_settings.h b/IDE/XCODE-FIPSv2/macOS-C++/Intel/user_settings.h index 03b8bb81b64..8422deef538 100644 --- a/IDE/XCODE-FIPSv2/macOS-C++/Intel/user_settings.h +++ b/IDE/XCODE-FIPSv2/macOS-C++/Intel/user_settings.h @@ -528,6 +528,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/XCODE-FIPSv2/macOS-C++/M1/user_settings.h b/IDE/XCODE-FIPSv2/macOS-C++/M1/user_settings.h index d4880d52ac6..273e2fd8aa0 100644 --- a/IDE/XCODE-FIPSv2/macOS-C++/M1/user_settings.h +++ b/IDE/XCODE-FIPSv2/macOS-C++/M1/user_settings.h @@ -539,6 +539,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/XCODE-FIPSv2/user_settings.h b/IDE/XCODE-FIPSv2/user_settings.h index 14dc7ed4ac6..733900aed27 100644 --- a/IDE/XCODE-FIPSv2/user_settings.h +++ b/IDE/XCODE-FIPSv2/user_settings.h @@ -540,6 +540,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/XCODE-FIPSv5/user_settings.h b/IDE/XCODE-FIPSv5/user_settings.h index 18e21608ec7..65fba97b735 100644 --- a/IDE/XCODE-FIPSv5/user_settings.h +++ b/IDE/XCODE-FIPSv5/user_settings.h @@ -621,6 +621,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/IDE/XCODE-FIPSv6/user_settings.h b/IDE/XCODE-FIPSv6/user_settings.h index fb4aaddd0f4..e60f2941d6f 100644 --- a/IDE/XCODE-FIPSv6/user_settings.h +++ b/IDE/XCODE-FIPSv6/user_settings.h @@ -681,6 +681,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/examples/configs/user_settings_template.h b/examples/configs/user_settings_template.h index 49a48e9b60e..43d8c457467 100644 --- a/examples/configs/user_settings_template.h +++ b/examples/configs/user_settings_template.h @@ -487,6 +487,9 @@ extern "C" { #define XSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) #define XSNPRINTF snprintf + + #include + #define XATOI(s) atoi((s)) #endif diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 7a177b34eb2..d50e3eb589d 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1305,7 +1305,6 @@ binding for XSNPRINTF #define XSTRTOK(s1,d,ptr) strtok_r((s1),(d),(ptr)) #endif #endif - #if defined(WOLFSSL_CERT_EXT) || defined(HAVE_OCSP) || \ defined(HAVE_CRL_IO) || defined(HAVE_HTTP_CLIENT) || \ !defined(NO_CRYPT_BENCHMARK) || defined(OPENSSL_EXTRA) @@ -1317,6 +1316,19 @@ binding for XSNPRINTF #endif #endif /* STRING_USER */ +/* The STRING_USER platform templates override the string and memory macros + * but not XATOI, which the certificate policy OID parser needs. Narrower than + * the case above: a platform that set STRING_USER to keep the standard + * library out only reaches through a feature that cannot work + * without XATOI, and one that supplies its own still wins. */ +#if defined(STRING_USER) && !defined(XATOI) && \ + (defined(WOLFSSL_CERT_EXT) || defined(HAVE_OCSP) || \ + defined(HAVE_CRL_IO) || defined(HAVE_HTTP_CLIENT) || \ + defined(OPENSSL_EXTRA)) + #include + #define XATOI(s) atoi((s)) +#endif + #ifdef WOLFSSL_WIDE_BYTE /* Packed octet stream -> one octet per byte cell. All sizes are in byte cells; * out needs octetSz, in needs WC_PACKED_CELLS(octetSz), and they must not