diff --git a/.github/workflows/no-malloc.yml b/.github/workflows/no-malloc.yml index d6364534c1..36f48b9015 100644 --- a/.github/workflows/no-malloc.yml +++ b/.github/workflows/no-malloc.yml @@ -74,6 +74,12 @@ jobs: "--enable-curve448", "--enable-mlkem", "--enable-staticmemory", "CFLAGS=-DWOLFSSL_NO_MALLOC -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"], "check": false, + "run": [["./wolfcrypt/test/testwolfcrypt"]]}, + {"name": "no-heap-cert", "minutes": 0.8, + "configure": ["--enable-rsa", "--enable-keygen", "--enable-ecc", + "--enable-acert", "--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"]]} ] EOF diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index c359aa6e81..4ac28958d2 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -156,6 +156,15 @@ ASN Options: * WOLFSSL_ALLOW_CRIT_AKID: Allow critical Auth Key Identifier * WOLFSSL_ALLOW_CRIT_SKID: Allow critical Subject Key Identifier * WC_ASN_UNKNOWN_EXT_CB: Callback for unknown extensions + * 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. + 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. + * WC_ASN_MAX_ALTNAMES: No-heap SAN pool slot count (default 8); excess + subject alternative names are rejected. * * ASN.1 Parsing: * WOLFSSL_ASN_ALL: Enable all ASN.1 features @@ -12471,7 +12480,14 @@ void FreeAltNames(DNS_entry* altNames, void* heap) altNames->ridStringStored = 0; } #endif - XFREE(altNames, heap, DYNAMIC_TYPE_ALTNAME); +#ifdef WC_ASN_NO_HEAP + /* Only free heap nodes; no-heap pool nodes aren't owned. */ + if (altNames->entryStored) { + XFREE(altNames, heap, DYNAMIC_TYPE_ALTNAME); + } +#else + XFREE(altNames, heap, DYNAMIC_TYPE_ALTNAME); +#endif altNames = tmp; } } @@ -12483,6 +12499,9 @@ DNS_entry* AltNameNew(void* heap) ret = (DNS_entry*)XMALLOC(sizeof(DNS_entry), heap, DYNAMIC_TYPE_ALTNAME); if (ret != NULL) { XMEMSET(ret, 0, sizeof(DNS_entry)); +#ifdef WC_ASN_NO_HEAP + ret->entryStored = 1; /* heap-allocated node; FreeAltNames frees it */ +#endif } (void)heap; return ret; @@ -12631,7 +12650,9 @@ static int StoreKey(DecodedCert* cert, const byte* source, word32* srcIdx, { int ret; int length; +#ifndef WC_ASN_NO_HEAP byte* publicKey; +#endif ret = CheckBitString(source, srcIdx, &length, maxIdx, 1, NULL); if (ret == 0) { @@ -12641,6 +12662,17 @@ static int StoreKey(DecodedCert* cert, const byte* source, word32* srcIdx, } if (ret == 0) { #endif +#ifdef WC_ASN_NO_HEAP + /* No heap: reference the key in place; source must outlive the cert. */ + cert->publicKey = (byte*)&source[*srcIdx]; + cert->pubKeyStored = 0; + cert->pubKeySize = (word32)length; + #ifdef HAVE_OCSP_RESPONDER + cert->publicKeyForHash = cert->publicKey; + cert->pubKeyForHashSize = cert->pubKeySize; + #endif + *srcIdx += (word32)length; +#else publicKey = (byte*)XMALLOC((size_t)length, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY); if (publicKey == NULL) { @@ -12659,6 +12691,7 @@ static int StoreKey(DecodedCert* cert, const byte* source, word32* srcIdx, *srcIdx += (word32)length; } +#endif } return ret; @@ -13299,7 +13332,9 @@ static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx, { int ret = 0; DECL_ASNGETDATA(dataASN, eccCertKeyASN_Length); +#ifndef WC_ASN_NO_HEAP byte* publicKey; +#endif /* Validate parameters. */ if (pubKey == NULL) { @@ -13369,6 +13404,12 @@ static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx, #endif /* Store public key data length. */ cert->pubKeySize = pubKeyLen; +#ifdef WC_ASN_NO_HEAP + /* No heap: point at pubKey in the input DER, so that buffer must stay + * valid for the DecodedCert's lifetime. */ + cert->publicKey = (byte*)pubKey; + cert->pubKeyStored = 0; +#else /* Must allocated space for key. * Don't memcpy into constant pointer so use temp. */ publicKey = (byte*)XMALLOC(cert->pubKeySize, cert->heap, @@ -13383,6 +13424,7 @@ static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx, /* Indicate publicKey needs to be freed. */ cert->pubKeyStored = 1; } +#endif } FREE_ASNGETDATA(dataASN, cert->heap); @@ -14249,7 +14291,7 @@ static const byte rdnChoice[] = { }; #endif -#ifdef WOLFSSL_IP_ALT_NAME +#if defined(WOLFSSL_IP_ALT_NAME) && !defined(WC_ASN_NO_HEAP) /* used to set the human readable string for the IP address with a ASN_IP_TYPE * DNS entry * return 0 on success @@ -14318,9 +14360,9 @@ static int GenerateDNSEntryIPString(DNS_entry* entry, void* heap) return ret; } -#endif /* WOLFSSL_IP_ALT_NAME */ +#endif /* WOLFSSL_IP_ALT_NAME && !WC_ASN_NO_HEAP */ -#ifdef WOLFSSL_RID_ALT_NAME +#if defined(WOLFSSL_RID_ALT_NAME) && !defined(WC_ASN_NO_HEAP) /* used to set the human readable string for the registeredID with an * ASN_RID_TYPE DNS entry * return 0 on success @@ -14417,7 +14459,7 @@ static int GenerateDNSEntryRIDString(DNS_entry* entry, void* heap) return ret; } -#endif /* WOLFSSL_RID_ALT_NAME */ +#endif /* WOLFSSL_RID_ALT_NAME && !WC_ASN_NO_HEAP */ #ifdef WOLFSSL_ASN_TEMPLATE @@ -14466,13 +14508,57 @@ static int AddDNSEntryToList(DNS_entry** lst, DNS_entry* entry) * @return 0 on success. * @return MEMORY_E when dynamic memory allocation fails. */ -static int SetDNSEntry(void* heap, const char* str, int strLen, - int type, DNS_entry** entries) +/* No-heap SAN entries come from a caller pool; pass NULL,NULL if none. */ +#ifdef WC_ASN_NO_HEAP + #define WC_DNS_POOL(obj) (obj)->altNamePool, &(obj)->altNamePoolUsed +#else + #define WC_DNS_POOL(obj) NULL, NULL +#endif + +static int SetDNSEntry(void* heap, DNS_entry* pool, word32* poolUsed, + const char* str, int strLen, int type, + DNS_entry** entries) { DNS_entry* dnsEntry; int ret = 0; +#ifndef WC_ASN_NO_HEAP char *dnsEntry_name = NULL; +#endif +#ifdef WC_ASN_NO_HEAP + /* No heap: borrow a pool slot; name points into the source DER. */ + (void)heap; +#ifdef WOLFSSL_IP_ALT_NAME + /* No-heap path can't parse an ipString/ridString; reject rather than skip. */ + if (type == ASN_IP_TYPE) { + return ASN_PARSE_E; + } +#endif +#ifdef WOLFSSL_RID_ALT_NAME + if (type == ASN_RID_TYPE) { + return ASN_PARSE_E; + } +#endif + if ((pool == NULL) || (*poolUsed >= WC_ASN_MAX_ALTNAMES)) { + /* Distinguish pool exhaustion from a real allocation failure. */ + WOLFSSL_MSG("No-heap SAN pool exhausted; raise WC_ASN_MAX_ALTNAMES"); + ret = MEMORY_E; + dnsEntry = NULL; + } + else { + dnsEntry = &pool[(*poolUsed)++]; + XMEMSET(dnsEntry, 0, sizeof(*dnsEntry)); + dnsEntry->type = type; + dnsEntry->len = strLen; + dnsEntry->name = (char*)str; /* points into the source DER */ + dnsEntry->nameStored = 0; + } + if (ret == 0) { + ret = AddDNSEntryToList(entries, dnsEntry); + } +#else + (void)pool; + (void)poolUsed; /* TODO: consider one malloc. */ /* Allocate DNS Entry object. */ dnsEntry = AltNameNew(heap); @@ -14517,6 +14603,7 @@ static int SetDNSEntry(void* heap, const char* str, int strLen, XFREE(dnsEntry_name, heap, DYNAMIC_TYPE_ALTNAME); XFREE(dnsEntry, heap, DYNAMIC_TYPE_ALTNAME); } +#endif return ret; } @@ -18988,7 +19075,8 @@ static int DecodeOtherHelper(ASNGetData* dataASN, DecodedCert* cert, int oid) } if (ret == 0) { - ret = SetDNSEntry(cert->heap, buf, (int)bufLen, ASN_OTHER_TYPE, &entry); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), buf, (int)bufLen, + ASN_OTHER_TYPE, &entry); if (ret == 0) { #ifdef WOLFSSL_FPKI entry->oidSum = oid; @@ -19050,8 +19138,8 @@ static int DecodeOtherName(DecodedCert* cert, const byte* input, break; default: WOLFSSL_MSG("\tadding unsupported OID"); - ret = SetDNSEntry(cert->heap, name, len, ASN_OTHER_TYPE, - &cert->altNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), name, len, + ASN_OTHER_TYPE, &cert->altNames); break; } } @@ -19089,8 +19177,9 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, * reference hostname. The result is DOMAIN_NAME_MISMATCH at verification * time rather than ASN_PARSE_E at parse time. */ if (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) { - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_DNS_TYPE, &cert->altNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_DNS_TYPE, + &cert->altNames); if (ret == 0) { idx += (word32)len; } @@ -19108,16 +19197,18 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, return ASN_PARSE_E; } - ret = SetDNSEntry(cert->heap, (const char*)(input + idxDir), strLen, - ASN_DIR_TYPE, &cert->altDirNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idxDir), strLen, ASN_DIR_TYPE, + &cert->altDirNames); if (ret == 0) { idx += (word32)len; } } /* GeneralName choice: rfc822Name */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) { - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_RFC822_TYPE, &cert->altEmailNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_RFC822_TYPE, + &cert->altEmailNames); if (ret == 0) { idx += (word32)len; } @@ -19163,8 +19254,9 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, } #endif - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_URI_TYPE, &cert->altNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_URI_TYPE, + &cert->altNames); if (ret == 0) { idx += (word32)len; } @@ -19196,8 +19288,9 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, * ASN_IP_TYPE case under WOLFSSL_GEN_IPADD in src/x509.c). */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) { - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_IP_TYPE, &cert->altNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_IP_TYPE, + &cert->altNames); if (ret == 0) { idx += (word32)len; } @@ -19228,8 +19321,9 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, * when ridString is not generated, instead of failing the * whole print operation. */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) { - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_RID_TYPE, &cert->altNames); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_RID_TYPE, + &cert->altNames); if (ret == 0) { idx += (word32)len; } @@ -19244,8 +19338,9 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag, * the public altNames view (used by OpenSSL-compat APIs) reflects * exactly what the SAN extension carries. */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) { - ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len, - ASN_OTHER_TYPE, &cert->altOtherNamesRaw); + ret = SetDNSEntry(cert->heap, WC_DNS_POOL(cert), + (const char*)(input + idx), len, ASN_OTHER_TYPE, + &cert->altOtherNamesRaw); if (ret != 0) { return ret; } @@ -24205,6 +24300,7 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der) (void)der; #endif signer->keyOID = cert->keyOID; + /* pubKeyStored stays 0 under WC_ASN_NO_HEAP (signer uses heap). */ if (cert->pubKeyStored) { signer->publicKey = cert->publicKey; signer->pubKeySize = cert->pubKeySize; @@ -38800,8 +38896,8 @@ static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, /* GeneralName choice: dnsName */ if (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) { - ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, - ASN_DNS_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idx), len, ASN_DNS_TYPE, entries); if (ret == 0) { idx += (word32)len; } @@ -38819,16 +38915,16 @@ static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, return ASN_PARSE_E; } - ret = SetDNSEntry(acert->heap, (const char*)(input + idxDir), strLen, - ASN_DIR_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idxDir), strLen, ASN_DIR_TYPE, entries); if (ret == 0) { idx += (word32)len; } } /* GeneralName choice: rfc822Name */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) { - ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, - ASN_RFC822_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idx), len, ASN_RFC822_TYPE, entries); if (ret == 0) { idx += (word32)len; } @@ -38874,8 +38970,8 @@ static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, } #endif - ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, - ASN_URI_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idx), len, ASN_URI_TYPE, entries); if (ret == 0) { idx += (word32)len; } @@ -38892,8 +38988,8 @@ static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, * IP-SAN compat layer). If iPAddress name-constraint enforcement is * ever extended to attribute certificates, this gate must drop. */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE)) { - ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, - ASN_IP_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idx), len, ASN_IP_TYPE, entries); if (ret == 0) { idx += (word32)len; } @@ -38903,8 +38999,8 @@ static int DecodeAcertGeneralName(const byte* input, word32* inOutIdx, #ifdef OPENSSL_ALL /* GeneralName choice: registeredID */ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_RID_TYPE)) { - ret = SetDNSEntry(acert->heap, (const char*)(input + idx), len, - ASN_RID_TYPE, entries); + ret = SetDNSEntry(acert->heap, WC_DNS_POOL(acert), + (const char*)(input + idx), len, ASN_RID_TYPE, entries); if (ret == 0) { idx += (word32)len; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0bf7595b06..c7eb3d8026 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1027,6 +1027,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cert_test(void); static wc_test_ret_t fill_signer_twice_test(void); #endif +#if defined(WOLFSSL_TEST_CERT) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_RSA) +static wc_test_ret_t cert_no_malloc_test(void); +#endif #if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_TEST_CERT) && \ !defined(NO_FILESYSTEM) && !defined(NO_RSA) && defined(WOLFSSL_GEN_CERT) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t certext_test(void); @@ -3073,6 +3077,14 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("FILL SIGNER test passed!\n"); #endif +#if defined(WOLFSSL_TEST_CERT) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_RSA) + if ( (ret = cert_no_malloc_test()) != 0) + TEST_FAIL("CERT NOMALLOC test failed!\n", ret); + else + TEST_PASS("CERT 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) @@ -26092,6 +26104,85 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cert_test(void) } #endif /* WOLFSSL_TEST_CERT */ +#if defined(WOLFSSL_TEST_CERT) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_RSA) +/* Heap/file-free parse from a const DER buffer; checks in-place refs under + * WC_ASN_NO_HEAP. Kept outside the NO_FILESYSTEM cert block so it runs in real + * no-malloc/no-filesystem builds. */ +static wc_test_ret_t cert_no_malloc_test(void) +{ + DecodedCert cert; + wc_test_ret_t ret; +#if defined(WC_ASN_NO_HEAP) && !defined(WOLFSSL_IP_ALT_NAME) + DNS_entry* dns; +#endif + + WOLFSSL_ENTER("cert_no_malloc_test"); + +#if defined(WC_ASN_NO_HEAP) && defined(WOLFSSL_IP_ALT_NAME) + /* server_cert_der_2048 carries an IP SAN that the no-heap path fail-closes + * (rejection is gated on WOLFSSL_IP_ALT_NAME); confirm it rather than a + * successful parse. */ + InitDecodedCert(&cert, server_cert_der_2048, sizeof_server_cert_der_2048, + NULL); + ret = ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + FreeDecodedCert(&cert); + if (ret != WC_NO_ERR_TRACE(ASN_PARSE_E)) { + return WC_TEST_RET_ENC_NC; + } + ret = 0; +#else + InitDecodedCert(&cert, server_cert_der_2048, sizeof_server_cert_der_2048, + NULL); + ret = ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if ((ret == 0) && ((cert.publicKey == NULL) || (cert.pubKeySize == 0))) { + ret = WC_TEST_RET_ENC_NC; + } +#ifdef WC_ASN_NO_HEAP + if ((ret == 0) && ((cert.pubKeyStored != 0) || + ((wc_ptr_t)cert.publicKey < (wc_ptr_t)cert.source) || + ((wc_ptr_t)cert.publicKey >= + (wc_ptr_t)cert.source + cert.maxIdx))) { + ret = WC_TEST_RET_ENC_NC; + } + /* Every SAN node must reference the source DER in place, not a heap copy. */ + for (dns = cert.altNames; (ret == 0) && (dns != NULL); dns = dns->next) { + if ((dns->entryStored != 0) || + ((wc_ptr_t)dns->name < (wc_ptr_t)cert.source) || + ((wc_ptr_t)dns->name >= (wc_ptr_t)cert.source + cert.maxIdx)) { + ret = WC_TEST_RET_ENC_NC; + } + } +#endif + FreeDecodedCert(&cert); +#endif + +#if defined(HAVE_ECC) && defined(USE_CERT_BUFFERS_256) + /* ECC leaf with no IP/RID SAN: exercises StoreEccKey in-place key ref. */ + if (ret == 0) { + InitDecodedCert(&cert, ca_ecc_cert_der_256, + sizeof_ca_ecc_cert_der_256, NULL); + ret = ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if ((ret == 0) && + ((cert.publicKey == NULL) || (cert.pubKeySize == 0))) { + ret = WC_TEST_RET_ENC_NC; + } + #ifdef WC_ASN_NO_HEAP + if ((ret == 0) && ((cert.pubKeyStored != 0) || + ((wc_ptr_t)cert.publicKey < (wc_ptr_t)cert.source) || + ((wc_ptr_t)cert.publicKey >= + (wc_ptr_t)cert.source + cert.maxIdx))) { + ret = WC_TEST_RET_ENC_NC; + } + #endif + FreeDecodedCert(&cert); + } +#endif + + return ret; +} +#endif /* WOLFSSL_TEST_CERT && USE_CERT_BUFFERS_2048 && !NO_RSA */ + #if !defined(NO_ASN_TIME) && !defined(NO_RSA) && defined(WOLFSSL_TEST_CERT) && \ !defined(NO_FILESYSTEM) /* Test that FillSigner clears pubKeyStored/subjectCNStored after transferring diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index bbc56a757f..2af628b7f9 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1449,6 +1449,19 @@ 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. */ +#if defined(WOLFSSL_NO_MALLOC) && defined(NO_WOLFSSL_MEMORY) && \ + !defined(XMALLOC_USER) && !defined(WOLFSSL_STATIC_MEMORY) + #define WC_ASN_NO_HEAP +#endif + +#ifdef WC_ASN_NO_HEAP + #ifndef WC_ASN_MAX_ALTNAMES + /* Total no-heap SAN pool slots across all lists; excess is rejected. */ + #define WC_ASN_MAX_ALTNAMES 8 + #endif +#endif + typedef struct DNS_entry DNS_entry; struct DNS_entry { @@ -1456,7 +1469,8 @@ struct DNS_entry { int type; /* i.e. ASN_DNS_TYPE */ int len; /* actual DNS len */ const char* - name; /* actual DNS name */ + name; /* actual DNS name; under WC_ASN_NO_HEAP this points into + * the source DER and is NOT NUL-terminated - use len */ int nameStored; #ifdef WOLFSSL_IP_ALT_NAME char* ipString; /* human readable form of IP address */ @@ -1470,6 +1484,9 @@ struct DNS_entry { #ifdef WOLFSSL_FPKI int oidSum; /* provide oid sum for verification */ #endif +#ifdef WC_ASN_NO_HEAP + WC_BITFIELD entryStored:1; /* 1 = heap node to free; 0 = no-heap pool node */ +#endif }; #ifdef WOLFSSL_FPKI @@ -2175,6 +2192,12 @@ struct DecodedCert { WOLFSSL_AIA_ENTRY extAuthInfoList[WOLFSSL_MAX_AIA_ENTRIES]; WC_BITFIELD extAuthInfoListSz:7; WC_BITFIELD extAuthInfoListOverflow:1; +#ifdef WC_ASN_NO_HEAP + /* No-heap SAN pool; entries chain through altNames into the source DER. + * Adds ~WC_ASN_MAX_ALTNAMES * sizeof(DNS_entry) of stack per parse. */ + DNS_entry altNamePool[WC_ASN_MAX_ALTNAMES]; + word32 altNamePoolUsed; +#endif }; #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) @@ -3211,6 +3234,12 @@ struct DecodedAcert { const byte * rawAttr; /* Not owned, points into raw acert. */ word32 rawAttrLen; SignatureCtx sigCtx; +#ifdef WC_ASN_NO_HEAP + /* No-heap alt-name pool, used like DecodedCert.altNamePool. + * Adds ~WC_ASN_MAX_ALTNAMES * sizeof(DNS_entry) of stack per parse. */ + DNS_entry altNamePool[WC_ASN_MAX_ALTNAMES]; + word32 altNamePoolUsed; +#endif }; typedef struct DecodedAcert DecodedAcert;