From e7bed96d41c7b993fb183fa09205f992597c48b2 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 26 Oct 2026 17:25:57 +0000 Subject: [PATCH 01/31] Add ERR_load_strings and ERR_get_next_error_library Applications with their own error library get a library number with ERR_get_next_error_library, register strings with ERR_load_strings and build codes with ERR_PACK, as xmlsec does. Add these, ERR_unload_strings and ERR_STRING_DATA. Library numbers start at 128, as in OpenSSL. There is no string table for them, so errors put with these library numbers are not queued. --- src/ssl_err.c | 65 ++++++++++++++++++++++++++++++++++++++++++- tests/api.c | 35 +++++++++++++++++++++++ wolfssl/openssl/err.h | 8 ++++++ wolfssl/ssl.h | 15 ++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/ssl_err.c b/src/ssl_err.c index 07ad469119b..bcce19e891e 100644 --- a/src/ssl_err.c +++ b/src/ssl_err.c @@ -193,6 +193,61 @@ void wolfSSL_ERR_print_errors_cb (int (*cb)(const char *str, size_t len, { /* handled internally */ } + + /* First library number given to the application, as in OpenSSL. */ + #define WOLFSSL_ERR_LIB_FIRST_DYNAMIC 128 + + /* Get a new error library number for the application. + * + * @return Library number. + */ + int wolfSSL_ERR_get_next_error_library(void) + { + #if !defined(SINGLE_THREADED) && defined(WOLFSSL_ATOMIC_OPS) && \ + defined(WOLFSSL_ATOMIC_INITIALIZER) + static wolfSSL_Atomic_Int errLibNext = + WOLFSSL_ATOMIC_INITIALIZER(WOLFSSL_ERR_LIB_FIRST_DYNAMIC); + + WOLFSSL_ENTER("wolfSSL_ERR_get_next_error_library"); + return (int)wolfSSL_Atomic_Int_FetchAdd(&errLibNext, 1); + #else + static int errLibNext = WOLFSSL_ERR_LIB_FIRST_DYNAMIC; + + WOLFSSL_ENTER("wolfSSL_ERR_get_next_error_library"); + return errLibNext++; + #endif + } + + /* Register error strings of a library. + * + * wolfSSL has no string tables for application libraries: errors of these + * libraries are not put in the error queue. For OpenSSL compatibility. + * + * @param [in] lib Library number. + * @param [in] str Strings. Not used. + * @return 1 on success. + */ + int wolfSSL_ERR_load_strings(int lib, WOLFSSL_ERR_STRING_DATA* str) + { + WOLFSSL_ENTER("wolfSSL_ERR_load_strings"); + (void)lib; + (void)str; + return 1; + } + + /* Remove error strings of a library. For OpenSSL compatibility. + * + * @param [in] lib Library number. + * @param [in] str Strings. Not used. + * @return 1 on success. + */ + int wolfSSL_ERR_unload_strings(int lib, WOLFSSL_ERR_STRING_DATA* str) + { + WOLFSSL_ENTER("wolfSSL_ERR_unload_strings"); + (void)lib; + (void)str; + return 1; + } #endif #if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE) || \ @@ -296,7 +351,9 @@ void wolfSSL_ERR_print_errors_cb (int (*cb)(const char *str, size_t len, /* Add an error to the error queue. * - * @param [in] lib Library the error occurred in. Not used. + * @param [in] lib Library the error occurred in. Errors of application + * libraries, see wolfSSL_ERR_get_next_error_library(), + * are not queued. * @param [in] fun System function the error occurred in. WOLFSSL_SYS_*. * @param [in] err Error value. * @param [in] file Name of file the error occurred in. @@ -307,6 +364,12 @@ void wolfSSL_ERR_print_errors_cb (int (*cb)(const char *str, size_t len, { WOLFSSL_ENTER("wolfSSL_ERR_put_error"); + #ifdef OPENSSL_EXTRA + if (lib >= WOLFSSL_ERR_LIB_FIRST_DYNAMIC) { + /* Reason of an application library: no string to show. */ + return; + } + #endif #if !defined(DEBUG_WOLFSSL) && !defined(OPENSSL_EXTRA) (void)fun; (void)err; diff --git a/tests/api.c b/tests/api.c index 1d60e540cda..45dd3a43341 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22118,6 +22118,40 @@ static int test_wolfSSL_ERR_put_error(void) return EXPECT_RESULT(); } +static int test_wolfSSL_ERR_load_strings(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(OPENSSL_COEXIST) + static char libName[] = "test lib"; + static char reasonStr[] = "test reason"; + ERR_STRING_DATA lib[2]; + ERR_STRING_DATA reason[2]; + int libNum = 0; + + ExpectIntEQ(ERR_PACK(0x80, 0x1, 0x2), 0x80001002UL); + ExpectIntGE(libNum = ERR_get_next_error_library(), 128); + ExpectIntEQ(ERR_get_next_error_library(), libNum + 1); + + XMEMSET(lib, 0, sizeof(lib)); + XMEMSET(reason, 0, sizeof(reason)); + lib[0].error = ERR_PACK(libNum, 0, 0); + lib[0].string = libName; + reason[0].error = ERR_PACK(libNum, 0, 1); + reason[0].string = reasonStr; + ExpectIntEQ(ERR_load_strings(libNum, lib), 1); + ExpectIntEQ(ERR_load_strings(libNum, reason), 1); + + /* Errors of application libraries are not queued. */ + ERR_clear_error(); + ERR_put_error(libNum, 0, 1, "this file", 1); + ExpectIntEQ(ERR_peek_error(), 0); + + ExpectIntEQ(ERR_unload_strings(libNum, reason), 1); + ExpectIntEQ(ERR_unload_strings(libNum, lib), 1); +#endif + return EXPECT_RESULT(); +} + /* * This is a regression test for a bug where the peek/get error functions were * drawing from the end of the queue rather than the front. @@ -47414,6 +47448,7 @@ TEST_CASE testCases[] = { /* Can't memory test as callbacks use Assert. */ TEST_DECL(test_error_queue_per_thread), TEST_DECL(test_wolfSSL_ERR_put_error), + TEST_DECL(test_wolfSSL_ERR_load_strings), TEST_DECL(test_wolfSSL_ERR_get_error_order), TEST_DECL(test_wolfSSL_ERR_GET_REASON_version_mismatch), #ifndef NO_BIO diff --git a/wolfssl/openssl/err.h b/wolfssl/openssl/err.h index 9c3b0b1c5bf..e8522e77903 100644 --- a/wolfssl/openssl/err.h +++ b/wolfssl/openssl/err.h @@ -59,6 +59,14 @@ /* err.h for openssl */ #define ERR_load_ERR_strings wolfSSL_ERR_load_ERR_strings +#define ERR_get_next_error_library wolfSSL_ERR_get_next_error_library +#define ERR_load_strings wolfSSL_ERR_load_strings +#define ERR_unload_strings wolfSSL_ERR_unload_strings +#define ERR_STRING_DATA WOLFSSL_ERR_STRING_DATA +#define ERR_PACK(l, f, r) \ + ((((unsigned long)(l) & 0xFFUL) << 24) | \ + (((unsigned long)(f) & 0xFFFUL) << 12) | \ + ((unsigned long)(r) & 0xFFFUL)) #define ERR_load_crypto_strings wolfSSL_ERR_load_crypto_strings #define ERR_load_CRYPTO_strings wolfSSL_ERR_load_crypto_strings #define ERR_peek_last_error wolfSSL_ERR_peek_last_error diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index ad14fcf5ab4..eff03702b5b 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -3574,6 +3574,21 @@ WOLFSSL_API unsigned long wolfSSL_ERR_get_error_line_data(const char** file, int WOLFSSL_API unsigned long wolfSSL_ERR_get_error(void); WOLFSSL_API void wolfSSL_ERR_clear_error(void); +#ifdef OPENSSL_EXTRA +/* Error string of an error library registered by the application. */ +typedef struct WOLFSSL_ERR_STRING_DATA { + unsigned long error; + const char* string; +} WOLFSSL_ERR_STRING_DATA; + +/* wolfSSL_ERR_put_error() does not queue errors of these libraries. */ +WOLFSSL_API int wolfSSL_ERR_get_next_error_library(void); +WOLFSSL_API int wolfSSL_ERR_load_strings(int lib, + WOLFSSL_ERR_STRING_DATA* str); +WOLFSSL_API int wolfSSL_ERR_unload_strings(int lib, + WOLFSSL_ERR_STRING_DATA* str); +#endif + WOLFSSL_API int wolfSSL_RAND_status(void); WOLFSSL_API int wolfSSL_RAND_pseudo_bytes(unsigned char* buf, int num); From 799744f8042b14f5442efdc3b9a97b0a2f0c627e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 27 Oct 2026 20:49:38 +0000 Subject: [PATCH 02/31] Make the DSA functions behave as OpenSSL's xmlsec signs and verifies DSA through the OpenSSL API. Several DSA functions were missing or differed from OpenSSL: - Add DSA_size: the largest DER encoding of a DSA signature for the key, with r and s as large as q. Callers size signature buffers with it. It returns -1 when q is not set. - DSA_do_sign and DSA_do_verify truncate the digest to the size of q, as FIPS 186-4, 4.6 does, also where wolfCrypt's DSA does not. - DSA_do_sign supports any size of q. It parsed the raw signature with d2i_DSA_SIG, whose raw form takes only 40 or 64 bytes, so a key with a 224-bit q, as OpenSSL 3 makes for 2048-bit DSA by default, got no signature. The DSA_SIG is now built from the two halves of the raw signature, each the size of q. - DSA_do_verify rejects r or s longer than q. Both were taken up to 32 bytes and written one after the other at the size of q, so with a shorter q, r * 256 + x verified like r, and the same for s. As in OpenSSL, which needs 0 < r, s < q, a value longer than q now fails. - A public key or parameters only have no private key. DSA_get0_key returned a zero BIGNUM as the private key and DSA_do_sign signed with x = 0. The private key is now NULL and DSA_do_sign fails without one. - DSA objects are reference counted; add DSA_up_ref. EVP_PKEY_get1_DSA rebuilt the DSA from the stored DER encoding, which a key set with EVP_PKEY_assign_DSA does not have, so it returned NULL. It now returns the held key with a new reference, as OpenSSL does, and EVP_PKEY_set1_DSA takes one, so the caller and the EVP_PKEY can free the key in any order. A pkey that has only the DER encoding decodes it once and keeps the key. set1 also frees the encoding of a key it replaces, which leaked, and clears the PKCS#8 header size that came with it. - EVP_PKEY_sign with a DSA key writes the DER encoding of r and s, of at most DSA_size() bytes, and signs all of the digest it is given. EVP_PKEY_verify takes only the DER encoding: the signature must decode and encode again to the same bytes, as OpenSSL's DSA_verify checks. Before, both used raw r and s. This changes behaviour: raw signatures no longer verify with EVP_PKEY_verify. EVP_SignFinal still writes raw r and s for DSA. EVP_PKEY_sign and EVP_PKEY_verify do not support DSA keys in builds with HAVE_SELFTEST, which do not have i2d_DSA_SIG, d2i_DSA_SIG, DSA_do_sign and DSA_do_verify. --- src/pk.c | 124 +++++++++++++-- tests/api/test_evp_pkey.c | 319 ++++++++++++++++++++++++++++++++++++++ tests/api/test_evp_pkey.h | 6 + tests/api/test_ossl_dsa.c | 294 +++++++++++++++++++++++++++++++++++ tests/api/test_ossl_dsa.h | 12 +- wolfcrypt/src/evp.c | 114 +++++++++----- wolfssl/openssl/dsa.h | 5 + 7 files changed, 825 insertions(+), 49 deletions(-) diff --git a/src/pk.c b/src/pk.c index 19f1ca89179..8cc240f22a6 100644 --- a/src/pk.c +++ b/src/pk.c @@ -1017,6 +1017,7 @@ WOLFSSL_DSA* wolfSSL_DSA_new(void) { WOLFSSL_DSA* external; DsaKey* key; + int err = 0; WOLFSSL_MSG("wolfSSL_DSA_new"); @@ -1035,6 +1036,17 @@ WOLFSSL_DSA* wolfSSL_DSA_new(void) } InitwolfSSL_DSA(external); + wolfSSL_RefInit(&external->ref, &err); +#ifdef WOLFSSL_REFCNT_ERROR_RETURN + if (err != 0) { + WOLFSSL_MSG("wolfSSL_DSA_new RefInit failure"); + XFREE(key, NULL, DYNAMIC_TYPE_DSA); + XFREE(external, NULL, DYNAMIC_TYPE_DSA); + return NULL; + } +#else + (void)err; +#endif if (wc_InitDsaKey(key) != 0) { WOLFSSL_MSG("wolfSSL_DSA_new InitDsaKey failure"); XFREE(key, NULL, DYNAMIC_TYPE_DSA); @@ -1049,9 +1061,19 @@ WOLFSSL_DSA* wolfSSL_DSA_new(void) void wolfSSL_DSA_free(WOLFSSL_DSA* dsa) { + int doFree = 0; + WOLFSSL_MSG("wolfSSL_DSA_free"); if (dsa) { + int err; + + /* Only free when the last reference is released. */ + wolfSSL_RefDec(&dsa->ref, &doFree, &err); + (void)err; + } + if (doFree) { + wolfSSL_RefFree(&dsa->ref); if (dsa->internal) { FreeDsaKey((DsaKey*)dsa->internal); XFREE(dsa->internal, NULL, DYNAMIC_TYPE_DSA); @@ -1070,6 +1092,25 @@ void wolfSSL_DSA_free(WOLFSSL_DSA* dsa) } } +/* Increments the reference count of the DSA key. + * + * @param [in, out] dsa DSA key. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_DSA_up_ref(WOLFSSL_DSA* dsa) +{ + int err = 1; + + WOLFSSL_ENTER("wolfSSL_DSA_up_ref"); + + if (dsa != NULL) { + wolfSSL_RefInc(&dsa->ref, &err); + } + + return !err; +} + /* wolfSSL -> OpenSSL */ int SetDsaExternal(WOLFSSL_DSA* dsa) { @@ -1103,9 +1144,16 @@ int SetDsaExternal(WOLFSSL_DSA* dsa) return WOLFSSL_FATAL_ERROR; } - if (wolfssl_bn_set_value(&dsa->priv_key, &key->x) != 1) { - WOLFSSL_MSG("dsa x key error"); - return WOLFSSL_FATAL_ERROR; + if (key->type == DSA_PRIVATE) { + if (wolfssl_bn_set_value(&dsa->priv_key, &key->x) != 1) { + WOLFSSL_MSG("dsa x key error"); + return WOLFSSL_FATAL_ERROR; + } + } + else { + /* Public key or parameters only: no private key. */ + wolfSSL_BN_clear_free(dsa->priv_key); + dsa->priv_key = NULL; } dsa->exSet = 1; @@ -1605,6 +1653,18 @@ WOLFSSL_DSA_SIG* wolfSSL_d2i_DSA_SIG(WOLFSSL_DSA_SIG **sig, #endif /* !HAVE_SELFTEST */ +/* Use the leftmost bytes of the digest up to the size of q. + * FIPS 186-4, 4.6. */ +static int dsa_digest_len(const WOLFSSL_DSA* dsa, int dLen) +{ + int qSz = mp_unsigned_bin_size(&((DsaKey*)dsa->internal)->q); + + if ((qSz > 0) && (dLen > qSz)) { + dLen = qSz; + } + return dLen; +} + int wolfssl_local_dsa_do_sign(const unsigned char* d, int dLen, unsigned char* sigRet, WOLFSSL_DSA* dsa) { @@ -1628,6 +1688,10 @@ int wolfssl_local_dsa_do_sign(const unsigned char* d, int dLen, return WOLFSSL_FATAL_ERROR; } } + if (((DsaKey*)dsa->internal)->type != DSA_PRIVATE) { + WOLFSSL_MSG("No DSA private key"); + return WOLFSSL_FATAL_ERROR; + } WC_ALLOC_VAR_EX(tmpRng, WC_RNG, 1, NULL, DYNAMIC_TYPE_RNG, return WOLFSSL_FATAL_ERROR); @@ -1647,6 +1711,7 @@ int wolfssl_local_dsa_do_sign(const unsigned char* d, int dLen, return WOLFSSL_FATAL_ERROR; } + dLen = dsa_digest_len(dsa, dLen); if (rng) { #ifdef HAVE_SELFTEST /* FIPS 186-4 4.2, as wc_DsaSign_ex() does below. wc_DsaSign() reads @@ -1694,8 +1759,8 @@ WOLFSSL_DSA_SIG* wolfSSL_DSA_do_sign_ex(const unsigned char* digest, int inLen, WOLFSSL_DSA* dsa) { byte sigBin[DSA_MAX_SIG_SIZE]; - const byte *tmp = sigBin; - int sigLen; + WOLFSSL_DSA_SIG* sig; + int halfSz; WOLFSSL_ENTER("wolfSSL_DSA_do_sign_ex"); @@ -1714,14 +1779,24 @@ WOLFSSL_DSA_SIG* wolfSSL_DSA_do_sign_ex(const unsigned char* digest, return NULL; } - sigLen = mp_unsigned_bin_size(&((DsaKey*)dsa->internal)->q); - if (sigLen <= 0) { + halfSz = mp_unsigned_bin_size(&((DsaKey*)dsa->internal)->q); + if (halfSz <= 0 || halfSz > DSA_MAX_HALF_SIZE) { WOLFSSL_MSG("mp_unsigned_bin_size error"); return NULL; } - /* 2 * sigLen for the two points r and s */ - return wolfSSL_d2i_DSA_SIG(NULL, &tmp, 2 * sigLen); + /* Raw signature is r then s, each the size of q. */ + if ((sig = wolfSSL_DSA_SIG_new_bn()) == NULL) + return NULL; + if (mp_read_unsigned_bin((mp_int*)sig->r->internal, sigBin, + (word32)halfSz) != MP_OKAY || + mp_read_unsigned_bin((mp_int*)sig->s->internal, sigBin + halfSz, + (word32)halfSz) != MP_OKAY) { + WOLFSSL_MSG("mp_read_unsigned_bin error"); + wolfSSL_DSA_SIG_free(sig); + return NULL; + } + return sig; } #endif @@ -1747,6 +1822,7 @@ int wolfssl_local_dsa_do_verify(const unsigned char* d, int dLen, unsigned char* } } + dLen = dsa_digest_len(dsa, dLen); #ifdef HAVE_SELFTEST /* FIPS 186-4 4.2, as wc_DsaVerify_ex() does below. wc_DsaVerify() reads * exactly WC_SHA_DIGEST_SIZE bytes, so it can only serve the digests that @@ -1790,6 +1866,32 @@ int wolfSSL_DSA_bits(const WOLFSSL_DSA *d) return wolfSSL_BN_num_bits(d->p); } +/* Maximum size of a DER encoded DSA signature: r = s = q. + * + * @param [in] dsa DSA key. + * @return Size in bytes on success. + * @return -1 when q is not set. + */ +int wolfSSL_DSA_size(const WOLFSSL_DSA *dsa) +{ + int qBits; + int intSz; + + WOLFSSL_ENTER("wolfSSL_DSA_size"); + + if ((dsa == NULL) || (dsa->q == NULL)) { + return WOLFSSL_FATAL_ERROR; + } + qBits = wolfSSL_BN_num_bits(dsa->q); + if (qBits <= 0) { + return WOLFSSL_FATAL_ERROR; + } + /* INTEGER of q: one more byte when the top bit is set. */ + intSz = qBits / WOLFSSL_BIT_SIZE + 1; + intSz += 1 + (int)SetLength((word32)intSz, NULL); + return 1 + (int)SetLength((word32)(2 * intSz), NULL) + 2 * intSz; +} + #ifndef HAVE_SELFTEST int wolfSSL_DSA_do_verify_ex(const unsigned char* digest, int digest_len, WOLFSSL_DSA_SIG* sig, WOLFSSL_DSA* dsa) @@ -1835,7 +1937,7 @@ int wolfSSL_DSA_do_verify_ex(const unsigned char* digest, int digest_len, /* read r */ /* front pad with zeros */ - if ((sz = wolfSSL_BN_num_bytes(sig->r)) < 0 || sz > DSA_MAX_HALF_SIZE) + if ((sz = wolfSSL_BN_num_bytes(sig->r)) < 0 || sz > qSz) return 0; while (sz++ < qSz) *sigBinPtr++ = 0; @@ -1847,7 +1949,7 @@ int wolfSSL_DSA_do_verify_ex(const unsigned char* digest, int digest_len, /* read s */ /* front pad with zeros */ - if ((sz = wolfSSL_BN_num_bytes(sig->s)) < 0 || sz > DSA_MAX_HALF_SIZE) + if ((sz = wolfSSL_BN_num_bytes(sig->s)) < 0 || sz > qSz) return 0; while (sz++ < qSz) *sigBinPtr++ = 0; diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index aefd4908b54..b2f9d5f189c 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -910,6 +910,172 @@ int test_wolfSSL_EVP_PKEY_set1_get1_DSA(void) return EXPECT_RESULT(); } /* END test_EVP_PKEY_set1_get1_DSA */ +int test_wolfSSL_EVP_PKEY_assign_get1_DSA(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && !defined(NO_DSA) && !defined(HAVE_SELFTEST) && \ + !defined(HAVE_FIPS) && !defined(WC_FIPS_186_5_PLUS) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_FILESYSTEM) + DSA* dsa = NULL; + DSA* dsa2 = NULL; + EVP_PKEY* pkey = NULL; + DSA_SIG* sig = NULL; + const BIGNUM* priv = NULL; + unsigned char digest[WC_SHA_DIGEST_SIZE]; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectTrue((fp = XFOPEN("./certs/dsa2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + + /* Free the pkey before the key returned by get1. */ + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + ExpectNotNull(pkey = EVP_PKEY_new()); + ExpectIntEQ(EVP_PKEY_assign_DSA(pkey, dsa), 1); + if (EXPECT_FAIL()) { + DSA_free(dsa); + } + dsa = NULL; + /* get1 returns the key of the pkey with a reference for the caller. */ + ExpectNotNull(dsa2 = EVP_PKEY_get1_DSA(pkey)); + ExpectPtrEq(dsa2, EVP_PKEY_get0_DSA(pkey)); + EVP_PKEY_free(pkey); + pkey = NULL; + DSA_get0_key(dsa2, NULL, &priv); + ExpectNotNull(priv); + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), dsa2)); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, dsa2), 1); + DSA_SIG_free(sig); + sig = NULL; + DSA_free(dsa2); + dsa2 = NULL; + + /* Free the key returned by get1 before the pkey. */ + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + ExpectNotNull(pkey = EVP_PKEY_new()); + ExpectIntEQ(EVP_PKEY_assign_DSA(pkey, dsa), 1); + if (EXPECT_FAIL()) { + DSA_free(dsa); + } + dsa = NULL; + ExpectNotNull(dsa2 = EVP_PKEY_get1_DSA(pkey)); + ExpectPtrEq(dsa2, EVP_PKEY_get0_DSA(pkey)); + DSA_free(dsa2); + dsa2 = NULL; + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), + EVP_PKEY_get0_DSA(pkey))); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, + EVP_PKEY_get0_DSA(pkey)), 1); + DSA_SIG_free(sig); + EVP_PKEY_free(pkey); +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_EVP_PKEY_set1_get1_DSA_ref(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && !defined(NO_DSA) && !defined(HAVE_SELFTEST) && \ + !defined(HAVE_FIPS) && !defined(WC_FIPS_186_5_PLUS) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_FILESYSTEM) + DSA* dsa = NULL; + DSA* dsa2 = NULL; + DSA* get1 = NULL; + EVP_PKEY* pkey = NULL; + DSA_SIG* sig = NULL; + const unsigned char* p = NULL; + unsigned char digest[WC_SHA_DIGEST_SIZE]; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectTrue((fp = XFOPEN("./certs/dsa2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + ExpectNotNull(dsa2 = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa2, der, derSz), 1); + ExpectNotNull(pkey = EVP_PKEY_new()); + + /* set1 takes a reference, so the key outlives the caller's one. */ + ExpectIntEQ(EVP_PKEY_set1_DSA(pkey, dsa), 1); + ExpectPtrEq(EVP_PKEY_get0_DSA(pkey), dsa); + DSA_free(dsa); + dsa = NULL; + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), + EVP_PKEY_get0_DSA(pkey))); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, + EVP_PKEY_get0_DSA(pkey)), 1); + DSA_SIG_free(sig); + sig = NULL; + + /* get1 after set1 returns the same key. */ + ExpectNotNull(get1 = EVP_PKEY_get1_DSA(pkey)); + ExpectPtrEq(get1, EVP_PKEY_get0_DSA(pkey)); + DSA_free(get1); + get1 = NULL; + + /* set1 with the key the pkey already holds must not free it. */ + ExpectIntEQ(EVP_PKEY_set1_DSA(pkey, EVP_PKEY_get0_DSA(pkey)), 1); + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), + EVP_PKEY_get0_DSA(pkey))); + DSA_SIG_free(sig); + sig = NULL; + + /* Replacing the key drops only the reference the pkey held. */ + ExpectNotNull(get1 = EVP_PKEY_get1_DSA(pkey)); + ExpectIntEQ(EVP_PKEY_set1_DSA(pkey, dsa2), 1); + ExpectPtrEq(EVP_PKEY_get0_DSA(pkey), dsa2); + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), get1)); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, get1), 1); + DSA_SIG_free(sig); + sig = NULL; + DSA_free(get1); + get1 = NULL; + DSA_free(dsa2); + dsa2 = NULL; + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), + EVP_PKEY_get0_DSA(pkey))); + DSA_SIG_free(sig); + sig = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + + /* get1 on a pkey with only the DER decodes the key once and keeps it. + * Decoding always sets the DSA object, so drop it here. */ + p = der; + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_DSA, NULL, &p, + (long)derSz)); + if (pkey != NULL) { + DSA_free(pkey->dsa); + pkey->dsa = NULL; + pkey->ownDsa = 0; + } + ExpectNotNull(get1 = EVP_PKEY_get1_DSA(pkey)); + ExpectPtrEq(get1, EVP_PKEY_get0_DSA(pkey)); + ExpectNotNull(dsa = EVP_PKEY_get1_DSA(pkey)); + ExpectPtrEq(dsa, get1); + DSA_free(dsa); + dsa = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), get1)); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, get1), 1); + DSA_SIG_free(sig); + DSA_free(get1); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EVP_PKEY_set1_get1_EC_KEY(void) { EXPECT_DECLS; @@ -2122,6 +2288,159 @@ int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void) return EXPECT_RESULT(); } +int test_wolfSSL_EVP_PKEY_sign_verify_dsa_der(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_DSA) && !defined(HAVE_SELFTEST) && \ + defined(WOLFSSL_KEY_GEN) && !defined(NO_SHA256) && \ + defined(USE_CERT_BUFFERS_2048) + /* SHA-256 of "What is easy to do is easy not to do." signed by OpenSSL + * with dsa_key_der_2048. */ + static const unsigned char sigOssl[] = { + 0x30, 0x2c, 0x02, 0x14, 0x29, 0x47, 0x2d, 0xb8, 0x5e, 0xf9, 0x13, 0x8f, + 0xd3, 0x32, 0x70, 0x8e, 0x6b, 0x0b, 0x3d, 0x1f, 0xd2, 0xa5, 0x3c, 0x3c, + 0x02, 0x14, 0x16, 0xb7, 0xed, 0xab, 0x52, 0x90, 0x74, 0xf6, 0x09, 0x6a, + 0x66, 0x70, 0x69, 0xf7, 0x43, 0xd0, 0xdc, 0x87, 0x8b, 0x94, + }; +#if !defined(NO_FILESYSTEM) && (defined(WOLFSSL_QT) || \ + defined(OPENSSL_ALL) || defined(WOLFSSL_OPENSSH)) + /* The same digest signed by OpenSSL with certs/dsa3072.der (256-bit q). */ + static const unsigned char sigOssl256[] = { + 0x30, 0x44, 0x02, 0x20, 0x23, 0x11, 0xc8, 0xeb, 0x07, 0x67, 0xbd, 0xf2, + 0x59, 0xaa, 0x92, 0x28, 0x56, 0x13, 0x8c, 0xee, 0x72, 0x53, 0x8f, 0x01, + 0x1e, 0xf5, 0x7e, 0x8d, 0x32, 0x99, 0xf3, 0x60, 0x05, 0xec, 0xa6, 0x7a, + 0x02, 0x20, 0x04, 0x30, 0x3f, 0x84, 0xeb, 0x64, 0x28, 0xa1, 0x64, 0x30, + 0x34, 0x98, 0x91, 0x34, 0x2d, 0x06, 0x1d, 0xad, 0x93, 0x38, 0x91, 0xfc, + 0xff, 0x3f, 0xd8, 0xfd, 0xb9, 0xb0, 0x2e, 0xd3, 0x9c, 0x66, + }; + unsigned char key[TWOK_BUF]; + int keySz = 0; + XFILE fp = XBADFILE; +#endif + const char* in = "What is easy to do is easy not to do."; + const unsigned char* der = dsa_key_der_2048; + const unsigned char* p = NULL; + unsigned char hash[WC_SHA256_DIGEST_SIZE]; + unsigned char sig[128]; + unsigned char bad[sizeof(sigOssl) + 1]; + unsigned char raw[40]; + size_t sigLen = 0; + EVP_PKEY* pkey = NULL; + EVP_PKEY_CTX* ctx = NULL; + DSA* dsa = NULL; + DSA_SIG* dsaSig = NULL; + + ExpectNotNull(SHA256((const unsigned char*)in, XSTRLEN(in), hash)); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, (int)sizeof_dsa_key_der_2048), 1); + ExpectNotNull(pkey = EVP_PKEY_new()); + ExpectIntEQ(EVP_PKEY_set1_DSA(pkey, dsa), 1); + + /* Verify a DER signature made by OpenSSL. */ + ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL)); + ExpectIntEQ(EVP_PKEY_verify_init(ctx), 1); + ExpectIntEQ(EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()), 1); + ExpectIntEQ(EVP_PKEY_verify(ctx, sigOssl, sizeof(sigOssl), hash, + sizeof(hash)), 1); + XMEMCPY(bad, sigOssl, sizeof(sigOssl)); + bad[sizeof(sigOssl) - 1] ^= 0x01; + ExpectIntNE(EVP_PKEY_verify(ctx, bad, sizeof(sigOssl), hash, + sizeof(hash)), 1); + ExpectIntNE(EVP_PKEY_verify(ctx, sigOssl, sizeof(sigOssl) - 1, hash, + sizeof(hash)), 1); + /* Raw r|s is not DER. */ + XMEMCPY(raw, sigOssl + 4, 20); + XMEMCPY(raw + 20, sigOssl + 26, 20); + ExpectIntNE(EVP_PKEY_verify(ctx, raw, sizeof(raw), hash, sizeof(hash)), + 1); + /* r longer than q: r * 256 + 0x5a. */ + bad[0] = 0x30; + bad[1] = 0x2d; + bad[2] = 0x02; + bad[3] = 0x15; + XMEMCPY(bad + 4, sigOssl + 4, 20); + bad[24] = 0x5a; + XMEMCPY(bad + 25, sigOssl + 24, 22); + ExpectIntNE(EVP_PKEY_verify(ctx, bad, sizeof(bad), hash, sizeof(hash)), + 1); + /* s longer than q: s * 256 + 0xa5. */ + XMEMCPY(bad, sigOssl, sizeof(sigOssl)); + bad[1] = 0x2d; + bad[25] = 0x15; + bad[46] = 0xa5; + ExpectIntNE(EVP_PKEY_verify(ctx, bad, sizeof(bad), hash, sizeof(hash)), + 1); + hash[0] ^= 0x01; + ExpectIntNE(EVP_PKEY_verify(ctx, sigOssl, sizeof(sigOssl), hash, + sizeof(hash)), 1); + hash[0] ^= 0x01; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + /* Sign: DER of r and s, at most DSA_size() bytes. */ + ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL)); + ExpectIntEQ(EVP_PKEY_sign_init(ctx), 1); + ExpectIntEQ(EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()), 1); + ExpectIntEQ(EVP_PKEY_sign(ctx, NULL, &sigLen, hash, sizeof(hash)), 1); + ExpectIntEQ((int)sigLen, DSA_size(dsa)); + sigLen = (size_t)DSA_size(dsa) - 1; + ExpectIntNE(EVP_PKEY_sign(ctx, sig, &sigLen, hash, sizeof(hash)), 1); + sigLen = sizeof(sig); + ExpectIntEQ(EVP_PKEY_sign(ctx, sig, &sigLen, hash, sizeof(hash)), 1); + ExpectIntLE((int)sigLen, DSA_size(dsa)); + ExpectIntEQ(sig[0], 0x30); + p = sig; + ExpectNotNull(dsaSig = d2i_DSA_SIG(NULL, &p, (long)sigLen)); + ExpectPtrEq(p, sig + sigLen); + ExpectIntEQ(DSA_do_verify(hash, (int)sizeof(hash), dsaSig, dsa), 1); + DSA_SIG_free(dsaSig); + dsaSig = NULL; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL)); + ExpectIntEQ(EVP_PKEY_verify_init(ctx), 1); + ExpectIntEQ(EVP_PKEY_verify(ctx, sig, sigLen, hash, sizeof(hash)), 1); + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + DSA_free(dsa); + dsa = NULL; + +#if !defined(NO_FILESYSTEM) && (defined(WOLFSSL_QT) || \ + defined(OPENSSL_ALL) || defined(WOLFSSL_OPENSSH)) + /* 256-bit q: all of the SHA-256 digest is used. + * d2i_PrivateKey decodes DSA keys only in these builds. */ + ExpectTrue((fp = XFOPEN("./certs/dsa3072.der", "rb")) != XBADFILE); + ExpectIntGT(keySz = (int)XFREAD(key, 1, sizeof(key), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + p = key; + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_DSA, NULL, &p, keySz)); + ExpectNotNull(dsa = EVP_PKEY_get0_DSA(pkey)); + + ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL)); + ExpectIntEQ(EVP_PKEY_verify_init(ctx), 1); + ExpectIntEQ(EVP_PKEY_verify(ctx, sigOssl256, sizeof(sigOssl256), hash, + sizeof(hash)), 1); + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL)); + ExpectIntEQ(EVP_PKEY_sign_init(ctx), 1); + sigLen = sizeof(sig); + ExpectIntEQ(EVP_PKEY_sign(ctx, sig, &sigLen, hash, sizeof(hash)), 1); + p = sig; + ExpectNotNull(dsaSig = d2i_DSA_SIG(NULL, &p, (long)sigLen)); + ExpectIntEQ(DSA_do_verify(hash, (int)sizeof(hash), dsaSig, dsa), 1); + DSA_SIG_free(dsaSig); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pkey); +#endif +#endif + return EXPECT_RESULT(); +} int test_wolfSSL_EVP_PKEY_sign_verify_ec(void) { EXPECT_DECLS; diff --git a/tests/api/test_evp_pkey.h b/tests/api/test_evp_pkey.h index 6cf395cd426..af16c468993 100644 --- a/tests/api/test_evp_pkey.h +++ b/tests/api/test_evp_pkey.h @@ -38,6 +38,8 @@ int test_wolfSSL_EVP_PKEY_hkdf(void); int test_wolfSSL_EVP_PBE_scrypt(void); int test_EVP_PKEY_cmp(void); int test_wolfSSL_EVP_PKEY_set1_get1_DSA(void); +int test_wolfSSL_EVP_PKEY_assign_get1_DSA(void); +int test_wolfSSL_EVP_PKEY_set1_get1_DSA_ref(void); int test_wolfSSL_EVP_PKEY_set1_get1_EC_KEY (void); int test_wolfSSL_EVP_PKEY_get0_EC_KEY(void); int test_wolfSSL_EVP_PKEY_set1_get1_DH (void); @@ -60,6 +62,7 @@ int test_wolfSSL_EVP_SignInit_ex(void); int test_wolfSSL_EVP_PKEY_sign_verify_rsa(void); int test_wolfSSL_EVP_PKEY_sign_verify_dsa(void); int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void); +int test_wolfSSL_EVP_PKEY_sign_verify_dsa_der(void); int test_wolfSSL_EVP_PKEY_sign_verify_ec(void); int test_wolfSSL_EVP_PKEY_verify_dsa_len(void); int test_wolfSSL_EVP_MD_rsa_signing(void); @@ -101,6 +104,8 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PBE_scrypt), \ TEST_DECL_GROUP("evp_pkey", test_EVP_PKEY_cmp), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_set1_get1_DSA), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_assign_get1_DSA), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_set1_get1_DSA_ref), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_set1_get1_EC_KEY), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_get0_EC_KEY), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_set1_get1_DH), \ @@ -123,6 +128,7 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_rsa), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_dsa), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_dsa_digest_len), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_dsa_der), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_ec), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_verify_dsa_len), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_MD_rsa_signing), \ diff --git a/tests/api/test_ossl_dsa.c b/tests/api/test_ossl_dsa.c index 826f50b5e10..e396c0f2b2c 100644 --- a/tests/api/test_ossl_dsa.c +++ b/tests/api/test_ossl_dsa.c @@ -51,6 +51,10 @@ int test_DSA_do_sign_verify(void) word32 bytes; byte sigBin[DSA_SIG_SIZE]; int dsacheck; + DSA_SIG* longSig = NULL; + const BIGNUM* r0 = NULL; + const BIGNUM* s0 = NULL; + int i; #ifdef USE_CERT_BUFFERS_1024 byte tmp[ONEK_BUF]; @@ -86,6 +90,32 @@ int test_DSA_do_sign_verify(void) ExpectNotNull(sig = DSA_do_sign(digest, WC_SHA_DIGEST_SIZE, dsa)); ExpectIntEQ(DSA_do_verify(digest, WC_SHA_DIGEST_SIZE, sig, dsa), 1); + /* r and s must be less than q: r * 256 + 1 or s * 256 + 1 fails. */ + if (EXPECT_SUCCESS()) { + DSA_SIG_get0(sig, &r0, &s0); + } + for (i = 0; i < 2; i++) { + BIGNUM* r = NULL; + BIGNUM* s = NULL; + BIGNUM* big = NULL; + + ExpectNotNull(longSig = DSA_SIG_new()); + ExpectNotNull(r = BN_dup(r0)); + ExpectNotNull(s = BN_dup(s0)); + big = (i == 0) ? r : s; + ExpectIntEQ(BN_lshift(big, big, 8), 1); + ExpectIntEQ(BN_add_word(big, 1), 1); + ExpectIntEQ(DSA_SIG_set0(longSig, r, s), 1); + if (EXPECT_FAIL()) { + BN_free(r); + BN_free(s); + } + ExpectIntNE(DSA_do_verify(digest, WC_SHA_DIGEST_SIZE, longSig, dsa), + 1); + DSA_SIG_free(longSig); + longSig = NULL; + } + DSA_SIG_free(sig); DSA_free(dsa); #endif @@ -93,6 +123,48 @@ int test_DSA_do_sign_verify(void) return EXPECT_RESULT(); } +int test_wolfSSL_DSA_size(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_DSA) + DSA* dsa = NULL; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + ExpectIntEQ(DSA_size(NULL), -1); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_size(dsa), -1); + DSA_free(dsa); + dsa = NULL; + + /* q is 160 bits: SEQUENCE of two 21 byte INTEGERs. */ + ExpectTrue((fp = XFOPEN("./certs/dsa2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) { + XFCLOSE(fp); + fp = XBADFILE; + } + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + ExpectIntEQ(DSA_size(dsa), 48); + DSA_free(dsa); + dsa = NULL; + + /* q is 256 bits: SEQUENCE of two 33 byte INTEGERs. */ + ExpectTrue((fp = XFOPEN("./certs/dsa-pubkey-2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(wolfSSL_DSA_LoadDer_ex(dsa, der, derSz, + WOLFSSL_DSA_LOAD_PUBLIC), 1); + ExpectIntEQ(DSA_size(dsa), 72); + DSA_free(dsa); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_DSA_generate_parameters(void) { EXPECT_DECLS; @@ -107,6 +179,160 @@ int test_wolfSSL_DSA_generate_parameters(void) return EXPECT_RESULT(); } +int test_wolfSSL_DSA_do_sign_digest_len(void) +{ + EXPECT_DECLS; +#if !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \ + !defined(WC_FIPS_186_5_PLUS) && defined(OPENSSL_EXTRA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_DSA) + /* OpenSSL signature of the digest below with certs/dsa2048.der. */ + static const unsigned char sigDer[] = { + 0x30, 0x2d, 0x02, 0x14, 0x47, 0xba, 0xa2, 0xa0, 0x80, 0xec, 0xd5, 0xd3, + 0xd4, 0x17, 0xa5, 0xa4, 0xc8, 0xc4, 0xe9, 0x6a, 0xfd, 0x56, 0x37, 0xf4, + 0x02, 0x15, 0x00, 0x86, 0x7f, 0xa1, 0x9c, 0x1e, 0x7b, 0xcc, 0x8b, 0xfa, + 0xb3, 0xc8, 0x47, 0xd8, 0x55, 0xc8, 0x86, 0x31, 0xe1, 0xc7, 0x74 + }; + const unsigned char* p = sigDer; + unsigned char digest[WC_SHA256_DIGEST_SIZE]; + DSA* dsa = NULL; + DSA_SIG* sig = NULL; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectTrue((fp = XFOPEN("./certs/dsa2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + + /* q is 160 bits: only the leftmost 20 bytes of the digest are used. */ + ExpectNotNull(sig = d2i_DSA_SIG(NULL, &p, (long)sizeof(sigDer))); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, dsa), 1); + ExpectIntEQ(DSA_do_verify(digest, WC_SHA_DIGEST_SIZE, sig, dsa), 1); + DSA_SIG_free(sig); + sig = NULL; + + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), dsa)); + ExpectIntEQ(DSA_do_verify(digest, WC_SHA_DIGEST_SIZE, sig, dsa), 1); + DSA_SIG_free(sig); + DSA_free(dsa); +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_DSA_do_sign_q224(void) +{ + EXPECT_DECLS; +#if !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \ + !defined(WC_FIPS_186_5_PLUS) && defined(OPENSSL_EXTRA) && !defined(NO_DSA) + /* DSA key made by OpenSSL: p is 2048 bits, q is 224 bits. */ + static const unsigned char keyDer[] = { + 0x30, 0x82, 0x03, 0x4f, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xd2, 0x59, 0xe9, 0xb1, 0xf3, 0xde, 0xae, 0x34, 0xf0, 0x7e, 0x89, 0x81, + 0x28, 0x9c, 0x2a, 0x57, 0x8c, 0x89, 0x73, 0xd9, 0xf8, 0xf2, 0x76, 0x1f, + 0x42, 0x09, 0x0f, 0x55, 0x69, 0xef, 0x71, 0xe1, 0x16, 0xb9, 0x1a, 0x15, + 0x48, 0xa7, 0xd9, 0xbb, 0xdd, 0xa1, 0xfa, 0x9d, 0xa0, 0x1c, 0xaa, 0xb2, + 0x4a, 0xe4, 0xff, 0x89, 0x37, 0xb4, 0x5c, 0xa4, 0xd6, 0x8b, 0x3d, 0xc8, + 0x92, 0x98, 0x80, 0x96, 0x79, 0xaa, 0x7f, 0xf6, 0xa7, 0x88, 0x02, 0xfd, + 0xa9, 0x60, 0x0b, 0x0c, 0x9b, 0xe9, 0x28, 0x7a, 0xa8, 0x72, 0x71, 0xa1, + 0x7a, 0xc4, 0x2a, 0xf5, 0x10, 0xbb, 0x15, 0xc6, 0x29, 0x7a, 0x25, 0x41, + 0x3d, 0x4a, 0xee, 0xf9, 0x59, 0x6d, 0x95, 0xde, 0xbe, 0x4f, 0xce, 0xa7, + 0xdb, 0xd5, 0x73, 0x07, 0xc2, 0x60, 0x12, 0x04, 0xca, 0x7b, 0x45, 0xb8, + 0x33, 0x3f, 0x0c, 0x93, 0x63, 0x58, 0x20, 0x45, 0xd4, 0xac, 0x86, 0x80, + 0xc6, 0x5b, 0xa8, 0xca, 0x47, 0xcd, 0xdb, 0xd9, 0xf8, 0xd7, 0xa8, 0x51, + 0xdd, 0x72, 0x42, 0x22, 0x28, 0x8c, 0x17, 0xe7, 0xd3, 0xa7, 0x1b, 0x82, + 0x8d, 0xaf, 0xdc, 0x92, 0x92, 0xea, 0xc0, 0x7b, 0xee, 0xbe, 0x6e, 0x21, + 0x78, 0xfa, 0x82, 0xb9, 0x8b, 0xc2, 0x1d, 0x14, 0x83, 0x6e, 0x66, 0xfe, + 0xed, 0x4e, 0x3e, 0xb3, 0x46, 0x1c, 0xcf, 0xd4, 0x8e, 0xb6, 0x6c, 0x92, + 0x9a, 0xea, 0xb3, 0xa2, 0xf2, 0xb2, 0xc7, 0xb8, 0x00, 0x61, 0x62, 0xaa, + 0x8c, 0x36, 0x72, 0xa6, 0x2b, 0xaa, 0x8c, 0x37, 0xcd, 0x3e, 0x45, 0xd6, + 0xfe, 0x01, 0x22, 0xc0, 0x7d, 0x65, 0x2c, 0xc0, 0x85, 0x5e, 0x14, 0x58, + 0x4a, 0x4d, 0x57, 0x12, 0xe0, 0xf0, 0x06, 0x2a, 0xbd, 0x23, 0x30, 0x33, + 0x17, 0x30, 0x3d, 0xf6, 0xe8, 0x83, 0x11, 0x65, 0xa1, 0xda, 0x8b, 0x4a, + 0x0c, 0xd9, 0x3e, 0xbf, 0x02, 0x1d, 0x00, 0xf4, 0x7a, 0xde, 0x83, 0x3f, + 0x3e, 0xc6, 0xa1, 0x18, 0xd9, 0x6b, 0xc6, 0x9d, 0x45, 0x05, 0xfb, 0x92, + 0xb2, 0x07, 0xdd, 0xdf, 0x01, 0x98, 0x67, 0x6f, 0xaf, 0xe8, 0x35, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xad, 0xc5, 0x28, 0x1c, 0xfe, 0xfb, 0x29, 0x4f, + 0x6a, 0x00, 0x3b, 0x01, 0xd4, 0x06, 0x6f, 0x3c, 0x3b, 0xb1, 0x1b, 0xeb, + 0x35, 0x24, 0x1d, 0xe8, 0xb9, 0xac, 0x29, 0x9d, 0xbb, 0xf7, 0x33, 0x17, + 0xf6, 0xfa, 0xf3, 0xe9, 0xe2, 0xd2, 0x05, 0xaa, 0x88, 0x6c, 0x42, 0xb8, + 0xa5, 0xdb, 0x40, 0xa2, 0x67, 0x96, 0xad, 0x88, 0x94, 0x06, 0x2e, 0xc4, + 0x99, 0xe5, 0xd1, 0x4a, 0xf4, 0x86, 0x12, 0x60, 0xaf, 0xad, 0x7a, 0x65, + 0x69, 0xc1, 0x15, 0x96, 0xf4, 0xa4, 0x3e, 0x29, 0x77, 0x5d, 0x27, 0xce, + 0xa6, 0x58, 0x70, 0x4d, 0x6f, 0xf2, 0xb3, 0xc5, 0x09, 0x64, 0xd5, 0xe5, + 0x78, 0xb1, 0x08, 0x7d, 0xaa, 0x87, 0xda, 0x67, 0x02, 0xa3, 0xd2, 0x30, + 0xc5, 0xa3, 0xc6, 0x61, 0xfb, 0xa1, 0xa1, 0x31, 0x37, 0x0a, 0x2c, 0x3c, + 0x05, 0x69, 0x55, 0xcb, 0xdf, 0x2c, 0x27, 0xfc, 0x60, 0xd8, 0xd5, 0x10, + 0xd5, 0xb8, 0x2c, 0x9f, 0x33, 0xdb, 0x9d, 0x58, 0x78, 0x5f, 0xb8, 0x56, + 0x7d, 0xd6, 0x83, 0x87, 0xf3, 0xcd, 0x6d, 0x89, 0x01, 0x7a, 0xa0, 0x38, + 0xe2, 0xb1, 0x51, 0x03, 0x25, 0xd2, 0x23, 0x6c, 0x08, 0x95, 0xe4, 0xac, + 0x14, 0xa9, 0xf7, 0x7b, 0xab, 0x50, 0xf8, 0xe7, 0x98, 0x12, 0xd4, 0xb3, + 0xdf, 0x42, 0x5f, 0xec, 0x06, 0x4f, 0x3e, 0xa6, 0x3e, 0x38, 0x73, 0x27, + 0x36, 0xc4, 0x89, 0xef, 0x1e, 0x0e, 0x5e, 0x02, 0xb9, 0x68, 0x4d, 0xd0, + 0xbd, 0x5a, 0x4f, 0xc1, 0x84, 0x90, 0x62, 0xe3, 0xd9, 0xe2, 0x66, 0xcb, + 0xc5, 0x6b, 0xfb, 0xfb, 0xb0, 0xaa, 0x49, 0x51, 0x8a, 0xd3, 0xf0, 0xf9, + 0x4b, 0x44, 0x96, 0xa3, 0x24, 0xa4, 0x94, 0xca, 0xb9, 0xf7, 0x75, 0xde, + 0x0e, 0xa5, 0x7d, 0xee, 0xfe, 0x60, 0x1a, 0x32, 0x7c, 0xa5, 0x13, 0x5e, + 0xa0, 0x52, 0xb3, 0x76, 0x00, 0x37, 0x86, 0xeb, 0x02, 0x82, 0x01, 0x00, + 0x4a, 0xbf, 0x4a, 0xab, 0x69, 0xeb, 0xd2, 0x2d, 0x22, 0x23, 0xcc, 0x14, + 0x8b, 0x0f, 0xf8, 0x64, 0x14, 0x36, 0xae, 0xf7, 0xf2, 0x2a, 0x00, 0x6b, + 0xeb, 0x5e, 0xf4, 0x2b, 0x5e, 0x25, 0x67, 0x5c, 0x67, 0x62, 0x43, 0x2b, + 0x1a, 0x48, 0xff, 0xef, 0x54, 0x2b, 0x49, 0x2b, 0xfb, 0x81, 0xa9, 0xfb, + 0xd5, 0x13, 0x9e, 0x9d, 0xee, 0xf1, 0x94, 0x05, 0xa2, 0x99, 0x47, 0xe4, + 0xfc, 0xdb, 0xed, 0xff, 0x46, 0xae, 0x33, 0x3b, 0xf4, 0x5b, 0xd3, 0xb5, + 0x73, 0xc6, 0xb4, 0x12, 0x99, 0xa4, 0xac, 0xdc, 0xb2, 0x1a, 0xa5, 0xd1, + 0xcb, 0xa7, 0x78, 0xf8, 0x8f, 0x36, 0x02, 0xc4, 0x49, 0xfa, 0x1a, 0x73, + 0x78, 0x0f, 0xf3, 0xab, 0x49, 0x99, 0x96, 0x5f, 0x44, 0x41, 0xd6, 0xe5, + 0xff, 0xfd, 0x9a, 0xcf, 0x56, 0xa2, 0xcf, 0x77, 0x34, 0xed, 0xa4, 0xc1, + 0xd4, 0xbc, 0x3c, 0xcc, 0x75, 0xee, 0x74, 0xbc, 0x38, 0x8e, 0x8c, 0xe4, + 0x8b, 0x64, 0x51, 0xfa, 0xa3, 0xd6, 0x50, 0x5e, 0x26, 0x2f, 0x45, 0x3b, + 0xd4, 0x4b, 0x09, 0x40, 0x63, 0x84, 0xcc, 0xcb, 0x4c, 0xc2, 0x3f, 0x40, + 0x2e, 0xe9, 0x90, 0x6a, 0xda, 0x78, 0xc7, 0x60, 0xc9, 0xbe, 0xb2, 0x42, + 0xed, 0x11, 0xee, 0x91, 0xda, 0x1e, 0xc9, 0x70, 0xb0, 0x89, 0x31, 0x81, + 0x60, 0xc1, 0x03, 0x85, 0x57, 0x29, 0x49, 0x9b, 0x07, 0x06, 0x50, 0x0e, + 0x92, 0xf6, 0x29, 0xc7, 0x45, 0x32, 0x21, 0xd0, 0xd7, 0x20, 0x2f, 0x2f, + 0x05, 0xf1, 0x84, 0x06, 0x82, 0x04, 0x0d, 0xd9, 0x40, 0x0c, 0x3d, 0x03, + 0xa2, 0xda, 0x71, 0xc3, 0x17, 0x04, 0x00, 0x51, 0xdd, 0x5b, 0x54, 0x8f, + 0x1f, 0xfd, 0x56, 0x21, 0x26, 0x1e, 0x22, 0x32, 0xaa, 0x10, 0x6b, 0x97, + 0x42, 0x09, 0xec, 0x09, 0x83, 0xb4, 0x3e, 0x8f, 0x8d, 0x7f, 0x9b, 0x3d, + 0xbf, 0x74, 0x66, 0x89, 0x02, 0x1d, 0x00, 0xc8, 0x5f, 0x91, 0x9a, 0x20, + 0x7f, 0x5f, 0x25, 0xc3, 0xba, 0xe7, 0x4c, 0xf9, 0xe2, 0x44, 0x0e, 0x41, + 0x4d, 0xe5, 0x08, 0xa3, 0xbf, 0x5e, 0x54, 0xf0, 0xc5, 0x05, 0x28 + }; + /* OpenSSL signature of the digest below with keyDer. */ + static const unsigned char sigDer[] = { + 0x30, 0x3e, 0x02, 0x1d, 0x00, 0xd9, 0xd2, 0x9c, 0x09, 0x6d, 0x9c, 0x0b, + 0xae, 0x74, 0xb3, 0xf4, 0x0d, 0x85, 0x2b, 0x1c, 0xdf, 0xb1, 0x2d, 0x93, + 0xd5, 0x32, 0x09, 0xa3, 0x3d, 0xc8, 0xb0, 0x9c, 0x02, 0x02, 0x1d, 0x00, + 0xa6, 0x82, 0x48, 0xed, 0x20, 0xcd, 0x74, 0x17, 0x8d, 0x8a, 0x17, 0x19, + 0x4c, 0x8c, 0xae, 0x01, 0x60, 0x5c, 0xb8, 0x35, 0x11, 0x34, 0x48, 0xac, + 0x30, 0xf6, 0x21, 0x42 + }; + const unsigned char* p = sigDer; + unsigned char digest[WC_SHA256_DIGEST_SIZE]; + DSA* dsa = NULL; + DSA_SIG* sig = NULL; + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, keyDer, (int)sizeof(keyDer)), 1); + + ExpectNotNull(sig = d2i_DSA_SIG(NULL, &p, (long)sizeof(sigDer))); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, dsa), 1); + DSA_SIG_free(sig); + sig = NULL; + + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), dsa)); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, dsa), 1); + DSA_SIG_free(sig); + DSA_free(dsa); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_DSA_SIG(void) { EXPECT_DECLS; @@ -161,3 +387,71 @@ int test_wolfSSL_DSA_SIG(void) return EXPECT_RESULT(); } +int test_wolfSSL_DSA_public_key(void) +{ + EXPECT_DECLS; +#if !defined(HAVE_SELFTEST) && defined(OPENSSL_EXTRA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_DSA) + DSA* dsa = NULL; + DSA_SIG* sig = NULL; + const BIGNUM* pub = NULL; + const BIGNUM* priv = NULL; + unsigned char digest[WC_SHA_DIGEST_SIZE]; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectTrue((fp = XFOPEN("./certs/dsa-pubkey-2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(wolfSSL_DSA_LoadDer_ex(dsa, der, derSz, + WOLFSSL_DSA_LOAD_PUBLIC), 1); + + /* A public key has no private key and can not sign. */ + DSA_get0_key(dsa, &pub, &priv); + ExpectNotNull(pub); + ExpectNull(priv); + ExpectNull(sig = DSA_do_sign(digest, (int)sizeof(digest), dsa)); + DSA_SIG_free(sig); + DSA_free(dsa); +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_DSA_up_ref(void) +{ + EXPECT_DECLS; +#if !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \ + !defined(WC_FIPS_186_5_PLUS) && defined(OPENSSL_EXTRA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_DSA) + DSA* dsa = NULL; + DSA_SIG* sig = NULL; + unsigned char digest[WC_SHA_DIGEST_SIZE]; + byte der[TWOK_BUF]; + int derSz = 0; + XFILE fp = XBADFILE; + + ExpectIntEQ(DSA_up_ref(NULL), 0); + + XMEMSET(digest, 0x5a, sizeof(digest)); + ExpectTrue((fp = XFOPEN("./certs/dsa2048.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(dsa = DSA_new()); + ExpectIntEQ(DSA_LoadDer(dsa, der, derSz), 1); + ExpectIntEQ(DSA_up_ref(dsa), 1); + if (EXPECT_SUCCESS()) { + /* Releases the reference taken above. The key stays usable. */ + DSA_free(dsa); + } + ExpectNotNull(sig = DSA_do_sign(digest, (int)sizeof(digest), dsa)); + ExpectIntEQ(DSA_do_verify(digest, (int)sizeof(digest), sig, dsa), 1); + DSA_SIG_free(sig); + DSA_free(dsa); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ossl_dsa.h b/tests/api/test_ossl_dsa.h index 04cd83d0da2..192dcc00554 100644 --- a/tests/api/test_ossl_dsa.h +++ b/tests/api/test_ossl_dsa.h @@ -25,13 +25,23 @@ #include int test_DSA_do_sign_verify(void); +int test_wolfSSL_DSA_size(void); int test_wolfSSL_DSA_generate_parameters(void); +int test_wolfSSL_DSA_do_sign_digest_len(void); +int test_wolfSSL_DSA_do_sign_q224(void); int test_wolfSSL_DSA_SIG(void); +int test_wolfSSL_DSA_public_key(void); +int test_wolfSSL_DSA_up_ref(void); #define TEST_OSSL_DSA_DECLS \ TEST_DECL_GROUP("ossl_dsa", test_DSA_do_sign_verify), \ + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_size), \ TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_generate_parameters), \ - TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_SIG) + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_do_sign_digest_len), \ + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_do_sign_q224), \ + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_SIG), \ + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_public_key), \ + TEST_DECL_GROUP("ossl_dsa", test_wolfSSL_DSA_up_ref) #endif /* WOLFCRYPT_TEST_OSSL_DSA_H */ diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index f60e4d59517..7afa8186c3c 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -3502,36 +3502,41 @@ int wolfSSL_EVP_PKEY_sign(WOLFSSL_EVP_PKEY_CTX *ctx, unsigned char *sig, } #endif /* !NO_RSA */ -#ifndef NO_DSA +#if !defined(NO_DSA) && !defined(HAVE_SELFTEST) case WC_EVP_PKEY_DSA: { int bytes; int ret; + WOLFSSL_DSA_SIG* dsaSig; if (!ctx->pkey->dsa) return WOLFSSL_FAILURE; - bytes = wolfSSL_BN_num_bytes(ctx->pkey->dsa->q); - if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) + if (!ctx->pkey->dsa->exSet && + SetDsaExternal(ctx->pkey->dsa) != WOLFSSL_SUCCESS) + return WOLFSSL_FAILURE; + /* DER encoding of r and s, as in OpenSSL. */ + bytes = wolfSSL_DSA_size(ctx->pkey->dsa); + if (bytes <= 0) return WOLFSSL_FAILURE; - bytes *= 2; if (!sig) { *siglen = (size_t)bytes; return WOLFSSL_SUCCESS; } if ((int)*siglen < bytes) return WOLFSSL_FAILURE; - /* wolfssl_local_dsa_do_sign() takes the length as an int, and wolfCrypt + /* wolfSSL_DSA_do_sign_ex() takes the length as an int, and wolfCrypt * rejects anything longer than this anyway. */ if (tbslen > WC_MAX_DIGEST_SIZE) return WOLFSSL_FAILURE; - ret = wolfssl_local_dsa_do_sign(tbs, (int)tbslen, sig, ctx->pkey->dsa); - /* wolfssl_local_dsa_do_sign() can return WOLFSSL_FATAL_ERROR */ - if (ret != WOLFSSL_SUCCESS) - return ret; - if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) + dsaSig = wolfSSL_DSA_do_sign_ex(tbs, (int)tbslen, ctx->pkey->dsa); + if (dsaSig == NULL) return WOLFSSL_FAILURE; - *siglen = (size_t)bytes; + ret = wolfSSL_i2d_DSA_SIG(dsaSig, &sig); + wolfSSL_DSA_SIG_free(dsaSig); + if (ret <= 0) + return WOLFSSL_FAILURE; + *siglen = (size_t)ret; return WOLFSSL_SUCCESS; } -#endif /* NO_DSA */ +#endif /* !NO_DSA && !HAVE_SELFTEST */ #ifdef HAVE_ECC case WC_EVP_PKEY_EC: { @@ -3644,25 +3649,36 @@ int wolfSSL_EVP_PKEY_verify(WOLFSSL_EVP_PKEY_CTX *ctx, const unsigned char *sig, ctx->padding, wolfSSL_EVP_MD_type(ctx->mgf1_md), ctx->saltlen); #endif /* !NO_RSA */ -#ifndef NO_DSA +#if !defined(NO_DSA) && !defined(HAVE_SELFTEST) case WC_EVP_PKEY_DSA: { - int dsacheck = 0; - int bytes; - if (!ctx->pkey->dsa || tbslen < WC_SHA_DIGEST_SIZE) - return WOLFSSL_FAILURE; - bytes = wolfSSL_BN_num_bytes(ctx->pkey->dsa->q); - if (bytes <= 0 || siglen < (size_t)bytes * 2) + int ret; + const unsigned char* p = sig; + unsigned char* der = NULL; + WOLFSSL_DSA_SIG* dsaSig; + if (!ctx->pkey->dsa) return WOLFSSL_FAILURE; - /* wolfssl_local_dsa_do_verify() takes the length as an int, and wolfCrypt + /* wolfSSL_DSA_do_verify_ex() takes the length as an int, and wolfCrypt * rejects anything longer than this anyway. */ if (tbslen > WC_MAX_DIGEST_SIZE) return WOLFSSL_FAILURE; - if (wolfssl_local_dsa_do_verify(tbs, (int)tbslen, (unsigned char *)sig, - ctx->pkey->dsa, &dsacheck) != WOLFSSL_SUCCESS || dsacheck != 1) + dsaSig = wolfSSL_d2i_DSA_SIG(NULL, &p, (long)siglen); + if (dsaSig == NULL) return WOLFSSL_FAILURE; - return WOLFSSL_SUCCESS; + /* Only the DER encoding of r and s, as in OpenSSL. */ + ret = wolfSSL_i2d_DSA_SIG(dsaSig, &der); + if ((ret > 0) && ((size_t)ret == siglen) && + (XMEMCMP(der, sig, (size_t)ret) == 0)) { + ret = wolfSSL_DSA_do_verify_ex(tbs, (int)tbslen, dsaSig, + ctx->pkey->dsa); + } + else { + ret = 0; + } + XFREE(der, NULL, DYNAMIC_TYPE_ASN1); + wolfSSL_DSA_SIG_free(dsaSig); + return (ret == 1) ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } -#endif /* NO_DSA */ +#endif /* !NO_DSA && !HAVE_SELFTEST */ #ifdef HAVE_ECC case WC_EVP_PKEY_EC: { @@ -9675,7 +9691,7 @@ int wolfSSL_EVP_PKEY_set1_RSA(WOLFSSL_EVP_PKEY *pkey, WOLFSSL_RSA *key) #endif /* !NO_RSA */ #if !defined (NO_DSA) && !defined(HAVE_SELFTEST) && defined(WOLFSSL_KEY_GEN) -/* with set1 functions the pkey struct does not own the DSA structure +/* with set1 functions the pkey struct takes a reference to the DSA structure * * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure */ @@ -9689,9 +9705,14 @@ int wolfSSL_EVP_PKEY_set1_DSA(WOLFSSL_EVP_PKEY *pkey, WOLFSSL_DSA *key) WOLFSSL_ENTER("wolfSSL_EVP_PKEY_set1_DSA"); if((pkey == NULL) || (key == NULL))return WOLFSSL_FAILURE; + /* Take the reference first: key may be the one the pkey holds. */ + if (wolfSSL_DSA_up_ref(key) != 1) { + WOLFSSL_MSG("wolfSSL_DSA_up_ref failed"); + return WOLFSSL_FAILURE; + } clearEVPPkeyKeys(pkey); pkey->dsa = key; - pkey->ownDsa = 0; /* pkey does not own DSA */ + pkey->ownDsa = 1; /* pkey holds a reference */ pkey->type = WC_EVP_PKEY_DSA; if (key->inSet == 0) { if (SetDsaInternal(key) != WOLFSSL_SUCCESS) { @@ -9731,6 +9752,16 @@ int wolfSSL_EVP_PKEY_set1_DSA(WOLFSSL_EVP_PKEY *pkey, WOLFSSL_DSA *key) return WOLFSSL_FAILURE; } + /* Drop the encoding of a key held before. */ + if (pkey->pkey.ptr != NULL) { + if (pkey->pkey_sz > 0) { + ForceZero(pkey->pkey.ptr, (word32)pkey->pkey_sz); + } + XFREE(pkey->pkey.ptr, pkey->heap, DYNAMIC_TYPE_DER); + pkey->pkey_sz = 0; + } + /* The new encoding has no PKCS#8 header. */ + pkey->pkcs8HeaderSz = 0; pkey->pkey.ptr = (char*)XMALLOC((size_t)derSz, pkey->heap, DYNAMIC_TYPE_DER); if (pkey->pkey.ptr == NULL) { @@ -9753,6 +9784,9 @@ WOLFSSL_DSA* wolfSSL_EVP_PKEY_get0_DSA(struct WOLFSSL_EVP_PKEY *pkey) return pkey->dsa; } +/* Returns the DSA key of the pkey with its reference count incremented. + * The caller frees it with wolfSSL_DSA_free(). + */ WOLFSSL_DSA* wolfSSL_EVP_PKEY_get1_DSA(WOLFSSL_EVP_PKEY* key) { WOLFSSL_DSA* local; @@ -9763,14 +9797,18 @@ WOLFSSL_DSA* wolfSSL_EVP_PKEY_get1_DSA(WOLFSSL_EVP_PKEY* key) WOLFSSL_MSG("Bad function argument"); return NULL; } - - local = wolfSSL_DSA_new(); - if (local == NULL) { - WOLFSSL_MSG("Error creating a new WOLFSSL_DSA structure"); + if (key->type != WC_EVP_PKEY_DSA) { + WOLFSSL_MSG("WOLFSSL_EVP_PKEY does not hold a DSA key"); return NULL; } - if (key->type == WC_EVP_PKEY_DSA) { + if (key->dsa == NULL) { + /* Decode the stored DER and keep the key on the pkey. */ + local = wolfSSL_DSA_new(); + if (local == NULL) { + WOLFSSL_MSG("Error creating a new WOLFSSL_DSA structure"); + return NULL; + } if (wolfSSL_DSA_LoadDer(local, (const unsigned char*)key->pkey.ptr, key->pkey_sz) != WOLFSSL_SUCCESS) { /* now try public key */ @@ -9778,16 +9816,18 @@ WOLFSSL_DSA* wolfSSL_EVP_PKEY_get1_DSA(WOLFSSL_EVP_PKEY* key) (const unsigned char*)key->pkey.ptr, key->pkey_sz, WOLFSSL_DSA_LOAD_PUBLIC) != WOLFSSL_SUCCESS) { wolfSSL_DSA_free(local); - local = NULL; + return NULL; } } + key->dsa = local; + key->ownDsa = 1; } - else { - WOLFSSL_MSG("WOLFSSL_EVP_PKEY does not hold a DSA key"); - wolfSSL_DSA_free(local); - local = NULL; + + if (wolfSSL_DSA_up_ref(key->dsa) != 1) { + WOLFSSL_MSG("wolfSSL_DSA_up_ref failed"); + return NULL; } - return local; + return key->dsa; } #endif /* !NO_DSA && !HAVE_SELFTEST && WOLFSSL_KEY_GEN */ diff --git a/wolfssl/openssl/dsa.h b/wolfssl/openssl/dsa.h index 4d7263dcb39..96e02e10516 100644 --- a/wolfssl/openssl/dsa.h +++ b/wolfssl/openssl/dsa.h @@ -51,11 +51,13 @@ struct WOLFSSL_DSA { void* internal; /* our Dsa Key */ char inSet; /* internal set from external ? */ char exSet; /* external set from internal ? */ + wolfSSL_Ref ref; /* Reference count information. */ }; WOLFSSL_API WOLFSSL_DSA* wolfSSL_DSA_new(void); WOLFSSL_API void wolfSSL_DSA_free(WOLFSSL_DSA* dsa); +WOLFSSL_API int wolfSSL_DSA_up_ref(WOLFSSL_DSA* dsa); #if !defined(NO_FILESYSTEM) && !defined(NO_STDIO_FILESYSTEM) WOLFSSL_API int wolfSSL_DSA_print_fp(XFILE fp, WOLFSSL_DSA* dsa, int indent); #endif /* !NO_FILESYSTEM && NO_STDIO_FILESYSTEM */ @@ -96,6 +98,7 @@ WOLFSSL_API int wolfSSL_DSA_do_verify( const unsigned char* d, unsigned char* sig, WOLFSSL_DSA* dsa, int *dsacheck); WOLFSSL_API int wolfSSL_DSA_bits(const WOLFSSL_DSA *d); +WOLFSSL_API int wolfSSL_DSA_size(const WOLFSSL_DSA *dsa); WOLFSSL_API WOLFSSL_DSA_SIG* wolfSSL_DSA_SIG_new(void); WOLFSSL_API void wolfSSL_DSA_SIG_free(WOLFSSL_DSA_SIG *sig); @@ -131,12 +134,14 @@ typedef WOLFSSL_DSA DSA; #define DSA_new wolfSSL_DSA_new #define DSA_free wolfSSL_DSA_free +#define DSA_up_ref wolfSSL_DSA_up_ref #define DSA_print_fp wolfSSL_DSA_print_fp #define DSA_LoadDer wolfSSL_DSA_LoadDer #define DSA_generate_key wolfSSL_DSA_generate_key #define DSA_generate_parameters wolfSSL_DSA_generate_parameters #define DSA_generate_parameters_ex wolfSSL_DSA_generate_parameters_ex +#define DSA_size wolfSSL_DSA_size #define DSA_get0_pqg wolfSSL_DSA_get0_pqg #define DSA_set0_pqg wolfSSL_DSA_set0_pqg #define DSA_get0_key wolfSSL_DSA_get0_key From 39c05dd6ad8bfb5b04b0f4dbefd42ae52a5d635e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 26 Oct 2026 17:26:01 +0000 Subject: [PATCH 03/31] Clear private keys and cipher state when they are freed The private key BIGNUM of DSA, DH and EC keys was freed without being zeroed, so the key stayed in freed memory. Use BN_clear_free for it in DSA_free, DH_free and EC_KEY_free, and where a private key is replaced. The cipher union of an EVP_CIPHER_CTX, which holds the key schedule, was left as it was after cleanup, and EVP_CIPHER_CTX_free freed the context without zeroing it. A DES key schedule stayed in freed memory. Zero the union in cleanup and the whole context before it is freed. --- src/pk.c | 8 ++++---- src/pk_ec.c | 6 +++--- tests/api/test_evp_cipher.c | 30 ++++++++++++++++++++++++++++++ tests/api/test_evp_cipher.h | 2 ++ wolfcrypt/src/evp.c | 3 +++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/pk.c b/src/pk.c index 8cc240f22a6..a96f35a3a80 100644 --- a/src/pk.c +++ b/src/pk.c @@ -1079,7 +1079,7 @@ void wolfSSL_DSA_free(WOLFSSL_DSA* dsa) XFREE(dsa->internal, NULL, DYNAMIC_TYPE_DSA); dsa->internal = NULL; } - wolfSSL_BN_free(dsa->priv_key); + wolfSSL_BN_clear_free(dsa->priv_key); wolfSSL_BN_free(dsa->pub_key); wolfSSL_BN_free(dsa->g); wolfSSL_BN_free(dsa->q); @@ -1435,7 +1435,7 @@ int wolfSSL_DSA_set0_key(WOLFSSL_DSA *d, WOLFSSL_BIGNUM *pub_key, d->pub_key = pub_key; } if (priv_key != NULL) { - wolfSSL_BN_free(d->priv_key); + wolfSSL_BN_clear_free(d->priv_key); d->priv_key = priv_key; } @@ -2976,7 +2976,7 @@ void wolfSSL_DH_free(WOLFSSL_DH* dh) } /* Dispose of any allocated BNs. */ - wolfSSL_BN_free(dh->priv_key); + wolfSSL_BN_clear_free(dh->priv_key); wolfSSL_BN_free(dh->pub_key); wolfSSL_BN_free(dh->g); wolfSSL_BN_free(dh->p); @@ -4939,7 +4939,7 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh) if (ret == 1) { /* Dispose of old public and private keys. */ wolfSSL_BN_free(dh->pub_key); - wolfSSL_BN_free(dh->priv_key); + wolfSSL_BN_clear_free(dh->priv_key); /* Allocate new public and private keys. */ dh->pub_key = wolfSSL_BN_new(); diff --git a/src/pk_ec.c b/src/pk_ec.c index 91da8ed6fb5..e55d16496c5 100644 --- a/src/pk_ec.c +++ b/src/pk_ec.c @@ -3140,7 +3140,7 @@ void wolfSSL_EC_KEY_free(WOLFSSL_EC_KEY *key) wolfSSL_RefFree(&key->ref); /* Dispose of private key. */ - wolfSSL_BN_free(key->priv_key); + wolfSSL_BN_clear_free(key->priv_key); wolfSSL_EC_POINT_free(key->pub_key); wolfSSL_EC_GROUP_free(key->group); if (key->internal != NULL) { @@ -4672,7 +4672,7 @@ int wolfSSL_EC_KEY_set_private_key(WOLFSSL_EC_KEY *key, if (ret == 1) { /* Free key if previously set. */ if (key->priv_key != NULL) { - wolfSSL_BN_free(key->priv_key); + wolfSSL_BN_clear_free(key->priv_key); } /* Duplicate the BN passed in. */ @@ -4686,7 +4686,7 @@ int wolfSSL_EC_KEY_set_private_key(WOLFSSL_EC_KEY *key, if ((ret == 1) && (SetECKeyInternal(key) != 1)) { WOLFSSL_MSG("SetECKeyInternal failed"); /* Dispose of new private key on error. */ - wolfSSL_BN_free(key->priv_key); + wolfSSL_BN_clear_free(key->priv_key); key->priv_key = NULL; ret = 0; } diff --git a/tests/api/test_evp_cipher.c b/tests/api/test_evp_cipher.c index 8f0f0f816f1..45812c30619 100644 --- a/tests/api/test_evp_cipher.c +++ b/tests/api/test_evp_cipher.c @@ -251,6 +251,36 @@ int test_wolfSSL_EVP_CIPHER_CTX_set_iv(void) return EXPECT_RESULT(); } +int test_wolfSSL_EVP_CIPHER_CTX_cleanup_wipe(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_DES3) + EVP_CIPHER_CTX* ctx = NULL; + byte key[DES3_KEY_SIZE]; + byte iv[DES_BLOCK_SIZE]; + const byte* p; + size_t i; + byte nonZero = 0; + + XMEMSET(key, 0xa5, sizeof(key)); + XMEMSET(iv, 0, sizeof(iv)); + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_CipherInit(ctx, EVP_des_ede3_cbc(), key, iv, 1), + WOLFSSL_SUCCESS); + ExpectIntEQ(EVP_CIPHER_CTX_cleanup(ctx), WOLFSSL_SUCCESS); + /* No key schedule left behind. */ + if (ctx != NULL) { + p = (const byte*)&ctx->cipher; + for (i = 0; i < sizeof(ctx->cipher); i++) { + nonZero |= p[i]; + } + } + ExpectIntEQ(nonZero, 0); + EVP_CIPHER_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EVP_get_cipherbynid(void) { EXPECT_DECLS; diff --git a/tests/api/test_evp_cipher.h b/tests/api/test_evp_cipher.h index 3acc4a4d293..8bc04bcbe22 100644 --- a/tests/api/test_evp_cipher.h +++ b/tests/api/test_evp_cipher.h @@ -28,6 +28,7 @@ int test_wolfSSL_EVP_CIPHER_CTX(void); int test_wolfSSL_EVP_CIPHER_CTX_iv_length(void); int test_wolfSSL_EVP_CIPHER_CTX_key_length(void); int test_wolfSSL_EVP_CIPHER_CTX_set_iv(void); +int test_wolfSSL_EVP_CIPHER_CTX_cleanup_wipe(void); int test_wolfSSL_EVP_get_cipherbynid(void); int test_wolfSSL_EVP_CIPHER_block_size(void); int test_wolfSSL_EVP_CIPHER_iv_length(void); @@ -76,6 +77,7 @@ int test_evp_cipher_update_no_padding_buffered(void); TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_iv_length), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_key_length), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_set_iv), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_cleanup_wipe), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_get_cipherbynid), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_block_size), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_iv_length), \ diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 7afa8186c3c..4faeaf2a55e 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -495,6 +495,7 @@ void wolfSSL_EVP_CIPHER_CTX_free(WOLFSSL_EVP_CIPHER_CTX *ctx) if (ctx) { WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_free"); wolfSSL_EVP_CIPHER_CTX_cleanup(ctx); + ForceZero(ctx, sizeof(*ctx)); XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -7106,6 +7107,8 @@ void wolfSSL_EVP_init(void) WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_cleanup"); if (ctx) { wolfSSL_EVP_CIPHER_CTX_cleanup_cipher(ctx); + /* Key schedules of all ciphers, DES included. */ + ForceZero(&ctx->cipher, sizeof(ctx->cipher)); ctx->cipherType = WOLFSSL_EVP_CIPH_TYPE_INIT; /* not yet initialized */ #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) if (ctx->key) { From 4a83150c31f87a95bd6e4834570daeab83fa9c1c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 27 Oct 2026 08:11:15 +0000 Subject: [PATCH 04/31] Add the RSA_padding_add/check_PKCS1_OAEP functions Applications that use OAEP with a label build the encoding themselves and encrypt with RSA_NO_PADDING, as xmlsec does for every OAEP key transport. Add RSA_padding_add_PKCS1_OAEP_mgf1 and RSA_padding_check_PKCS1_OAEP_mgf1 on top of the wolfCrypt OAEP encoding. They take the hash of the label and the hash of MGF1. As in OpenSSL, a NULL md is SHA-1 and a NULL mgf1md is md. A hash that wolfCrypt OAEP does not support fails. RSA_padding_add_PKCS1_OAEP and RSA_padding_check_PKCS1_OAEP call them with NULL hashes. The OAEP seed comes from the global RNG, as for BN_rand, instead of a new DRBG for each call. The RNG is shared, so the padding holds its lock. As in RAND_bytes, the RNG is looked up under the lock, since another thread can free it with RAND_Cleanup. The check takes an encoded message without its leading zero bytes, as OpenSSL does, and to may be from. Differences from OpenSSL: the block is at most RSA_MAX_SIZE bits, and an empty message fails, since wolfCrypt reports it as it reports a decoding error. --- src/pk_rsa.c | 201 ++++++++++++++++++++++++++ tests/api/test_ossl_rsa.c | 291 ++++++++++++++++++++++++++++++++++++++ tests/api/test_ossl_rsa.h | 4 + wolfssl/openssl/rsa.h | 30 ++++ 4 files changed, 526 insertions(+) diff --git a/src/pk_rsa.c b/src/pk_rsa.c index b029fcc4051..b8abd54dca2 100644 --- a/src/pk_rsa.c +++ b/src/pk_rsa.c @@ -2952,6 +2952,207 @@ int wolfSSL_RSA_verify_PKCS1_PSS(WOLFSSL_RSA *rsa, const unsigned char *mHash, (OPENSSL_ALL || WOLFSSL_ASIO || WOLFSSL_HAPROXY || WOLFSSL_NGINX) */ #endif /* WC_RSA_PSS */ +#ifdef WOLFSSL_RSA_OAEP_COMPAT +/* Get the wolfCrypt hash and MGF of OAEP. As in OpenSSL, no md is SHA-1 and + * no mgf1md is md. + * + * @param [in] md Hash of the label. May be NULL. + * @param [in] mgf1md Hash of MGF1. May be NULL. + * @param [out] hash wolfCrypt hash type. + * @param [out] mgf wolfCrypt MGF. + * @return 1 on success. + * @return 0 when a hash is not supported. + */ +static int rsa_oaep_hash_mgf(const WOLFSSL_EVP_MD* md, + const WOLFSSL_EVP_MD* mgf1md, enum wc_HashType* hash, int* mgf) +{ + int ret = 1; + + *hash = (md != NULL) ? EvpMd2MacType(md) : WC_HASH_TYPE_SHA; + *mgf = wc_hash2mgf((mgf1md != NULL) ? EvpMd2MacType(mgf1md) : *hash); + if ((*hash == WC_HASH_TYPE_NONE) || (*mgf == WC_MGF1NONE)) { + WOLFSSL_MSG("OAEP hash not supported"); + ret = 0; + } + + return ret; +} + +/* Add PKCS#1 OAEP padding to a message. + * RFC 8017, 7.1.1, step 2. + * + * @param [out] to Encoded message of tlen bytes. + * @param [in] tlen Size of the RSA modulus in bytes. + * @param [in] from Message. + * @param [in] flen Length of message in bytes. + * @param [in] param Label. May be NULL when plen is 0. + * @param [in] plen Length of label in bytes. + * @param [in] md Hash of the label. NULL for SHA-1. + * @param [in] mgf1md Hash of MGF1. NULL for md. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char* to, int tlen, + const unsigned char* from, int flen, const unsigned char* param, int plen, + const WOLFSSL_EVP_MD* md, const WOLFSSL_EVP_MD* mgf1md) +{ + int ret = 1; + WC_RNG* rng = NULL; + enum wc_HashType hash = WC_HASH_TYPE_NONE; + int mgf = WC_MGF1NONE; + + WOLFSSL_ENTER("wolfSSL_RSA_padding_add_PKCS1_OAEP_mgf1"); + + /* wolfCrypt pads blocks up to the size of the largest RSA key. */ + if ((to == NULL) || (tlen <= 0) || (tlen > RSA_MAX_SIZE / 8) || + (flen < 0) || ((from == NULL) && (flen > 0)) || (plen < 0) || + ((param == NULL) && (plen > 0))) { + WOLFSSL_MSG("Bad argument"); + ret = 0; + } + if (ret == 1) { + ret = rsa_oaep_hash_mgf(md, mgf1md, &hash, &mgf); + } + /* Create the global RNG before locking. Creating it takes the lock. */ + if ((ret == 1) && (wolfssl_make_global_rng() == NULL)) { + WOLFSSL_MSG("Failed to make RNG"); + ret = 0; + } + /* Global RNG is shared, lock it while padding. */ + if ((ret == 1) && (wc_LockMutex(&globalRNGMutex) != 0)) { + WOLFSSL_MSG("Bad Lock Mutex rng"); + ret = 0; + } + if (ret == 1) { + /* Get it under the lock. It may have been freed since it was made. */ + rng = wolfssl_get_global_rng(); + if (rng == NULL) { + ret = 0; + } + else if (wc_RsaPad_ex((from != NULL) ? from : to, (word32)flen, to, + (word32)tlen, RSA_BLOCK_TYPE_2, rng, WC_RSA_OAEP_PAD, hash, + mgf, (byte*)param, (word32)plen, 0, tlen * 8, NULL) != 0) { + WOLFSSL_MSG("wc_RsaPad_ex failed"); + ret = 0; + } + wc_UnLockMutex(&globalRNGMutex); + } + + return ret; +} + +/* Add PKCS#1 OAEP padding with SHA-1 and MGF1-SHA-1 to a message. + * + * @param [out] to Encoded message of tlen bytes. + * @param [in] tlen Size of the RSA modulus in bytes. + * @param [in] from Message. + * @param [in] flen Length of message in bytes. + * @param [in] param Label. May be NULL when plen is 0. + * @param [in] plen Length of label in bytes. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_RSA_padding_add_PKCS1_OAEP(unsigned char* to, int tlen, + const unsigned char* from, int flen, const unsigned char* param, int plen) +{ + WOLFSSL_ENTER("wolfSSL_RSA_padding_add_PKCS1_OAEP"); + + return wolfSSL_RSA_padding_add_PKCS1_OAEP_mgf1(to, tlen, from, flen, param, + plen, NULL, NULL); +} + +/* Check PKCS#1 OAEP padding and get the message. + * RFC 8017, 7.1.2, step 3. + * + * from may lack the leading zero bytes of the encoded message. + * + * @param [out] to Buffer for the message. May be from. + * @param [in] tlen Size of to in bytes. + * @param [in] from Encoded message. + * @param [in] flen Length of encoded message in bytes. + * @param [in] num Size of the RSA modulus in bytes. + * @param [in] param Label. May be NULL when plen is 0. + * @param [in] plen Length of label in bytes. + * @param [in] md Hash of the label. NULL for SHA-1. + * @param [in] mgf1md Hash of MGF1. NULL for md. + * @return Length of the message on success. + * @return -1 on failure. wolfCrypt does not tell an empty message from a + * decoding error, so an empty message also fails. + */ +int wolfSSL_RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char* to, int tlen, + const unsigned char* from, int flen, int num, const unsigned char* param, + int plen, const WOLFSSL_EVP_MD* md, const WOLFSSL_EVP_MD* mgf1md) +{ + int ret = 0; + byte* em = NULL; + byte* msg = NULL; + enum wc_HashType hash = WC_HASH_TYPE_NONE; + int mgf = WC_MGF1NONE; + + WOLFSSL_ENTER("wolfSSL_RSA_padding_check_PKCS1_OAEP_mgf1"); + + /* wolfCrypt decodes blocks up to the size of the largest RSA key. */ + if ((to == NULL) || (tlen < 0) || (from == NULL) || (flen < 0) || + (num <= 0) || (num > RSA_MAX_SIZE / 8) || (flen > num) || + (plen < 0) || ((param == NULL) && (plen > 0))) { + WOLFSSL_MSG("Bad argument"); + ret = WOLFSSL_FATAL_ERROR; + } + if ((ret == 0) && (rsa_oaep_hash_mgf(md, mgf1md, &hash, &mgf) != 1)) { + ret = WOLFSSL_FATAL_ERROR; + } + if (ret == 0) { + em = (byte*)XMALLOC((size_t)num, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (em == NULL) { + ret = WOLFSSL_FATAL_ERROR; + } + } + if (ret == 0) { + /* Put back the leading zero bytes. */ + XMEMSET(em, 0, (size_t)(num - flen)); + XMEMCPY(em + num - flen, from, (size_t)flen); + ret = wc_RsaUnPad_ex(em, (word32)num, &msg, RSA_BLOCK_TYPE_2, + WC_RSA_OAEP_PAD, hash, mgf, (byte*)param, (word32)plen, 0, + num * 8, NULL); + if ((ret <= 0) || (ret > tlen)) { + WOLFSSL_MSG("OAEP decoding failed"); + ret = WOLFSSL_FATAL_ERROR; + } + else { + XMEMCPY(to, msg, (size_t)ret); + } + } + + if (em != NULL) { + ForceZero(em, (word32)num); + XFREE(em, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + return ret; +} + +/* Check PKCS#1 OAEP padding with SHA-1 and MGF1-SHA-1 and get the message. + * + * @param [out] to Buffer for the message. May be from. + * @param [in] tlen Size of to in bytes. + * @param [in] from Encoded message. + * @param [in] flen Length of encoded message in bytes. + * @param [in] num Size of the RSA modulus in bytes. + * @param [in] param Label. May be NULL when plen is 0. + * @param [in] plen Length of label in bytes. + * @return Length of the message on success. + * @return -1 on failure. + */ +int wolfSSL_RSA_padding_check_PKCS1_OAEP(unsigned char* to, int tlen, + const unsigned char* from, int flen, int num, const unsigned char* param, + int plen) +{ + WOLFSSL_ENTER("wolfSSL_RSA_padding_check_PKCS1_OAEP"); + + return wolfSSL_RSA_padding_check_PKCS1_OAEP_mgf1(to, tlen, from, flen, + num, param, plen, NULL, NULL); +} +#endif /* WOLFSSL_RSA_OAEP_COMPAT */ + /* * RSA sign/verify APIs */ diff --git a/tests/api/test_ossl_rsa.c b/tests/api/test_ossl_rsa.c index 7c30e5ce07e..77118d3b2c7 100644 --- a/tests/api/test_ossl_rsa.c +++ b/tests/api/test_ossl_rsa.c @@ -582,6 +582,297 @@ int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void) return EXPECT_RESULT(); } +int test_wolfSSL_RSA_padding_PKCS1_OAEP(void) +{ + EXPECT_DECLS; +#if !defined(NO_RSA) && defined(WOLFSSL_RSA_OAEP_COMPAT) && \ + !defined(WC_NO_RNG) + static const unsigned char msg[16] = { + 'O', 'A', 'E', 'P', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 't', 'e', 's', + 't', '!' + }; + static const unsigned char label[5] = { 'l', 'a', 'b', 'e', 'l' }; + unsigned char em[256] = {0}; + unsigned char out[256] = {0}; + + /* SHA-1 OAEP takes 2 * 20 + 2 bytes. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(NULL, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0), 0); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, 41, NULL, 0, NULL, 0), 0); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, 42, NULL, 0, NULL, 0), 1); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, 57, msg, (int)sizeof(msg), + NULL, 0), 0); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, 58, msg, (int)sizeof(msg), + NULL, 0), 1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, 58, + 58, NULL, 0), (int)sizeof(msg)); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 1), 0); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(NULL, (int)sizeof(out), em, 58, + 58, NULL, 0), -1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, 59, + 58, NULL, 0), -1); + + /* With a label. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, (int)sizeof(em), msg, + (int)sizeof(msg), label, (int)sizeof(label)), 1); + ExpectIntEQ(em[0], 0); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label)), + (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + /* Leading zero byte removed. */ + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em + 1, + (int)sizeof(em) - 1, (int)sizeof(em), label, (int)sizeof(label)), + (int)sizeof(msg)); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(msg) - 1, em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label)), -1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label) - 1), -1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0), -1); + em[100] ^= 0x01; + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label)), -1); + + /* In place. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0), 1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(em, (int)sizeof(em), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0), (int)sizeof(msg)); + ExpectBufEQ(em, msg, sizeof(msg)); + + /* Each call uses a fresh random seed. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0), 1); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(out, (int)sizeof(out), msg, + (int)sizeof(msg), NULL, 0), 1); + ExpectBufNE(em, out, sizeof(em)); + +#ifdef WC_RSA_NO_PADDING + { + /* msg encrypted by OpenSSL to client_key_der_2048 with the label. */ + static const unsigned char ct[] = { + 0x8f, 0x18, 0x00, 0x4d, 0x31, 0x50, 0xd2, 0x5d, 0x58, 0x47, 0x9a, + 0x97, 0x99, 0x94, 0xd1, 0x3a, 0x97, 0x95, 0x83, 0x5c, 0x45, 0x73, + 0xdf, 0x40, 0x6a, 0x6d, 0xd5, 0x5a, 0x3d, 0xae, 0x93, 0xe1, 0x9f, + 0xa1, 0x97, 0xce, 0xe1, 0xa5, 0xd2, 0x0a, 0xf7, 0x4c, 0x27, 0x5b, + 0x4e, 0xa6, 0xe8, 0x16, 0x01, 0xed, 0x76, 0x60, 0x23, 0xa1, 0xd2, + 0xd7, 0xff, 0xda, 0xb4, 0xc9, 0xd5, 0x7f, 0x47, 0xc1, 0xb1, 0x20, + 0x99, 0x8a, 0x74, 0xde, 0x8c, 0x22, 0x06, 0xd1, 0xe8, 0x4f, 0xf5, + 0x4a, 0xa4, 0xd0, 0xc7, 0xf1, 0x7f, 0x6f, 0x50, 0x10, 0xe8, 0xa0, + 0x2a, 0xce, 0x72, 0xfe, 0x8f, 0xa4, 0x6c, 0xc6, 0x0e, 0x1a, 0x37, + 0x20, 0xfe, 0x6f, 0x8b, 0x9a, 0x22, 0x18, 0x0f, 0x5f, 0x04, 0x95, + 0x0e, 0x7f, 0xec, 0x48, 0xec, 0xcf, 0x0c, 0xe3, 0x45, 0x8f, 0xc6, + 0x49, 0x2c, 0xb9, 0x77, 0xf4, 0x66, 0xf5, 0x23, 0xd1, 0x89, 0x56, + 0x61, 0x1a, 0x23, 0x5d, 0x24, 0x5c, 0xfe, 0x85, 0xf3, 0x66, 0x8a, + 0x18, 0xf0, 0xaf, 0xb0, 0x31, 0x6c, 0xe7, 0x46, 0xb0, 0x98, 0xea, + 0x92, 0x37, 0x88, 0x57, 0x68, 0xb6, 0x80, 0xb5, 0x53, 0x70, 0xca, + 0xe7, 0x55, 0x87, 0xfb, 0x94, 0x71, 0x8d, 0x03, 0x6a, 0x02, 0xa8, + 0xdd, 0x89, 0x58, 0x7e, 0xd1, 0xc5, 0xa6, 0xd3, 0x26, 0xe1, 0x79, + 0xc3, 0xb2, 0x81, 0xf6, 0x08, 0x6b, 0x60, 0xa3, 0x58, 0x2d, 0x8d, + 0x4d, 0xc0, 0xa7, 0xa8, 0xe6, 0x72, 0x2b, 0xa5, 0xdb, 0xdd, 0xc2, + 0x04, 0x13, 0x7a, 0xfe, 0x01, 0xff, 0xa3, 0x40, 0x4d, 0x97, 0xdf, + 0x66, 0x82, 0x71, 0x59, 0x2f, 0xa5, 0xca, 0x6f, 0xd5, 0xf5, 0x79, + 0x14, 0xa2, 0x0e, 0xcb, 0x93, 0x98, 0xdc, 0xeb, 0x45, 0xf8, 0xac, + 0xa3, 0xc3, 0xc6, 0x39, 0xa4, 0x08, 0x76, 0x44, 0xd3, 0x37, 0xbf, + 0x07, 0x18, 0xff, + }; + RSA* rsa = NULL; + const unsigned char* der = client_key_der_2048; + unsigned char ct2[256] = {0}; + + ExpectNotNull(d2i_RSAPrivateKey(&rsa, &der, + sizeof_client_key_der_2048)); + ExpectIntEQ(RSA_private_decrypt((int)sizeof(ct), ct, em, rsa, + RSA_NO_PADDING), (int)sizeof(em)); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label)), + (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + + /* Without a label, RSA_PKCS1_OAEP_PADDING decrypts it. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0), 1); + ExpectIntEQ(RSA_public_encrypt((int)sizeof(em), em, ct2, rsa, + RSA_NO_PADDING), (int)sizeof(ct2)); + ExpectIntEQ(RSA_private_decrypt((int)sizeof(ct2), ct2, out, rsa, + RSA_PKCS1_OAEP_PADDING), (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + RSA_free(rsa); + } +#endif +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_RSA_padding_PKCS1_OAEP_mgf1(void) +{ + EXPECT_DECLS; +#if !defined(NO_RSA) && defined(WOLFSSL_RSA_OAEP_COMPAT) && \ + !defined(WC_NO_RNG) && !defined(NO_SHA256) + static const unsigned char msg[16] = { + 'O', 'A', 'E', 'P', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 't', 'e', 's', + 't', '!' + }; + static const unsigned char label[5] = { 'l', 'a', 'b', 'e', 'l' }; + unsigned char em[256] = {0}; + unsigned char out[256] = {0}; + + /* NULL hashes are SHA-1, as without _mgf1. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, (int)sizeof(em), msg, + (int)sizeof(msg), label, (int)sizeof(label), NULL, NULL), 1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label)), + (int)sizeof(msg)); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label), + EVP_sha1(), EVP_sha1()), (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + + /* No MGF1 hash is the label hash. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0, EVP_sha256(), NULL), 1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0, EVP_sha256(), + EVP_sha256()), (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + /* Other hashes fail. */ + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0, EVP_sha256(), EVP_sha1()), + -1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0, EVP_sha1(), EVP_sha256()), + -1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP(out, (int)sizeof(out), em, + (int)sizeof(em), (int)sizeof(em), NULL, 0), -1); + + /* SHA-256 OAEP takes 2 * 32 + 2 bytes. */ + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, 65, NULL, 0, NULL, 0, + EVP_sha256(), EVP_sha1()), 0); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, 66, NULL, 0, NULL, 0, + EVP_sha256(), EVP_sha1()), 1); + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, (int)sizeof(em), msg, + (int)sizeof(msg), NULL, 0, (const EVP_MD*)"no hash", NULL), 0); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), em, + 66, 66, NULL, 0, EVP_sha256(), (const EVP_MD*)"no hash"), -1); + +#if defined(WOLFSSL_SHA512) && defined(WOLFSSL_SHA384) && \ + defined(WOLFSSL_SHA224) + { + const EVP_MD* mds[4]; + int i; + int j; + + mds[0] = EVP_sha224(); + mds[1] = EVP_sha256(); + mds[2] = EVP_sha384(); + mds[3] = EVP_sha512(); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + ExpectIntEQ(RSA_padding_add_PKCS1_OAEP_mgf1(em, + (int)sizeof(em), msg, (int)sizeof(msg), label, + (int)sizeof(label), mds[i], mds[j]), 1); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, + (int)sizeof(out), em, (int)sizeof(em), (int)sizeof(em), + label, (int)sizeof(label), mds[i], mds[j]), + (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + } + } + } +#endif + +#ifdef WC_RSA_NO_PADDING + { + /* msg encrypted by OpenSSL to client_key_der_2048 with OAEP SHA-256, + * MGF1-SHA-1 and the label. */ + static const unsigned char ct[] = { + 0x61, 0x16, 0xd4, 0xb5, 0x9c, 0x43, 0x15, 0x01, 0xd9, 0x99, 0x21, + 0x8f, 0x52, 0xa2, 0xc5, 0x65, 0x23, 0x22, 0xe7, 0x55, 0x32, 0x5f, + 0x25, 0xac, 0xc2, 0xa9, 0x1b, 0xa7, 0x8b, 0x0c, 0x05, 0x86, 0x62, + 0x6c, 0xa8, 0x3c, 0xa4, 0x24, 0x9a, 0x92, 0x53, 0x55, 0x06, 0xc2, + 0xcf, 0x26, 0x71, 0x79, 0xfc, 0x24, 0xf4, 0xd2, 0xc4, 0x99, 0x1f, + 0x65, 0xc8, 0xda, 0xa4, 0xa7, 0xe3, 0xbc, 0x95, 0x3c, 0x85, 0x8d, + 0x24, 0x2f, 0xb3, 0x3c, 0xf9, 0x4f, 0x60, 0x5a, 0xad, 0x7b, 0x64, + 0xe1, 0x4b, 0xc6, 0xe2, 0x69, 0xa3, 0x61, 0x38, 0x52, 0xcb, 0xce, + 0x76, 0x62, 0xe6, 0xaf, 0x06, 0x7f, 0x39, 0xf8, 0x4f, 0x4a, 0x37, + 0x64, 0x37, 0x2b, 0x27, 0x2e, 0xda, 0xed, 0x8b, 0xe5, 0xbf, 0xb2, + 0xb8, 0x90, 0x7f, 0x75, 0x77, 0x0f, 0xed, 0xcb, 0x2d, 0x54, 0xcb, + 0x9c, 0xa8, 0xac, 0xfe, 0x84, 0x65, 0x16, 0xe8, 0x85, 0x81, 0xb5, + 0x7d, 0xff, 0xf8, 0xef, 0x09, 0xe8, 0x51, 0x5b, 0x65, 0x21, 0xfd, + 0xc5, 0xfb, 0xb6, 0xda, 0xfc, 0xae, 0xde, 0x56, 0xae, 0x64, 0x98, + 0xd7, 0xde, 0xe4, 0xe5, 0x60, 0x7d, 0xee, 0x95, 0x47, 0x03, 0xd6, + 0xed, 0x38, 0x3e, 0xf3, 0xe6, 0xfa, 0x28, 0x2b, 0xd7, 0xdb, 0xbc, + 0x6f, 0x21, 0x8e, 0x8d, 0xab, 0x15, 0xda, 0xba, 0x16, 0xe3, 0x53, + 0x79, 0xaf, 0xae, 0x0e, 0x91, 0x5e, 0xe9, 0xec, 0x30, 0x42, 0x35, + 0x47, 0x38, 0x33, 0xd4, 0x05, 0x3e, 0x17, 0x31, 0xec, 0x84, 0x9f, + 0xf2, 0x35, 0x6f, 0xc3, 0xf5, 0xb3, 0x17, 0x02, 0x86, 0xec, 0x74, + 0x25, 0xe5, 0xc0, 0x13, 0x7b, 0x29, 0x52, 0x73, 0xcf, 0x81, 0x8a, + 0x56, 0xca, 0x33, 0x90, 0xba, 0x1f, 0x87, 0xd0, 0x1e, 0x2f, 0x31, + 0xb0, 0xe3, 0x2e, 0xbb, 0x2e, 0x55, 0x35, 0x3e, 0xda, 0xd6, 0x36, + 0xdd, 0x38, 0x49, + }; + RSA* rsa = NULL; + const unsigned char* der = client_key_der_2048; + + ExpectNotNull(d2i_RSAPrivateKey(&rsa, &der, + sizeof_client_key_der_2048)); + ExpectIntEQ(RSA_private_decrypt((int)sizeof(ct), ct, em, rsa, + RSA_NO_PADDING), (int)sizeof(em)); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, (int)sizeof(out), + em, (int)sizeof(em), (int)sizeof(em), label, (int)sizeof(label), + EVP_sha256(), EVP_sha1()), (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + #if defined(WOLFSSL_SHA3) && !defined(WOLFSSL_NOSHA3_256) && \ + defined(WOLFSSL_SHA512) + { + /* msg encrypted by OpenSSL with OAEP SHA3-256 and + * MGF1-SHA-512. */ + static const unsigned char ct3[] = { + 0x74, 0x3f, 0xf3, 0xea, 0x2b, 0x45, 0x01, 0x10, 0x37, 0x0d, + 0xbb, 0x85, 0x95, 0xd7, 0x63, 0x9c, 0x71, 0x8d, 0x2d, 0x37, + 0xc6, 0x09, 0x2a, 0x76, 0x09, 0x76, 0x33, 0x9b, 0xba, 0x45, + 0xed, 0xf6, 0x4c, 0x9e, 0x7d, 0xc4, 0x4a, 0xd9, 0xfc, 0x45, + 0x4b, 0x7b, 0x9d, 0x11, 0xd7, 0x09, 0x96, 0xb4, 0x4d, 0x75, + 0xbc, 0xa5, 0x3c, 0x15, 0x0e, 0xc5, 0xe2, 0xa7, 0xa6, 0xea, + 0x0b, 0x6d, 0xb3, 0x48, 0xf2, 0x27, 0xad, 0x3b, 0x6c, 0xb7, + 0x8e, 0x7e, 0xc5, 0xa9, 0xa5, 0x93, 0xaa, 0x2c, 0xcf, 0x9e, + 0xb0, 0x4c, 0x16, 0x2d, 0x03, 0x31, 0x5b, 0x82, 0x51, 0x7e, + 0x32, 0x43, 0x92, 0x11, 0xa1, 0x9e, 0x01, 0xee, 0xc5, 0xc3, + 0xda, 0xbc, 0x4a, 0xb5, 0xf4, 0xf8, 0x10, 0x55, 0xbf, 0x08, + 0xdb, 0x09, 0xec, 0x51, 0xa0, 0xe5, 0xcd, 0xe6, 0x11, 0x60, + 0x60, 0x65, 0x09, 0x07, 0x0d, 0xe5, 0x19, 0x1c, 0x8d, 0xea, + 0xb2, 0xc1, 0x1a, 0x64, 0x2b, 0x3f, 0xb3, 0x0e, 0x51, 0x25, + 0xe0, 0x6d, 0xe2, 0xcd, 0xce, 0x8c, 0xe3, 0x50, 0x54, 0x1d, + 0x87, 0x27, 0x41, 0x4b, 0xb4, 0xcc, 0xc2, 0x45, 0x47, 0x7a, + 0xfd, 0x5f, 0x4a, 0xf7, 0x67, 0x7b, 0xe9, 0x5d, 0xf4, 0x14, + 0x1a, 0xaf, 0x9c, 0x9e, 0x7d, 0x2f, 0x5f, 0xd3, 0x1b, 0xe5, + 0x63, 0x3f, 0x20, 0x15, 0xed, 0xd8, 0xe2, 0xf0, 0xdb, 0xf2, + 0x9d, 0xf6, 0x86, 0x0e, 0xaa, 0x53, 0x7d, 0x4e, 0xc2, 0xd7, + 0x95, 0x11, 0xbb, 0xff, 0x75, 0x55, 0xcd, 0xb0, 0xda, 0x77, + 0x3a, 0x5c, 0x7a, 0xa8, 0xbf, 0x4d, 0xd1, 0x3e, 0xf6, 0x20, + 0xd9, 0x8d, 0xc9, 0x66, 0xf8, 0x13, 0x6e, 0xbc, 0xc7, 0x82, + 0x35, 0xa5, 0xfc, 0x44, 0xcd, 0x7a, 0x92, 0xd2, 0x90, 0x63, + 0xcf, 0x91, 0x70, 0xe8, 0xb1, 0x8b, 0x2c, 0xe6, 0xbd, 0x2c, + 0x18, 0xf2, 0xc8, 0xc3, 0x31, 0xff, + }; + + ExpectIntEQ(RSA_private_decrypt((int)sizeof(ct3), ct3, em, rsa, + RSA_NO_PADDING), (int)sizeof(em)); + ExpectIntEQ(RSA_padding_check_PKCS1_OAEP_mgf1(out, + (int)sizeof(out), em, (int)sizeof(em), (int)sizeof(em), NULL, + 0, EVP_sha3_256(), EVP_sha512()), (int)sizeof(msg)); + ExpectBufEQ(out, msg, sizeof(msg)); + } + #endif + RSA_free(rsa); + } +#endif +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_RSA_sign_sha3(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_rsa.h b/tests/api/test_ossl_rsa.h index e3b2a186f60..622ad021274 100644 --- a/tests/api/test_ossl_rsa.h +++ b/tests/api/test_ossl_rsa.h @@ -28,6 +28,8 @@ int test_wolfSSL_RSA(void); int test_wolfSSL_RSA_DER(void); int test_wolfSSL_RSA_print(void); int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void); +int test_wolfSSL_RSA_padding_PKCS1_OAEP(void); +int test_wolfSSL_RSA_padding_PKCS1_OAEP_mgf1(void); int test_wolfSSL_RSA_sign_sha3(void); int test_wolfSSL_RSA_get0_key(void); int test_wolfSSL_RSA_meth(void); @@ -53,6 +55,8 @@ int test_wolfSSL_PEM_write_mem_RSAPrivateKey(void); TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_DER), \ TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_print), \ TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_padding_add_PKCS1_PSS), \ + TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_padding_PKCS1_OAEP), \ + TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_padding_PKCS1_OAEP_mgf1), \ TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_sign_sha3), \ TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_get0_key), \ TEST_DECL_GROUP("ossl_rsa", test_wolfSSL_RSA_meth), \ diff --git a/wolfssl/openssl/rsa.h b/wolfssl/openssl/rsa.h index 0c28c4427b9..d7bea15e47d 100644 --- a/wolfssl/openssl/rsa.h +++ b/wolfssl/openssl/rsa.h @@ -177,6 +177,27 @@ WOLFSSL_LOCAL int wolfSSL_RSA_verify_mgf(int hashAlg, int mgf1Hash, int saltLen); WOLFSSL_API int wolfSSL_RSA_public_decrypt(int flen, const unsigned char* from, unsigned char* to, WOLFSSL_RSA* rsa, int padding); +#if defined(OPENSSL_EXTRA) && !defined(WC_NO_RSA_OAEP) && \ + !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || FIPS_VERSION_GT(2,0)) \ + && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(WOLFSSL_RSA_VERIFY_ONLY) + /* RSA_padding_add_PKCS1_OAEP and RSA_padding_check_PKCS1_OAEP */ + #define WOLFSSL_RSA_OAEP_COMPAT +#endif +#ifdef WOLFSSL_RSA_OAEP_COMPAT +WOLFSSL_API int wolfSSL_RSA_padding_add_PKCS1_OAEP(unsigned char* to, int tlen, + const unsigned char* from, int flen, const unsigned char* param, int plen); +WOLFSSL_API int wolfSSL_RSA_padding_add_PKCS1_OAEP_mgf1(unsigned char* to, + int tlen, const unsigned char* from, int flen, const unsigned char* param, + int plen, const WOLFSSL_EVP_MD* md, const WOLFSSL_EVP_MD* mgf1md); +/* An empty message fails, as wolfCrypt reports it as a decoding error. */ +WOLFSSL_API int wolfSSL_RSA_padding_check_PKCS1_OAEP(unsigned char* to, + int tlen, const unsigned char* from, int flen, int num, + const unsigned char* param, int plen); +WOLFSSL_API int wolfSSL_RSA_padding_check_PKCS1_OAEP_mgf1(unsigned char* to, + int tlen, const unsigned char* from, int flen, int num, + const unsigned char* param, int plen, const WOLFSSL_EVP_MD* md, + const WOLFSSL_EVP_MD* mgf1md); +#endif WOLFSSL_API int wolfSSL_RSA_GenAdd(WOLFSSL_RSA* rsa); WOLFSSL_API int wolfSSL_RSA_LoadDer(WOLFSSL_RSA* rsa, const unsigned char* derBuf, int derSz); @@ -275,6 +296,15 @@ WOLFSSL_API int wolfSSL_RSA_set_ex_data_with_cleanup( #define RSA_get0_key wolfSSL_RSA_get0_key +#ifdef WOLFSSL_RSA_OAEP_COMPAT +#define RSA_padding_add_PKCS1_OAEP wolfSSL_RSA_padding_add_PKCS1_OAEP +#define RSA_padding_check_PKCS1_OAEP wolfSSL_RSA_padding_check_PKCS1_OAEP +#define RSA_padding_add_PKCS1_OAEP_mgf1 \ + wolfSSL_RSA_padding_add_PKCS1_OAEP_mgf1 +#define RSA_padding_check_PKCS1_OAEP_mgf1 \ + wolfSSL_RSA_padding_check_PKCS1_OAEP_mgf1 +#endif + #define RSA_F4 WOLFSSL_RSA_F4 #define OPENSSL_RSA_MAX_MODULUS_BITS RSA_MAX_SIZE From 524568450e0b4df222b1e1a349a26dd2882090d0 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 27 Oct 2026 08:11:15 +0000 Subject: [PATCH 05/31] Add EVP_PKEY_RSA2, EVP_PKEY_RSA_PSS and the prime curve short names OpenSSL code handles all RSA key types together, as xmlsec does with case labels next to EVP_PKEY_RSA. Add them with OpenSSL's values: EVP_PKEY_RSA2 is 19, OpenSSL's NID_rsa, and EVP_PKEY_RSA_PSS is NID_rsassaPss. wolfSSL's own NID_rsa equals EVP_PKEY_RSA, so the value is given as a number. wolfSSL RSA keys are always EVP_PKEY_RSA and never have these types. Add SN_X9_62_prime192v1 to SN_X9_62_prime256v1, SN_secp224r1, SN_secp384r1 and SN_secp521r1, as in OpenSSL. Each is the name that OBJ_nid2sn returns for the curve's NID. xmlsec uses them in its table of named curves. --- tests/api/test_evp_pkey.c | 22 ++++++++++++++++++++++ tests/api/test_evp_pkey.h | 2 ++ tests/api/test_ossl_ec.c | 29 +++++++++++++++++++++++++++++ tests/api/test_ossl_ec.h | 2 ++ wolfssl/openssl/ec.h | 12 ++++++++++++ wolfssl/openssl/evp.h | 6 ++++++ 6 files changed, 73 insertions(+) diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index b2f9d5f189c..e5115594ba4 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -132,6 +132,28 @@ int test_wolfSSL_EVP_PKEY_base_id(void) #endif return EXPECT_RESULT(); } +int test_wolfSSL_EVP_PKEY_rsa_types(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && \ + defined(USE_CERT_BUFFERS_2048) + EVP_PKEY* pkey = NULL; + const unsigned char* in = client_key_der_2048; + + /* Distinct values, so all three can be case labels in one switch. */ + ExpectIntNE(EVP_PKEY_RSA2, EVP_PKEY_RSA); + ExpectIntNE(EVP_PKEY_RSA_PSS, EVP_PKEY_RSA); + ExpectIntNE(EVP_PKEY_RSA2, EVP_PKEY_RSA_PSS); + ExpectIntEQ(EVP_PKEY_RSA_PSS, NID_rsassaPss); + + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &in, + (long)sizeof_client_key_der_2048)); + ExpectIntEQ(EVP_PKEY_base_id(pkey), EVP_PKEY_RSA); + EVP_PKEY_free(pkey); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EVP_PKEY_id(void) { EXPECT_DECLS; diff --git a/tests/api/test_evp_pkey.h b/tests/api/test_evp_pkey.h index af16c468993..419f7869a99 100644 --- a/tests/api/test_evp_pkey.h +++ b/tests/api/test_evp_pkey.h @@ -29,6 +29,7 @@ int test_wolfSSL_EVP_PKEY_CTX_set_rsa_keygen_bits(void); int test_wolfSSL_QT_EVP_PKEY_CTX_free(void); int test_wolfSSL_EVP_PKEY_up_ref(void); int test_wolfSSL_EVP_PKEY_base_id(void); +int test_wolfSSL_EVP_PKEY_rsa_types(void); int test_wolfSSL_EVP_PKEY_id(void); int test_wolfSSL_EVP_MD_pkey_type(void); int test_wolfSSL_EVP_MD_hmac_signing(void); @@ -94,6 +95,7 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); TEST_DECL_GROUP("evp_pkey", test_wolfSSL_QT_EVP_PKEY_CTX_free), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_up_ref), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_base_id), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_rsa_types), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_id), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_MD_pkey_type), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_MD_hmac_signing), \ diff --git a/tests/api/test_ossl_ec.c b/tests/api/test_ossl_ec.c index 823276ad8cd..01a2b5be96a 100644 --- a/tests/api/test_ossl_ec.c +++ b/tests/api/test_ossl_ec.c @@ -1208,6 +1208,35 @@ int test_wolfSSL_EC_curve(void) return EXPECT_RESULT(); } +int test_wolfSSL_EC_curve_short_names(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) + static const struct { + int nid; + const char* sn; + } curves[] = { + { NID_X9_62_prime192v1, SN_X9_62_prime192v1 }, + { NID_X9_62_prime192v2, SN_X9_62_prime192v2 }, + { NID_X9_62_prime192v3, SN_X9_62_prime192v3 }, + { NID_X9_62_prime239v1, SN_X9_62_prime239v1 }, + { NID_X9_62_prime239v2, SN_X9_62_prime239v2 }, + { NID_X9_62_prime239v3, SN_X9_62_prime239v3 }, + { NID_X9_62_prime256v1, SN_X9_62_prime256v1 }, + { NID_secp224r1, SN_secp224r1 }, + { NID_secp384r1, SN_secp384r1 }, + { NID_secp521r1, SN_secp521r1 }, + }; + size_t i; + + for (i = 0; i < sizeof(curves) / sizeof(curves[0]); i++) { + ExpectStrEQ(OBJ_nid2sn(curves[i].nid), curves[i].sn); + ExpectIntEQ(OBJ_sn2nid(curves[i].sn), curves[i].nid); + } +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EC_KEY_dup(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_ec.h b/tests/api/test_ossl_ec.h index 52fc422caa1..47b53367b95 100644 --- a/tests/api/test_ossl_ec.h +++ b/tests/api/test_ossl_ec.h @@ -34,6 +34,7 @@ int test_wolfSSL_SPAKE(void); int test_wolfSSL_EC_KEY_generate(void); int test_EC_i2d(void); int test_wolfSSL_EC_curve(void); +int test_wolfSSL_EC_curve_short_names(void); int test_wolfSSL_EC_KEY_dup(void); int test_wolfSSL_EC_KEY_set_group(void); int test_wolfSSL_EC_KEY_set_conv_form(void); @@ -58,6 +59,7 @@ int test_wolfSSL_EC_POINT_point2hex_oversized(void); TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_KEY_generate), \ TEST_DECL_GROUP("ossl_ec", test_EC_i2d), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_curve), \ + TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_curve_short_names), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_KEY_dup), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_KEY_set_group), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_KEY_set_conv_form), \ diff --git a/wolfssl/openssl/ec.h b/wolfssl/openssl/ec.h index 95e32972fa4..3d50c8be8b4 100644 --- a/wolfssl/openssl/ec.h +++ b/wolfssl/openssl/ec.h @@ -124,6 +124,18 @@ enum { #define NID_brainpoolP320r1 WC_NID_brainpoolP320r1 #define NID_brainpoolP384r1 WC_NID_brainpoolP384r1 #define NID_brainpoolP512r1 WC_NID_brainpoolP512r1 + +/* Short names of the curves, as OBJ_nid2sn() returns them */ +#define SN_X9_62_prime192v1 "prime192v1" +#define SN_X9_62_prime192v2 "prime192v2" +#define SN_X9_62_prime192v3 "prime192v3" +#define SN_X9_62_prime239v1 "prime239v1" +#define SN_X9_62_prime239v2 "prime239v2" +#define SN_X9_62_prime239v3 "prime239v3" +#define SN_X9_62_prime256v1 "prime256v1" +#define SN_secp224r1 "secp224r1" +#define SN_secp384r1 "secp384r1" +#define SN_secp521r1 "secp521r1" #endif #ifdef HAVE_ED448 diff --git a/wolfssl/openssl/evp.h b/wolfssl/openssl/evp.h index f55728b41df..b5ad2c0e328 100644 --- a/wolfssl/openssl/evp.h +++ b/wolfssl/openssl/evp.h @@ -443,6 +443,10 @@ enum { WC_EVP_PKEY_RSA = 16, WC_EVP_PKEY_DSA = 17, WC_EVP_PKEY_EC = 18, + /* OpenSSL RSA key types. wolfSSL never creates keys of these types; + * they exist so code that switches on them compiles. */ + WC_EVP_PKEY_RSA2 = 19, + WC_EVP_PKEY_RSA_PSS = WC_NID_rsassaPss, WC_AES_128_GCM_TYPE = 21, WC_AES_192_GCM_TYPE = 22, WC_AES_256_GCM_TYPE = 23, @@ -522,6 +526,8 @@ enum { #define EVP_PKEY_RSA WC_EVP_PKEY_RSA #define EVP_PKEY_DSA WC_EVP_PKEY_DSA #define EVP_PKEY_EC WC_EVP_PKEY_EC +#define EVP_PKEY_RSA2 WC_EVP_PKEY_RSA2 +#define EVP_PKEY_RSA_PSS WC_EVP_PKEY_RSA_PSS #define AES_128_GCM_TYPE WC_AES_128_GCM_TYPE #define AES_192_GCM_TYPE WC_AES_192_GCM_TYPE #define AES_256_GCM_TYPE WC_AES_256_GCM_TYPE From 14a73a7e77d0211a19d2e71d84a9801cf4b36e72 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 27 Oct 2026 14:07:35 +0000 Subject: [PATCH 06/31] Fix i2d_PUBKEY of EC keys, X509_get0_pubkey and i2d_ECDSA_SIG i2d_PUBKEY and i2d_PublicKey of an EC private key decoded the key's DER as a public key. That failed for a key loaded from PKCS#8, whose DER has no curve, and for a key without the optional public key. OpenSSL encodes the public key in both cases, and xmlsec needs it to write the key of a PKCS#12 file. Encode the public key from the EC key, computing the public point when the key has none. EC_KEY loading from PKCS#8 decoded the key after its header, so the key took the default curve. Decode the whole PKCS#8 so the curve is kept, as a brainpool key needs. X509_get0_pubkey and X509_get_pubkey now come from master; the tests of this commit expect X509_get_pubkey to return the X509's key with a new reference. OpenSSL's i2d_ECDSA_SIG(sig, &p) with p NULL allocates the encoding. wolfSSL does this only with WOLFSSL_I2D_ECDSA_SIG_ALLOC, which no build option set, so xmlsec's ASN.1 ECDSA signatures failed. Define it with --enable-opensslall in configure and the matching CMake bundles, and remove it from the known macro extras. Other builds still return only the length, as the API docs now say. When the encoding fails after the buffer was allocated, free it and set p back to NULL, as OpenSSL does. --- .wolfssl_known_macro_extras | 1 - CMakeLists.txt | 47 ++++----- cmake/options.h.in | 2 + configure.ac | 2 +- doc/dox_comments/header_files/ecc.h | 35 +++++++ src/internal.c | 47 +++++++++ src/pk_ec.c | 14 ++- tests/api/test_evp_pkey.c | 138 +++++++++++++++++++++++++ tests/api/test_evp_pkey.h | 2 + tests/api/test_ossl_ec.c | 67 +++++++++++- tests/api/test_ossl_ec.h | 2 + tests/api/test_ossl_x509_pk.c | 151 ++++++++++++++++++++++++++++ tests/api/test_ossl_x509_pk.h | 3 + wolfcrypt/src/evp_pk.c | 134 ++++++++++-------------- wolfssl/internal.h | 3 + 15 files changed, 540 insertions(+), 108 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 36f3f3f5ede..74fc92526ef 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1020,7 +1020,6 @@ WOLFSSL_HARDEN_TLS_NO_PKEY_CHECK WOLFSSL_HARDEN_TLS_NO_SCR_CHECK WOLFSSL_HAVE_KYBER WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY -WOLFSSL_I2D_ECDSA_SIG_ALLOC WOLFSSL_IAR_ARM_TIME WOLFSSL_IGNORE_BAD_CERT_PATH WOLFSSL_IMX6 diff --git a/CMakeLists.txt b/CMakeLists.txt index 534714847a1..e41e25e44de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -474,7 +474,7 @@ if(WOLFSSL_OPENVPN) foreach(_o WOLFSSL_AES WOLFSSL_CRL WOLFSSL_DES3 WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_KEYING_MATERIAL" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_OPENVPN" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_KEYING_MATERIAL" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_OPENVPN" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_NGINX" "Enable nginx support (default: disabled)" "no" "yes;no") @@ -482,7 +482,7 @@ if(WOLFSSL_NGINX) foreach(_o WOLFSSL_CRL WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_OCSPSTAPLING WOLFSSL_OCSPSTAPLING_V2) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NGINX" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NGINX" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_HAPROXY" "Enable HAProxy support (default: disabled)" "no" "yes;no") @@ -490,7 +490,7 @@ if(WOLFSSL_HAPROXY) foreach(_o WOLFSSL_CRL WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_OCSPSTAPLING WOLFSSL_OCSPSTAPLING_V2 WOLFSSL_OLD_TLS) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DFP_MAX_BITS=16384" "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECURE_RENEGOTIATION" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DRSA_MAX_SIZE=8192" "-DSESSION_CERTS" "-DSP_INT_BITS=8192" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALLOW_SSLV3" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_HAPROXY" "-DWOLFSSL_KEEP_RNG_SEED_FD_OPEN" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DFP_MAX_BITS=16384" "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECURE_RENEGOTIATION" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DRSA_MAX_SIZE=8192" "-DSESSION_CERTS" "-DSP_INT_BITS=8192" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALLOW_SSLV3" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_HAPROXY" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEEP_RNG_SEED_FD_OPEN" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() # Note: no -DSINGLE_THREADED here. configure.ac's --enable-lighty does not @@ -518,7 +518,7 @@ if(WOLFSSL_OPENLDAP) foreach(_o WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_NETSNMP" "Enable net-snmp support (default: disabled)" "no" "yes;no") @@ -526,7 +526,7 @@ if(WOLFSSL_NETSNMP) foreach(_o WOLFSSL_CRL WOLFSSL_DES3 WOLFSSL_DTLS WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_CFB" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_CFB" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_HITCH" "Enable Hitch support (default: disabled)" "no" "yes;no") @@ -534,7 +534,7 @@ if(WOLFSSL_HITCH) foreach(_o WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_CIPHER_INTERNALNAME" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_HITCH" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_CIPHER_INTERNALNAME" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_HITCH" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_MEMCACHED" "Enable Memcached support (default: disabled)" "no" "yes;no") @@ -547,7 +547,7 @@ if(WOLFSSL_RSYSLOG) foreach(_o WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_RSYSLOG" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_RSYSLOG" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_BIND" "Enable BIND DNS support (default: disabled)" "no" "yes;no") @@ -555,7 +555,7 @@ if(WOLFSSL_BIND) foreach(_o WOLFSSL_AES WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_ECB" "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_BIND" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_DSA_768_MODULUS" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_ECB" "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_BIND" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_DSA_768_MODULUS" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_NTP" "Enable ntp support (default: disabled)" "no" "yes;no") @@ -563,7 +563,7 @@ if(WOLFSSL_NTP) foreach(_o WOLFSSL_AES WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CMAC" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CMAC" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_LIBSSH2" "Enable libssh2 support (default: disabled)" "no" "yes;no") @@ -579,7 +579,7 @@ if(WOLFSSL_STRONGSWAN) foreach(_o WOLFSSL_CRL WOLFSSL_DES3 WOLFSSL_DTLS WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_LOG_PRINTF" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_LOG_PRINTF" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_QT" "Enable Qt support (default: disabled)" "no" "yes;no") @@ -587,7 +587,7 @@ if(WOLFSSL_QT) foreach(_o WOLFSSL_ARC4 WOLFSSL_DES3 WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_OLD_TLS) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ECC_BRAINPOOL" "-DHAVE_ECC_KOBLITZ" "-DHAVE_ECC_SECPR2" "-DHAVE_ECC_SECPR3" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_OLD_MD5_NAME" "-DNO_OLD_RNGNAME" "-DNO_OLD_SHA_NAMES" "-DNO_OLD_SSL_NAMES" "-DNO_OLD_WC_NAMES" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_SSL2" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_ALLOW_SSLV3" "-DWOLFSSL_ALLOW_TLSV10" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CUSTOM_CURVES" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_QT" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ECC_BRAINPOOL" "-DHAVE_ECC_KOBLITZ" "-DHAVE_ECC_SECPR2" "-DHAVE_ECC_SECPR3" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DNO_OLD_MD5_NAME" "-DNO_OLD_RNGNAME" "-DNO_OLD_SHA_NAMES" "-DNO_OLD_SSL_NAMES" "-DNO_OLD_WC_NAMES" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_SSL2" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_ALLOW_SSLV3" "-DWOLFSSL_ALLOW_TLSV10" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CUSTOM_CURVES" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_QT" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_APACHE_HTTPD" "Enable Apache httpd support (default: disabled)" "no" "yes;no") @@ -595,7 +595,7 @@ if(WOLFSSL_APACHE_HTTPD) foreach(_o WOLFSSL_CRL WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECURE_RENEGOTIATION" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_COMP" "-DOPENSSL_NO_SSL2" "-DOPENSSL_NO_SSL3" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_APACHE_HTTPD" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_NO_OCSP_ISSUER_CHECK" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECURE_RENEGOTIATION" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_COMP" "-DOPENSSL_NO_SSL2" "-DOPENSSL_NO_SSL3" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_APACHE_HTTPD" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_NO_OCSP_ISSUER_CHECK" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_JNI" "Enable wolfSSL JNI support (default: disabled)" "no" "yes;no") @@ -603,7 +603,7 @@ if(WOLFSSL_JNI) foreach(_o WOLFSSL_AES WOLFSSL_CRL WOLFSSL_CRL_MONITOR WOLFSSL_DTLS WOLFSSL_DTLS13 WOLFSSL_OCSP WOLFSSL_PKCALLBACKS) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DATOMIC_USER" "-DHAVE_ALPN" "-DHAVE_CRL" "-DHAVE_CTS" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DKEEP_PEER_CERT" "-DNO_ERROR_QUEUE" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DPERSIST_CERT_CACHE" "-DPERSIST_SESSION_CACHE" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_DTLS_CH_FRAG" "-DWOLFSSL_DTLS_DROP_STATS" "-DWOLFSSL_DTLS_MTU" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_JNI" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_SEND_HRR_COOKIE" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_MIDDLEBOX_COMPAT") + list(APPEND WOLFSSL_DEFINITIONS "-DATOMIC_USER" "-DHAVE_ALPN" "-DHAVE_CRL" "-DHAVE_CTS" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DKEEP_PEER_CERT" "-DNO_ERROR_QUEUE" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DPERSIST_CERT_CACHE" "-DPERSIST_SESSION_CACHE" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_DTLS_CH_FRAG" "-DWOLFSSL_DTLS_DROP_STATS" "-DWOLFSSL_DTLS_MTU" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_JNI" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_SEND_HRR_COOKIE" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_MIDDLEBOX_COMPAT") endif() add_option("WOLFSSL_WOLFCLU" "Enable wolfCLU support (default: disabled)" "no" "yes;no") @@ -611,7 +611,7 @@ if(WOLFSSL_WOLFCLU) foreach(_o WOLFSSL_AES WOLFSSL_DES3 WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_PKCS7) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_ED25519" "-DHAVE_OCSP_RESPONDER" "-DHAVE_OID_ENCODING" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWC_SIG_MIN_HASH_TYPE=WC_HASH_TYPE_MD5" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NO_ASN_STRICT" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_ED25519" "-DHAVE_OCSP_RESPONDER" "-DHAVE_OID_ENCODING" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWC_SIG_MIN_HASH_TYPE=WC_HASH_TYPE_MD5" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NO_ASN_STRICT" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_SBLIM_SFCB" "Enable SBLIM SFCB support (default: disabled)" "no" "yes;no") @@ -624,7 +624,7 @@ if(WOLFSSL_TAILSCALE) foreach(_o WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_CTC_NAME_SIZE=128" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ACME_OID" "-DWOLFSSL_AESGCM_STREAM" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_BASE16" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CERT_SETUP_CB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_NO_INIT_CTX_KEY" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_CTC_NAME_SIZE=128" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ACME_OID" "-DWOLFSSL_AESGCM_STREAM" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_BASE16" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CERT_SETUP_CB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_NO_INIT_CTX_KEY" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_CHRONY" "Enable chrony support (default: disabled)" "no" "yes;no") @@ -632,7 +632,7 @@ if(WOLFSSL_CHRONY) foreach(_o WOLFSSL_AES WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_ED25519" "-DHAVE_KEYING_MATERIAL" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_AES_SIV" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CMAC" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_COMP_KEY" "-DHAVE_ED25519" "-DHAVE_KEYING_MATERIAL" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_COUNTER" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_AES_SIV" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CMAC" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_LIBEST" "Enable libest support (default: disabled)" "no" "yes;no") @@ -640,7 +640,7 @@ if(WOLFSSL_LIBEST) foreach(_o WOLFSSL_AES WOLFSSL_CRL WOLFSSL_OCSP WOLFSSL_PKCS7 WOLFSSL_SRP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_LIBEST" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PSS_SALT_LEN_DISCOVER" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_LIBEST" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALT_NAMES" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PSS_SALT_LEN_DISCOVER" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_OPENRESTY" "Enable OpenResty support (default: disabled)" "no" "yes;no") @@ -648,7 +648,7 @@ if(WOLFSSL_OPENRESTY) foreach(_o WOLFSSL_CRL WOLFSSL_DSA WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_OCSPSTAPLING WOLFSSL_OCSPSTAPLING_V2) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NGINX" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ALPN" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SESSION_TICKET" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_TRUSTED_CA" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DSESSION_CERTS" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_NGINX" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT") endif() add_option("WOLFSSL_LIBWEBSOCKETS" "Enable libwebsockets support (default: disabled)" "no" "yes;no") @@ -656,7 +656,7 @@ if(WOLFSSL_LIBWEBSOCKETS) foreach(_o WOLFSSL_MD5 WOLFSSL_OCSP) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_EC" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_LIBWEBSOCKETS" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_COMP_KEY" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DOPENSSL_NO_EC" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_LIBWEBSOCKETS" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_FORTRESS" "Enable wolfSSL fortress build (default: disabled)" "no" "yes;no") @@ -680,7 +680,7 @@ if(WOLFSSL_KRB) foreach(_o WOLFSSL_AES WOLFSSL_ARC4 WOLFSSL_CRL WOLFSSL_DES3 WOLFSSL_MD4 WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_PKCS7) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KRB" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_EX_DATA" "-DHAVE_OPENSSL_CMD" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KRB" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_SMIME" "Enable S/MIME support (default: disabled)" "no" "yes;no") @@ -688,7 +688,7 @@ if(WOLFSSL_SMIME) foreach(_o WOLFSSL_AES WOLFSSL_MD5 WOLFSSL_OCSP WOLFSSL_PKCS7) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DHAVE_SMIME" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_TICKET_HAVE_ID") + list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_AES_KEYWRAP" "-DHAVE_COMP_KEY" "-DHAVE_OPENSSL_CMD" "-DHAVE_SMIME" "-DHAVE_X963_KDF" "-DOPENSSL_ALL" "-DOPENSSL_EXTRA" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_TICKET_HAVE_ID") endif() add_option("WOLFSSL_SCEP" "Enable wolfSCEP support (default: disabled)" "no" "yes;no") @@ -704,7 +704,7 @@ 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) force_option(${_o} "yes") endforeach() - list(APPEND WOLFSSL_DEFINITIONS "-DATOMIC_USER" "-DHAVE_AES_KEYWRAP" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_ECC_BRAINPOOL" "-DHAVE_ECC_CDH" "-DHAVE_ECC_KOBLITZ" "-DHAVE_ECC_SECPR2" "-DHAVE_ECC_SECPR3" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_KEYING_MATERIAL" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECRET_CALLBACK" "-DHAVE_SESSION_TICKET" "-DHAVE_STUNNEL" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_X963_KDF" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DOPENSSL_EXTRA_X509_SMALL" "-DSESSION_CERTS" "-DWC_CTC_NAME_SIZE=128" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_CMAC" "-DWOLFSSL_CUSTOM_CURVES" "-DWOLFSSL_DER_LOAD" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_PUBLIC_ECC_ADD_DBL" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT" "-DWOLFSSL_VALIDATE_ECC_IMPORT" "-DWOLFSSL_WPAS") + list(APPEND WOLFSSL_DEFINITIONS "-DATOMIC_USER" "-DHAVE_AES_KEYWRAP" "-DHAVE_ANON" "-DHAVE_COMP_KEY" "-DHAVE_CRL" "-DHAVE_ECC_BRAINPOOL" "-DHAVE_ECC_CDH" "-DHAVE_ECC_KOBLITZ" "-DHAVE_ECC_SECPR2" "-DHAVE_ECC_SECPR3" "-DHAVE_EXT_CACHE" "-DHAVE_EX_DATA" "-DHAVE_KEYING_MATERIAL" "-DHAVE_MAX_FRAGMENT" "-DHAVE_OPENSSL_CMD" "-DHAVE_SECRET_CALLBACK" "-DHAVE_SESSION_TICKET" "-DHAVE_STUNNEL" "-DHAVE_TRUNCATED_HMAC" "-DHAVE_X963_KDF" "-DKEEP_OUR_CERT" "-DKEEP_PEER_CERT" "-DNO_SESSION_CACHE_REF" "-DOPENSSL_ALL" "-DOPENSSL_COMPATIBLE_DEFAULTS" "-DOPENSSL_EXTRA" "-DOPENSSL_EXTRA_X509_SMALL" "-DSESSION_CERTS" "-DWC_CTC_NAME_SIZE=128" "-DWC_RSA_NO_PADDING" "-DWOLFSSL_AES_DIRECT" "-DWOLFSSL_ALLOW_RC4" "-DWOLFSSL_ALT_CERT_CHAINS" "-DWOLFSSL_ALWAYS_KEEP_SNI" "-DWOLFSSL_ALWAYS_VERIFY_CB" "-DWOLFSSL_CERT_EXT" "-DWOLFSSL_CERT_GEN" "-DWOLFSSL_CERT_NAME_ALL" "-DWOLFSSL_CERT_REQ" "-DWOLFSSL_CHECK_ALERT_ON_ERR" "-DWOLFSSL_CMAC" "-DWOLFSSL_CUSTOM_CURVES" "-DWOLFSSL_DER_LOAD" "-DWOLFSSL_DES_ECB" "-DWOLFSSL_EITHER_SIDE" "-DWOLFSSL_ENCRYPTED_KEYS" "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC" "-DWOLFSSL_KEY_GEN" "-DWOLFSSL_PRIORITIZE_PSK" "-DWOLFSSL_PUBLIC_ECC_ADD_DBL" "-DWOLFSSL_PUBLIC_MP" "-DWOLFSSL_SIGNER_DER_CERT" "-DWOLFSSL_TICKET_HAVE_ID" "-DWOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE" "-DWOLFSSL_TRUST_PEER_CERT" "-DWOLFSSL_VALIDATE_ECC_IMPORT" "-DWOLFSSL_WPAS") endif() add_option("WOLFSSL_QT_TEST" "Enable Qt test support (default: disabled)" "no" "yes;no") @@ -1218,7 +1218,8 @@ if (WOLFSSL_OPENSSLALL) list(APPEND WOLFSSL_DEFINITIONS "-DOPENSSL_ALL" "-DWOLFSSL_EITHER_SIDE" "-DWC_RSA_NO_PADDING" "-DWC_RSA_PSS" "-DWOLFSSL_PSS_LONG_SALT" "-DWOLFSSL_TICKET_HAVE_ID" - "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_CERT_NAME_ALL") + "-DWOLFSSL_ERROR_CODE_OPENSSL" "-DWOLFSSL_CERT_NAME_ALL" + "-DWOLFSSL_I2D_ECDSA_SIG_ALLOC") endif() add_option(WOLFSSL_NO_STUB diff --git a/cmake/options.h.in b/cmake/options.h.in index 6c35a38de09..723253c8655 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -363,6 +363,8 @@ extern "C" { #cmakedefine WOLFSSL_ENCRYPTED_KEYS #undef WOLFSSL_ERROR_CODE_OPENSSL #cmakedefine WOLFSSL_ERROR_CODE_OPENSSL +#undef WOLFSSL_I2D_ECDSA_SIG_ALLOC +#cmakedefine WOLFSSL_I2D_ECDSA_SIG_ALLOC #undef WOLFSSL_IP_ALT_NAME #cmakedefine WOLFSSL_IP_ALT_NAME #undef WOLFSSL_KEY_GEN diff --git a/configure.ac b/configure.ac index 8504097da7f..806a10f84d2 100644 --- a/configure.ac +++ b/configure.ac @@ -13827,7 +13827,7 @@ then fi AS_IF([test "x$ENABLED_OPENSSLALL" = "xyes"], - [AM_CFLAGS="$AM_CFLAGS -DOPENSSL_ALL -DWOLFSSL_EITHER_SIDE -DWC_RSA_NO_PADDING -DWC_RSA_PSS -DWOLFSSL_PSS_LONG_SALT -DWOLFSSL_TICKET_HAVE_ID -DWOLFSSL_ERROR_CODE_OPENSSL -DWOLFSSL_CERT_NAME_ALL"]) + [AM_CFLAGS="$AM_CFLAGS -DOPENSSL_ALL -DWOLFSSL_EITHER_SIDE -DWC_RSA_NO_PADDING -DWC_RSA_PSS -DWOLFSSL_PSS_LONG_SALT -DWOLFSSL_TICKET_HAVE_ID -DWOLFSSL_ERROR_CODE_OPENSSL -DWOLFSSL_CERT_NAME_ALL -DWOLFSSL_I2D_ECDSA_SIG_ALLOC"]) AS_IF([test "x$ENABLED_AESSIV" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_SIV"]) diff --git a/doc/dox_comments/header_files/ecc.h b/doc/dox_comments/header_files/ecc.h index 589918da90f..419d420e414 100644 --- a/doc/dox_comments/header_files/ecc.h +++ b/doc/dox_comments/header_files/ecc.h @@ -3646,3 +3646,38 @@ int wc_ecc_use_key_id(ecc_key* key, word32 keyId, word32 flags); \sa wc_ecc_use_key_id */ int wc_ecc_get_key_id(ecc_key* key, word32* keyId); + +/*! + \ingroup ECC + \brief Encodes an ECDSA signature as DER, as OpenSSL's i2d_ECDSA_SIG(). + + With pp NULL only the length is returned. When *pp is a buffer, the + encoding is written to it and *pp is moved past it. When *pp is NULL and + WOLFSSL_I2D_ECDSA_SIG_ALLOC is defined, a buffer is allocated for the + encoding and *pp is set to it, as OpenSSL does. Free it with + OPENSSL_free(). Otherwise only the length is returned and *pp stays + NULL. --enable-opensslall defines WOLFSSL_I2D_ECDSA_SIG_ALLOC, and so + do --enable-all, the application options that enable opensslall and the + matching CMake options. Use pp NULL to get the length in every build. + + \return Length of the encoding on success. + \return 0 on error. A buffer allocated here is freed and *pp set to NULL. + + \param sig ECDSA signature to encode. + \param pp NULL, or the address of the output buffer pointer. + + _Example_ + \code + unsigned char* der = NULL; + int derSz = wolfSSL_i2d_ECDSA_SIG(sig, NULL); + // With WOLFSSL_I2D_ECDSA_SIG_ALLOC, allocates der + derSz = wolfSSL_i2d_ECDSA_SIG(sig, &der); + if (derSz > 0) { + // use der + OPENSSL_free(der); + } + \endcode + + \sa wolfSSL_d2i_ECDSA_SIG +*/ +int wolfSSL_i2d_ECDSA_SIG(const WOLFSSL_ECDSA_SIG *sig, unsigned char **pp); diff --git a/src/internal.c b/src/internal.c index 60f13ef08f8..5a9adabe7b2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6060,6 +6060,8 @@ static void FreeX509Contents(WOLFSSL_X509* x509) XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); x509->extKeyUsageSrc= NULL; } + wolfSSL_EVP_PKEY_free(x509->pubKeyPKey); + x509->pubKeyPKey = NULL; #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ #if defined(OPENSSL_ALL) if (x509->algor.algorithm) { @@ -17157,6 +17159,51 @@ void DoCertFatalAlert(WOLFSSL* ssl, int ret) ssl->options.fatalAlert = 1; } +#ifdef KEEP_PEER_CERT +/* Refill ssl->peerCert in place. Keep its X509_get0_pubkey() key when the + * public key is the same: the app may still hold it. */ +static int SetPeerCert(WOLFSSL* ssl, DecodedCert* dCert) +{ + int ret; +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + WOLFSSL_EVP_PKEY* pkey = ssl->peerCert.pubKeyPKey; + buffer pub = ssl->peerCert.pubKey; + int keyOID = ssl->peerCert.pubKeyOID; +#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ + defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_SLHDSA) + word32 curveOID = ssl->peerCert.pkCurveOID; +#endif + + ssl->peerCert.pubKeyPKey = NULL; + ssl->peerCert.pubKey.buffer = NULL; + ssl->peerCert.pubKey.length = 0; +#endif + FreeX509(&ssl->peerCert); + InitX509(&ssl->peerCert, 0, ssl->heap); + ret = CopyDecodedToX509(&ssl->peerCert, dCert); +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + if ((ret == 0) && (pub.buffer != NULL) && + (ssl->peerCert.pubKey.buffer != NULL) && + (keyOID == ssl->peerCert.pubKeyOID) && +#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ + defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ + defined(WOLFSSL_HAVE_SLHDSA) + (curveOID == ssl->peerCert.pkCurveOID) && +#endif + (pub.length == ssl->peerCert.pubKey.length) && + (XMEMCMP(pub.buffer, ssl->peerCert.pubKey.buffer, + pub.length) == 0)) { + ssl->peerCert.pubKeyPKey = pkey; + pkey = NULL; + } + wolfSSL_EVP_PKEY_free(pkey); + XFREE(pub.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); +#endif + return ret; +} +#endif /* KEEP_PEER_CERT */ + int SetupStoreCtxCallback(WOLFSSL_X509_STORE_CTX** store_pt, WOLFSSL* ssl, WOLFSSL_CERT_MANAGER* cm, ProcPeerCertArgs* args, int cert_err, void* heap, int* x509Free) diff --git a/src/pk_ec.c b/src/pk_ec.c index e55d16496c5..83add5c450b 100644 --- a/src/pk_ec.c +++ b/src/pk_ec.c @@ -3696,6 +3696,8 @@ int wolfSSL_EC_KEY_LoadDer_ex(WOLFSSL_EC_KEY* key, const unsigned char* derBuf, if (res == 1) { /* Load into internal EC key based on key type option. */ if (opt == WOLFSSL_EC_KEY_LOAD_PRIVATE) { + /* Decode with any PKCS#8 header, which holds the curve. */ + idx = 0; ret = wc_EccPrivateKeyDecode(derBuf, &idx, (ecc_key*)key->internal, (word32)derSz); } @@ -5205,8 +5207,11 @@ WOLFSSL_ECDSA_SIG* wolfSSL_d2i_ECDSA_SIG(WOLFSSL_ECDSA_SIG** sig, * @param [in] sig ECDSA signature object. * @param [in, out] pp On in, reference to buffer in which to place encoding. * On out, reference to buffer after encoding. - * May be NULL or point to NULL in which case no encoding - * is done. + * May be NULL in which case no encoding is done. + * When it points to NULL, with + * WOLFSSL_I2D_ECDSA_SIG_ALLOC it is set to a new buffer + * with the encoding, freed with OPENSSL_free(), and + * stays NULL on error; otherwise no encoding is done. * @return Length of encoding on success. * @return 0 on error. */ @@ -5251,6 +5256,11 @@ int wolfSSL_i2d_ECDSA_SIG(const WOLFSSL_ECDSA_SIG *sig, unsigned char **pp) (mp_int*)sig->s->internal) != MP_OKAY) { /* No bytes encoded. */ len = 0; + if (!update_p) { + /* Free the buffer allocated here. */ + XFREE(*pp, NULL, DYNAMIC_TYPE_OPENSSL); + *pp = NULL; + } } else if (update_p) { /* Update pointer to after encoding. */ diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index e5115594ba4..bcfbf2359e3 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -2310,6 +2310,144 @@ int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void) return EXPECT_RESULT(); } +int test_wolfSSL_i2d_PUBKEY_EC_private(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) && \ + defined(USE_CERT_BUFFERS_256) && !defined(NO_ECC256) && \ + !defined(NO_ECC_SECP) + /* RFC 5915 ECPrivateKey of a P-256 key without the public key. */ + static const unsigned char privOnly[] = { + 0x30, 0x31, 0x02, 0x01, 0x01, 0x04, 0x20, + 0x51, 0x9b, 0x42, 0x3d, 0x71, 0x5f, 0x8b, 0x58, + 0x1f, 0x4f, 0xa8, 0xee, 0x59, 0xf4, 0x77, 0x1a, + 0x5b, 0x44, 0xc8, 0x13, 0x0b, 0x4e, 0x3e, 0xac, + 0xca, 0x54, 0xa5, 0x6d, 0xda, 0x72, 0xb4, 0x64, + 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + /* Its public point, x and y. */ + static const unsigned char privOnlyPub[] = { + 0x1c, 0xcb, 0xe9, 0x1c, 0x07, 0x5f, 0xc7, 0xf4, + 0xf0, 0x33, 0xbf, 0xa2, 0x48, 0xdb, 0x8f, 0xcc, + 0xd3, 0x56, 0x5d, 0xe9, 0x4b, 0xbf, 0xb1, 0x2f, + 0x3c, 0x59, 0xff, 0x46, 0xc2, 0x71, 0xbf, 0x83, + 0xce, 0x40, 0x14, 0xc6, 0x88, 0x11, 0xf9, 0xa2, + 0x1a, 0x1f, 0xdb, 0x2c, 0x0e, 0x61, 0x13, 0xe0, + 0x6d, 0xb7, 0xca, 0x93, 0xb7, 0x40, 0x4e, 0x78, + 0xdc, 0x7c, 0xcd, 0x5c, 0xa8, 0x9a, 0x4c, 0xa9 + }; +#ifdef HAVE_ECC_BRAINPOOL + /* PKCS#8 brainpoolP256r1 key: the curve is only in the header. */ + static const unsigned char bpP8[] = { + 0x30, 0x81, 0x88, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, + 0x08, 0x01, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, + 0x20, 0x3a, 0x2f, 0x65, 0x24, 0x08, 0x72, 0x6c, 0x87, 0xa6, 0xb0, 0xe1, + 0x88, 0xac, 0x31, 0x74, 0xd1, 0xc0, 0x41, 0x92, 0xe6, 0xd1, 0x3f, 0x40, + 0xb5, 0x49, 0x7a, 0xa9, 0x0d, 0xe3, 0x58, 0x1e, 0xf4, 0xa1, 0x44, 0x03, + 0x42, 0x00, 0x04, 0x69, 0x46, 0x8e, 0xee, 0x68, 0x80, 0xa4, 0xae, 0x02, + 0x86, 0x6b, 0xc1, 0xef, 0xfe, 0xd1, 0xc6, 0xaa, 0x10, 0x85, 0x5d, 0xbd, + 0xc9, 0x28, 0x88, 0x3e, 0xd1, 0xdd, 0xad, 0xfe, 0x0f, 0x65, 0xc7, 0x2f, + 0xf7, 0xa2, 0x73, 0xd0, 0xe6, 0xae, 0x4e, 0x09, 0x9b, 0x34, 0xf6, 0xfa, + 0x85, 0x97, 0x05, 0xfd, 0xc5, 0xfc, 0x7e, 0x43, 0xfd, 0x24, 0xf4, 0xad, + 0x0e, 0x9d, 0x22, 0x6e, 0xf7, 0x7f, 0xd0 + }; +#endif + EVP_PKEY* pkey = NULL; + EVP_PKEY* pub = NULL; + const unsigned char* p = ecc_key_der_256; + unsigned char* der = NULL; + unsigned char* der2 = NULL; + int derSz = 0; + EC_KEY* ec = NULL; + unsigned char point[65]; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) + byte* buf = NULL; + size_t bufSz = 0; +#endif + + /* The SubjectPublicKeyInfo of a private key. */ + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, + (long)sizeof_ecc_key_der_256)); + ExpectIntEQ(i2d_PUBKEY(pkey, NULL), (int)sizeof_ecc_key_pub_der_256); + ExpectIntEQ(derSz = i2d_PUBKEY(pkey, &der), + (int)sizeof_ecc_key_pub_der_256); + ExpectBufEQ(der, ecc_key_pub_der_256, sizeof_ecc_key_pub_der_256); + /* The public key reads back. */ + p = der; + ExpectNotNull(pub = d2i_PUBKEY(NULL, &p, derSz)); + ExpectIntEQ(i2d_PUBKEY(pub, &der2), derSz); + ExpectBufEQ(der2, der, derSz); + EVP_PKEY_free(pub); + XFREE(der2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + der2 = NULL; + + /* The encoding follows the current EC key. */ + ExpectNotNull(ec = EVP_PKEY_get0_EC_KEY(pkey)); + ExpectIntEQ(EC_KEY_generate_key(ec), 1); + ExpectIntEQ(i2d_PUBKEY(pkey, &der2), derSz); + ExpectIntNE(XMEMCMP(der2, der, (size_t)derSz), 0); + ExpectIntEQ(EC_POINT_point2oct(EC_KEY_get0_group(ec), + EC_KEY_get0_public_key(ec), POINT_CONVERSION_UNCOMPRESSED, point, + sizeof(point), NULL), sizeof(point)); + if (EXPECT_SUCCESS()) { + ExpectBufEQ(der2 + derSz - sizeof(point), point, sizeof(point)); + } + XFREE(der2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + der = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + + /* The public key is computed when the encoding does not have it. */ + p = privOnly; + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, + (long)sizeof(privOnly))); + ExpectIntEQ(derSz = i2d_PUBKEY(pkey, &der), + (int)sizeof_ecc_key_pub_der_256); + ExpectBufEQ(der, ecc_key_pub_der_256, 27); + if (EXPECT_SUCCESS()) { + ExpectBufEQ(der + derSz - sizeof(privOnlyPub), privOnlyPub, + sizeof(privOnlyPub)); + } + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + der = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) + /* PKCS#8: the inner key has no curve parameters. */ + ExpectIntEQ(load_file("./certs/ecc-keyPkcs8.der", &buf, &bufSz), 0); + p = buf; + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, + (long)bufSz)); + ExpectIntEQ(i2d_PUBKEY(pkey, &der), (int)sizeof_ecc_key_pub_der_256); + ExpectBufEQ(der, ecc_key_pub_der_256, sizeof_ecc_key_pub_der_256); + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + der = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + +#ifdef HAVE_ECC_BRAINPOOL + /* The curve of a PKCS#8 key is taken from the header. */ + p = bpP8; + ExpectNotNull(pkey = d2i_PrivateKey(EVP_PKEY_EC, NULL, &p, + (long)sizeof(bpP8))); + ExpectIntEQ(derSz = i2d_PUBKEY(pkey, &der), 92); + /* Same AlgorithmIdentifier and public point. */ + ExpectBufEQ(der + 2, bpP8 + 6, 22); + if (EXPECT_SUCCESS()) { + ExpectBufEQ(der + derSz - 65, bpP8 + sizeof(bpP8) - 65, 65); + } + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + EVP_PKEY_free(pkey); +#endif +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EVP_PKEY_sign_verify_dsa_der(void) { EXPECT_DECLS; diff --git a/tests/api/test_evp_pkey.h b/tests/api/test_evp_pkey.h index 419f7869a99..55fef107ee2 100644 --- a/tests/api/test_evp_pkey.h +++ b/tests/api/test_evp_pkey.h @@ -64,6 +64,7 @@ int test_wolfSSL_EVP_PKEY_sign_verify_rsa(void); int test_wolfSSL_EVP_PKEY_sign_verify_dsa(void); int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void); int test_wolfSSL_EVP_PKEY_sign_verify_dsa_der(void); +int test_wolfSSL_i2d_PUBKEY_EC_private(void); int test_wolfSSL_EVP_PKEY_sign_verify_ec(void); int test_wolfSSL_EVP_PKEY_verify_dsa_len(void); int test_wolfSSL_EVP_MD_rsa_signing(void); @@ -131,6 +132,7 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_dsa), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_dsa_digest_len), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_dsa_der), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_i2d_PUBKEY_EC_private), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_sign_verify_ec), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_verify_dsa_len), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_MD_rsa_signing), \ diff --git a/tests/api/test_ossl_ec.c b/tests/api/test_ossl_ec.c index 01a2b5be96a..fb80275339a 100644 --- a/tests/api/test_ossl_ec.c +++ b/tests/api/test_ossl_ec.c @@ -1636,15 +1636,80 @@ int test_wolfSSL_ECDSA_SIG(void) ExpectIntEQ(XMEMCMP(sigData, outSig, 8), 0); p = NULL; + ExpectIntEQ(wolfSSL_i2d_ECDSA_SIG(NULL, &p), 0); + ExpectNull(p); ExpectIntEQ(wolfSSL_i2d_ECDSA_SIG(sig, &p), 8); #ifndef WOLFSSL_I2D_ECDSA_SIG_ALLOC + /* Only the length. */ ExpectNull(p); #else + /* A new buffer starting with the encoding, as in OpenSSL. */ ExpectNotNull(p); ExpectIntEQ(XMEMCMP(p, outSig, 8), 0); - XFREE(p, NULL, DYNAMIC_TYPE_OPENSSL); + OPENSSL_free(p); +#endif + + wolfSSL_ECDSA_SIG_free(sig); +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_I2D_ECDSA_SIG_ALLOC) && \ + defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \ + !defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY) +#define TEST_I2D_ECDSA_SIG_OOM +static wolfSSL_Malloc_cb test_i2d_ecdsa_sig_prev_malloc = NULL; +static int test_i2d_ecdsa_sig_allocs = 0; + +/* Only the first allocation succeeds. */ +static void* test_i2d_ecdsa_sig_malloc(size_t size) +{ + if (test_i2d_ecdsa_sig_allocs++ > 0) + return NULL; + if (test_i2d_ecdsa_sig_prev_malloc != NULL) + return test_i2d_ecdsa_sig_prev_malloc(size); + return malloc(size); +} #endif +/* i2d_ECDSA_SIG frees the buffer it allocated when encoding fails. Only math + * libraries that allocate while encoding can fail here. */ +int test_wolfSSL_i2d_ECDSA_SIG_oom(void) +{ + EXPECT_DECLS; +#ifdef TEST_I2D_ECDSA_SIG_OOM + WOLFSSL_ECDSA_SIG* sig = NULL; + const unsigned char* cp = NULL; + unsigned char* p = NULL; + unsigned char sigData[8] = + { 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01 }; + wolfSSL_Malloc_cb prevM = NULL; + wolfSSL_Free_cb prevF = NULL; + wolfSSL_Realloc_cb prevR = NULL; + int len = 0; + + cp = sigData; + ExpectNotNull(sig = wolfSSL_d2i_ECDSA_SIG(NULL, &cp, sizeof(sigData))); + + ExpectIntEQ(wolfSSL_GetAllocators(&prevM, &prevF, &prevR), 0); + test_i2d_ecdsa_sig_prev_malloc = prevM; + test_i2d_ecdsa_sig_allocs = 0; + ExpectIntEQ(wolfSSL_SetAllocators(test_i2d_ecdsa_sig_malloc, prevF, + prevR), 0); + len = wolfSSL_i2d_ECDSA_SIG(sig, &p); + (void)wolfSSL_SetAllocators(prevM, prevF, prevR); + test_i2d_ecdsa_sig_prev_malloc = NULL; + + if (len == 0) { + ExpectNull(p); + } + else { + ExpectIntEQ(len, sizeof(sigData)); + ExpectNotNull(p); + ExpectIntEQ(XMEMCMP(p, sigData, sizeof(sigData)), 0); + } + + OPENSSL_free(p); wolfSSL_ECDSA_SIG_free(sig); #endif return EXPECT_RESULT(); diff --git a/tests/api/test_ossl_ec.h b/tests/api/test_ossl_ec.h index 47b53367b95..2e542dcea35 100644 --- a/tests/api/test_ossl_ec.h +++ b/tests/api/test_ossl_ec.h @@ -43,6 +43,7 @@ int test_wolfSSL_EC_KEY_public_key(void); int test_wolfSSL_EC_KEY_print_fp(void); int test_wolfSSL_EC_get_builtin_curves(void); int test_wolfSSL_ECDSA_SIG(void); +int test_wolfSSL_i2d_ECDSA_SIG_oom(void); int test_ECDSA_size_sign(void); int test_ECDSA_do_verify_oversized_sig(void); int test_ECDH_compute_key(void); @@ -68,6 +69,7 @@ int test_wolfSSL_EC_POINT_point2hex_oversized(void); TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_KEY_print_fp), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_EC_get_builtin_curves), \ TEST_DECL_GROUP("ossl_ec", test_wolfSSL_ECDSA_SIG), \ + TEST_DECL_GROUP("ossl_ec", test_wolfSSL_i2d_ECDSA_SIG_oom), \ TEST_DECL_GROUP("ossl_ec", test_ECDSA_size_sign), \ TEST_DECL_GROUP("ossl_ec", test_ECDSA_do_verify_oversized_sig), \ TEST_DECL_GROUP("ossl_ec", test_ECDH_compute_key), \ diff --git a/tests/api/test_ossl_x509_pk.c b/tests/api/test_ossl_x509_pk.c index e4b7302d7c2..714d3886fb6 100644 --- a/tests/api/test_ossl_x509_pk.c +++ b/tests/api/test_ossl_x509_pk.c @@ -328,6 +328,157 @@ int test_wolfSSL_X509_PUBKEY_get(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \ + (!defined(NO_RSA) || defined(HAVE_ECC)) +static int test_X509_get0_pubkey_file(const char* file) +{ + EXPECT_DECLS; + X509* x509 = NULL; + X509* dup = NULL; + EVP_PKEY* key = NULL; + EVP_PKEY* pkey = NULL; + unsigned char* der = NULL; + unsigned char* der2 = NULL; + int derSz = 0; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(file, + WOLFSSL_FILETYPE_PEM)); + /* The cert owns the key: the same key every time. */ + ExpectNotNull(key = X509_get0_pubkey(x509)); + ExpectPtrEq(X509_get0_pubkey(x509), key); + /* X509_get_pubkey returns a new key for the caller to free. */ + ExpectNotNull(pkey = X509_get_pubkey(x509)); + ExpectPtrNE(pkey, key); + ExpectIntGT(derSz = i2d_PUBKEY(key, &der), 0); + ExpectIntEQ(i2d_PUBKEY(pkey, &der2), derSz); + ExpectBufEQ(der2, der, derSz); + XFREE(der2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + der2 = NULL; + EVP_PKEY_free(pkey); + pkey = NULL; + ExpectPtrEq(X509_get0_pubkey(x509), key); + + /* A copy owns its own key. */ + ExpectNotNull(dup = X509_dup(x509)); + ExpectNotNull(pkey = X509_get0_pubkey(dup)); + ExpectPtrNE(pkey, key); + X509_free(x509); + x509 = NULL; + ExpectIntEQ(i2d_PUBKEY(pkey, &der2), derSz); + ExpectBufEQ(der2, der, derSz); + + XFREE(der2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + X509_free(dup); + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_X509_get0_pubkey(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) +#if !defined(NO_RSA) && defined(HAVE_ECC) + X509* x509 = NULL; + X509* ecc = NULL; + EVP_PKEY* key = NULL; + EVP_PKEY* pkey = NULL; + unsigned char* der = NULL; + unsigned char* der2 = NULL; + int derSz = 0; +#endif + + ExpectNull(X509_get0_pubkey(NULL)); +#ifndef NO_RSA + ExpectIntEQ(test_X509_get0_pubkey_file(svrCertFile), TEST_SUCCESS); +#endif +#ifdef HAVE_ECC + ExpectIntEQ(test_X509_get0_pubkey_file(eccCertFile), TEST_SUCCESS); +#endif + +#if !defined(NO_RSA) && defined(HAVE_ECC) + /* Setting a new public key drops the old owned key. */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(ecc = wolfSSL_X509_load_certificate_file(eccCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(pkey = X509_get0_pubkey(x509)); + ExpectIntEQ(EVP_PKEY_base_id(pkey), EVP_PKEY_RSA); + ExpectNotNull(key = X509_get0_pubkey(ecc)); + ExpectIntEQ(X509_set_pubkey(x509, key), WOLFSSL_SUCCESS); + ExpectNotNull(pkey = X509_get0_pubkey(x509)); + ExpectIntEQ(EVP_PKEY_base_id(pkey), EVP_PKEY_EC); + ExpectIntGT(derSz = i2d_PUBKEY(key, &der), 0); + ExpectIntEQ(i2d_PUBKEY(pkey, &der2), derSz); + ExpectBufEQ(der2, der, derSz); + + XFREE(der2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + X509_free(ecc); + X509_free(x509); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && defined(KEEP_PEER_CERT) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) +static EVP_PKEY* test_get0_pubkey_leaf = NULL; +static int test_get0_pubkey_leaf_bad = 0; + +/* Keep the leaf's key from the first call and check it on later calls. */ +static int test_get0_pubkey_verify_cb(int ok, X509_STORE_CTX* store) +{ + X509* cert = X509_STORE_CTX_get_current_cert(store); + + (void)ok; + if (X509_STORE_CTX_get_error_depth(store) == 0) { + if (test_get0_pubkey_leaf == NULL) { + test_get0_pubkey_leaf = X509_get0_pubkey(cert); + } + else if ((EVP_PKEY_bits(test_get0_pubkey_leaf) != 2048) || + (X509_get0_pubkey(cert) != test_get0_pubkey_leaf)) { + test_get0_pubkey_leaf_bad = 1; + } + } + return 1; +} +#endif + +/* The key of the leaf in the verify callback stays valid while the peer cert + * does, as in OpenSSL. */ +int test_wolfSSL_X509_get0_pubkey_peer(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(KEEP_PEER_CERT) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_get0_pubkey_leaf = NULL; + test_get0_pubkey_leaf_bad = 0; + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method), 0); + /* The leaf is not trusted, so the callback is called on an error. */ + ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_get0_pubkey_verify_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(test_get0_pubkey_leaf); + ExpectIntEQ(test_get0_pubkey_leaf_bad, 0); + ExpectIntEQ(EVP_PKEY_bits(test_get0_pubkey_leaf), 2048); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_set_pubkey(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_pk.h b/tests/api/test_ossl_x509_pk.h index c7afb032848..cfaddfdca7c 100644 --- a/tests/api/test_ossl_x509_pk.h +++ b/tests/api/test_ossl_x509_pk.h @@ -30,6 +30,8 @@ int test_wolfSSL_X509_PUBKEY_EC(void); int test_wolfSSL_X509_PUBKEY_DSA(void); int test_wolfSSL_X509_PUBKEY_MLDSA(void); int test_wolfSSL_X509_PUBKEY_get(void); +int test_wolfSSL_X509_get0_pubkey(void); +int test_wolfSSL_X509_get0_pubkey_peer(void); int test_wolfSSL_X509_set_pubkey(void); int test_wolfSSL_X509_get0_pubkey(void); int test_wolfSSL_X509_get_pubkey_key_types(void); @@ -45,6 +47,7 @@ int test_wolfSSL_X509_PUBKEY_get0_param_fail(void); TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_PUBKEY_get), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_set_pubkey), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey), \ + TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_peer), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get_pubkey_key_types), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_threads), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_PUBKEY_get0_param_fail) diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index 32290ae00cf..10c2e77c7ec 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -2645,116 +2645,90 @@ int wolfSSL_i2d_PrivateKey_bio(WOLFSSL_BIO* bio, WOLFSSL_EVP_PKEY* key) #ifdef HAVE_ECC /* Encode EC key as public key DER. * - * @param [in] key WOLFSSL_EVP_KEY object to encode. * @param [in] ec WOLFSSL_EC_KEY object to encode. * @param [out] der Buffer with DER encoding of EC public key. * @return Public key DER encoding size on success. * @return WOLFSSL_FATAL_ERROR when dynamic memory allocation fails. * @return WOLFSSL_FATAL_ERROR when encoding fails. */ -static int wolfssl_i_i2d_ecpublickey(const WOLFSSL_EVP_PKEY* key, - const WOLFSSL_EC_KEY *ec, unsigned char **der) +static int wolfssl_i_i2d_ecpublickey(const WOLFSSL_EC_KEY *ec, + unsigned char **der) { - word32 pub_derSz = 0; - int ret; - unsigned char *local_der = NULL; - word32 local_derSz = 0; - unsigned char *pub_der = NULL; - ecc_key *eccKey = NULL; - word32 inOutIdx = 0; - - /* We need to get the DER, then convert it to a public key. But what we get - * might be a buffered private key so we need to decode it and then encode - * the public part. */ - ret = wolfssl_i_evp_pkey_get_der(key, &local_der); - if (ret <= 0) { - /* In this case, there was no buffered DER at all. This could be the - * case where the key that was passed in was generated. So now we - * have to create the local DER. */ - local_derSz = (word32)wolfSSL_i2d_ECPrivateKey(ec, &local_der); - if (local_derSz == 0) { - ret = WOLFSSL_FATAL_ERROR; - } - } else { - local_derSz = (word32)ret; - ret = 0; - } - - if (ret == 0) { - eccKey = (ecc_key *)XMALLOC(sizeof(*eccKey), NULL, DYNAMIC_TYPE_ECC); - if (eccKey == NULL) { - WOLFSSL_MSG("Failed to allocate key buffer."); - ret = WOLFSSL_FATAL_ERROR; - } - } + int ret = 0; + int sz = 0; + ecc_key* eccKey = NULL; + ecc_key* pubKey = NULL; - /* Initialize a wolfCrypt ECC key. */ - if (ret == 0) { - ret = wc_ecc_init(eccKey); - } - if (ret == 0) { - /* Decode the DER data with wolfCrypt ECC key. */ - ret = wc_EccPublicKeyDecode(local_der, &inOutIdx, eccKey, local_derSz); - if (ret < 0) { - /* We now try again as x.963 [point type][x][opt y]. */ - ret = wc_ecc_import_x963(local_der, local_derSz, eccKey); - } + if ((ec == NULL) || (ec->internal == NULL)) { + ret = WOLFSSL_FATAL_ERROR; } - - if (ret == 0) { - /* Get the size of the encoding of the public key DER. */ - pub_derSz = (word32)wc_EccPublicKeyDerSize(eccKey, 1); - if ((int)pub_derSz <= 0) { - ret = WOLFSSL_FATAL_ERROR; - } + /* Sync the internal key with the EC key. */ + if ((ret == 0) && (!ec->inSet) && + (SetECKeyInternal((WOLFSSL_EC_KEY*)ec) != 1)) { + ret = WOLFSSL_FATAL_ERROR; } - if (ret == 0) { - /* Allocate memory for public key DER encoding. */ - pub_der = (unsigned char*)XMALLOC(pub_derSz, NULL, - DYNAMIC_TYPE_PUBLIC_KEY); - if (pub_der == NULL) { - WOLFSSL_MSG("Failed to allocate output buffer."); - ret = WOLFSSL_FATAL_ERROR; + eccKey = (ecc_key*)ec->internal; + if (eccKey->type == ECC_PRIVATEKEY_ONLY) { + /* Compute the public point into a separate key. */ + pubKey = (ecc_key*)XMALLOC(sizeof(*pubKey), NULL, + DYNAMIC_TYPE_ECC); + if (pubKey == NULL) { + ret = WOLFSSL_FATAL_ERROR; + } + else if (wc_ecc_init(pubKey) != 0) { + XFREE(pubKey, NULL, DYNAMIC_TYPE_ECC); + pubKey = NULL; + ret = WOLFSSL_FATAL_ERROR; + } + else { + pubKey->idx = eccKey->idx; + pubKey->dp = eccKey->dp; + if (wc_ecc_make_pub(eccKey, &pubKey->pubkey) != 0) { + ret = WOLFSSL_FATAL_ERROR; + } + pubKey->type = ECC_PUBLICKEY; + eccKey = pubKey; + } } } if (ret == 0) { - /* Encode public key as DER. */ - pub_derSz = (word32)wc_EccPublicKeyToDer(eccKey, pub_der, pub_derSz, 1); - if ((int)pub_derSz <= 0) { + sz = wc_EccPublicKeyDerSize(eccKey, 1); + if (sz <= 0) { ret = WOLFSSL_FATAL_ERROR; } } - - /* This block is for actually returning the DER of the public key */ if ((ret == 0) && (der != NULL)) { int bufferPassedIn = ((*der) != NULL); + unsigned char* buf = *der; + if (!bufferPassedIn) { - *der = (unsigned char*)XMALLOC(pub_derSz, NULL, + buf = (unsigned char*)XMALLOC((size_t)sz, NULL, DYNAMIC_TYPE_PUBLIC_KEY); - if (*der == NULL) { - WOLFSSL_MSG("Failed to allocate output buffer."); + if (buf == NULL) { ret = WOLFSSL_FATAL_ERROR; } } + if ((ret == 0) && + (wc_EccPublicKeyToDer(eccKey, buf, (word32)sz, 1) != sz)) { + ret = WOLFSSL_FATAL_ERROR; + } if (ret == 0) { - XMEMCPY(*der, pub_der, pub_derSz); - if (bufferPassedIn) { - *der += pub_derSz; - } + *der = bufferPassedIn ? buf + sz : buf; + } + else if (!bufferPassedIn) { + XFREE(buf, NULL, DYNAMIC_TYPE_PUBLIC_KEY); } } - /* Dispose of allocated objects. */ - XFREE(pub_der, NULL, DYNAMIC_TYPE_PUBLIC_KEY); - XFREE(local_der, NULL, DYNAMIC_TYPE_OPENSSL); - wc_ecc_free(eccKey); - XFREE(eccKey, NULL, DYNAMIC_TYPE_ECC); + if (pubKey != NULL) { + wc_ecc_free(pubKey); + XFREE(pubKey, NULL, DYNAMIC_TYPE_ECC); + } - /* Return error or the size of the DER encoded public key. */ if (ret == 0) { - ret = (int)pub_derSz; + ret = sz; } return ret; } @@ -2964,7 +2938,7 @@ int wolfSSL_i2d_PublicKey(const WOLFSSL_EVP_PKEY *key, unsigned char **der) #endif #ifdef HAVE_ECC case WC_EVP_PKEY_EC: - return wolfssl_i_i2d_ecpublickey(key, key->ecc, der); + return wolfssl_i_i2d_ecpublickey(key->ecc, der); #endif #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) /* Emit a SubjectPublicKeyInfo (withAlg=1) rather than the bare key: diff --git a/wolfssl/internal.h b/wolfssl/internal.h index d0aa5473165..ad6efa0cf3e 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6379,6 +6379,9 @@ struct WOLFSSL_X509 { #endif buffer pubKey; int pubKeyOID; +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + WOLFSSL_EVP_PKEY* pubKeyPKey; /* owned, from wolfSSL_X509_get0_pubkey */ +#endif DNS_entry* altNamesNext; /* hint for retrieval */ #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ From f3a0e86da689f5b3db2153a3125d80d766dd4397 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 28 Oct 2026 16:36:27 +0000 Subject: [PATCH 07/31] Fix the encoding and decoding of X509 and X509_REQ objects i2d_X509_bio and i2d_X509_REQ_bio (and through it i2d_X509_REQ) encoded the certificate or request again from its parsed fields. The new encoding can order or drop extensions and attributes, so it no longer matched the signature, and a DSA request could not be written at all. Both were also only built with certificate generation, though declared for every build. Write the stored encoding, as i2d_X509 and PEM_write_bio_X509_REQ do. Without a stored encoding, encode from the fields when certificate generation is built. The oversized key identifier tests now re-encode with X509_sign and X509_REQ_sign. As in OpenSSL 3.0.13, a certificate keeps its stored encoding until it is signed again. The request setters X509_REQ_set_version, X509_REQ_set_subject_name, X509_REQ_set_pubkey and X509_REQ_add1_attr_by_NID encode the request again with its old signature, as OpenSSL's do, so X509_REQ_verify fails until it is signed again. A setter still succeeds when the request cannot be encoded again, as for DSA or Ed448; then no encoding is kept and i2d fails. A parsed request now keeps the value of its version field, as OpenSSL. It was one higher, so each re-encoding raised the version. X509_REQ_add_extensions replaced the stored encoding with only the unsigned request info, so functions that parse it, such as X509_get_ext_count, X509_REQ_get_extensions and X509_REQ_verify, worked on the wrong data. A signed request is now encoded again with its old signature, as OpenSSL does. A request never signed keeps no encoding. Extensions can now be added before the public key is set. The encoding buffer has room for the largest signature, and the encoding fails when the signature cannot be encoded instead of returning a negative length. With a non-NULL *x, d2i_X509_bio, d2i_X509_CRL_bio and PEM_read_bio_X509 leaked the object that was there. Free it and store the new one. As OpenSSL, when what d2i_X509_bio reads does not decode, it frees *x and sets it to NULL. When nothing is read *x is kept. Two tests that passed a freed certificate in *x now clear the pointer. --- src/internal.c | 2 + src/x509.c | 177 +++++++++--- tests/api.c | 1 + tests/api/test_ossl_x509_name.c | 1 + tests/api/test_x509.c | 475 +++++++++++++++++++++++++++++++- tests/api/test_x509.h | 14 +- 6 files changed, 620 insertions(+), 50 deletions(-) diff --git a/src/internal.c b/src/internal.c index 5a9adabe7b2..c03503a6469 100644 --- a/src/internal.c +++ b/src/internal.c @@ -16460,6 +16460,8 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) /* CSR attributes */ if (x509->isCSR) { + /* As OpenSSL, a request keeps the value of its version field */ + x509->version = dCert->version; copyRet = CopyREQAttributes(x509, dCert); if (copyRet != 0) ret = copyRet; diff --git a/src/x509.c b/src/x509.c index 39ea4847773..b80cb0f99e9 100644 --- a/src/x509.c +++ b/src/x509.c @@ -9552,27 +9552,75 @@ static int loadX509orX509REQFromBio(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, return ret; } +#endif /* !NO_BIO */ +#endif /* WOLFSSL_CERT_GEN */ -/* Converts the X509 to DER format and outputs it into bio. +#ifndef NO_BIO +/* Writes the DER encoding of the X509 or X509_REQ to bio. + * + * The stored encoding is written. Encoding the fields again can reorder or + * drop extensions and attributes, which breaks the signature. As in OpenSSL + * 3.0, the setters of a certificate leave that encoding until it is signed + * again, while those of a request encode the request again. * * bio is the structure to hold output DER - * x509 certificate to create DER from + * x509 certificate or request to write + * req if set then x509 is a request + * + * returns WOLFSSL_SUCCESS on success + */ +static int writeX509orX509REQDer(WOLFSSL_BIO* bio, WOLFSSL_X509* x509, + int req) +{ + const unsigned char* der; + int derSz = 0; + + if (bio == NULL || x509 == NULL) { + return WOLFSSL_FAILURE; + } + + der = wolfSSL_X509_get_der(x509, &derSz); + if (der == NULL || derSz <= 0) { + #ifdef WOLFSSL_CERT_GEN + /* No stored encoding: make one from the fields. */ + return loadX509orX509REQFromBio(bio, x509, req); + #else + (void)req; + return WOLFSSL_FAILURE; + #endif + } + + if (wolfSSL_BIO_write(bio, der, derSz) != derSz) { + return WOLFSSL_FAILURE; + } + + return WOLFSSL_SUCCESS; +} + +/* Writes the DER encoding of the X509 to bio, as i2d_X509() does. * * returns WOLFSSL_SUCCESS on success */ int wolfSSL_i2d_X509_bio(WOLFSSL_BIO* bio, WOLFSSL_X509* x509) { - return loadX509orX509REQFromBio(bio, x509, 0); + WOLFSSL_ENTER("wolfSSL_i2d_X509_bio"); + + return writeX509orX509REQDer(bio, x509, 0); } #ifdef WOLFSSL_CERT_REQ +/* Writes the DER encoding of the X509_REQ to bio. + * + * returns WOLFSSL_SUCCESS on success + */ int wolfSSL_i2d_X509_REQ_bio(WOLFSSL_BIO* bio, WOLFSSL_X509* x509) { - return loadX509orX509REQFromBio(bio, x509, 1); + WOLFSSL_ENTER("wolfSSL_i2d_X509_REQ_bio"); + + return writeX509orX509REQDer(bio, x509, 1); } #endif /* WOLFSSL_CERT_REQ */ #endif /* !NO_BIO */ -#endif /* WOLFSSL_CERT_GEN */ /* Converts an internal structure to a DER buffer * @@ -9757,10 +9805,21 @@ static WOLFSSL_X509* d2i_X509orX509REQ_bio(WOLFSSL_BIO* bio, if (localX509 == NULL) { WOLFSSL_MSG("wolfSSL_X509_d2i error"); XFREE(mem, NULL, DYNAMIC_TYPE_OPENSSL); + /* What was read does not decode: as OpenSSL, free the object passed + * in. */ + if (x509 != NULL) { + wolfSSL_X509_free(*x509); + *x509 = NULL; + } return NULL; } if (x509 != NULL) { + /* Free the object passed in and put the new one there. OpenSSL + * decodes into it instead. */ + if (*x509 != NULL && *x509 != localX509) { + wolfSSL_X509_free(*x509); + } *x509 = localX509; } @@ -10147,13 +10206,22 @@ WOLFSSL_X509_CRL *wolfSSL_d2i_X509_CRL_bio(WOLFSSL_BIO *bp, der = (byte*)XMALLOC(derSz, 0, DYNAMIC_TYPE_DER); if (der != NULL) { if (wolfSSL_BIO_read(bp, der, derSz) == derSz) { - crl = wolfSSL_d2i_X509_CRL(x, der, derSz); + crl = wolfSSL_d2i_X509_CRL(NULL, der, derSz); } } } XFREE(der, 0, DYNAMIC_TYPE_DER); + /* Free the object passed in and put the new one there. OpenSSL decodes + * into it instead. */ + if (crl != NULL && x != NULL) { + if (*x != NULL && *x != crl) { + wolfSSL_X509_CRL_free(*x); + } + *x = crl; + } + return crl; } #endif @@ -13895,6 +13963,11 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) } totalLen = AddSignature(NULL, ret, NULL, x509->sig.length, x509->sigOID); + if (totalLen <= 0) { + WOLFSSL_MSG("Unable to add signature"); + ret = WOLFSSL_FAILURE; + goto cleanup; + } if (totalLen > *derSz) { WOLFSSL_MSG("Output der buffer too short"); ret = WOLFSSL_FAILURE; @@ -13902,6 +13975,11 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) } ret = AddSignature(der, ret, x509->sig.buffer, x509->sig.length, x509->sigOID); + if (ret <= 0) { + WOLFSSL_MSG("Unable to add signature"); + ret = WOLFSSL_FAILURE; + goto cleanup; + } } *derSz = ret; @@ -14807,7 +14885,12 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, CERT_TYPE, cb, u); } - if (x != NULL) { + /* Free the object passed in and put the new one there. OpenSSL decodes + * into it instead. Keep it when nothing was read. */ + if (x != NULL && x509 != NULL) { + if (*x != NULL && *x != x509) { + wolfSSL_X509_free(*x); + } *x = x509; } @@ -18464,6 +18547,8 @@ void wolfSSL_X509_REQ_free(WOLFSSL_X509* req) wolfSSL_X509_free(req); } +static void regenX509REQDerBuffer(WOLFSSL_X509* x509); + int wolfSSL_X509_REQ_set_version(WOLFSSL_X509 *x, long version) { WOLFSSL_ENTER("wolfSSL_X509_REQ_set_version"); @@ -18471,6 +18556,8 @@ int wolfSSL_X509_REQ_set_version(WOLFSSL_X509 *x, long version) return WOLFSSL_FAILURE; } x->version = (int)version; + /* As OpenSSL, a request that is changed is encoded again */ + regenX509REQDerBuffer(x); return WOLFSSL_SUCCESS; } @@ -18543,44 +18630,42 @@ int wolfSSL_X509_REQ_sign_ctx(WOLFSSL_X509 *req, return WOLFSSL_FAILURE; } -static int regenX509REQDerBuffer(WOLFSSL_X509* x509) +/* Store the encoding of a request whose fields changed. A signed request keeps + * its old signature until it is signed again. An unsigned request has no + * complete encoding, and neither has one that cannot be encoded, as a DSA + * request: then none is kept, so none out of date is written. The change + * stands, as in OpenSSL, which encodes a request when it is written. */ +static void regenX509REQDerBuffer(WOLFSSL_X509* x509) { - int derSz = X509_BUFFER_SZ; - int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); -#ifndef WOLFSSL_SMALL_STACK - byte der[X509_BUFFER_SZ]; -#else - byte* der; + /* Room for the request info and the largest signature. */ + int derSz = X509_BUFFER_SZ + MAX_SIG_SZ; + DerBuffer* newDer = NULL; + WC_DECLARE_VAR(der, byte, X509_BUFFER_SZ + MAX_SIG_SZ, 0); - der = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (!der) { - WOLFSSL_MSG("malloc failed"); - return WOLFSSL_FAILURE; - } +#if !defined(WOLFSSL_NO_CA_NAMES) && defined(OPENSSL_EXTRA) + /* The names are encoded again, not as they were in the old encoding + * or in the names they were set from, and not where they were. */ + ClearX509NameRaw(&x509->issuer); + ClearX509NameRaw(&x509->subject); #endif - - if (wolfssl_x509_make_der(x509, 1, der, &derSz, 0) == WOLFSSL_SUCCESS) { + if (x509->sig.buffer == NULL) { FreeDer(&x509->derCert); - #if !defined(WOLFSSL_NO_CA_NAMES) && defined(OPENSSL_EXTRA) - /* The names are encoded again, not as they were in the old encoding - * or in the names they were set from, and not where they were. */ - ClearX509NameRaw(&x509->issuer); - ClearX509NameRaw(&x509->subject); - #endif - if (AllocDer(&x509->derCert, (word32)derSz, CERT_TYPE, - x509->heap) == 0) { - XMEMCPY(x509->derCert->buffer, der, derSz); - ret = WOLFSSL_SUCCESS; - } - else { - WOLFSSL_MSG("Failed to allocate DER buffer for X509"); - } + return; } - else { + + WC_ALLOC_VAR_EX(der, byte, derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER, + { FreeDer(&x509->derCert); return; }); + + if (wolfssl_x509_make_der(x509, 1, der, &derSz, 1) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("Unable to make DER for X509 REQ"); } + else if (AllocCopyDer(&newDer, der, (word32)derSz, CERT_TYPE, + x509->heap) != 0) { + WOLFSSL_MSG("Failed to allocate DER buffer for X509"); + } + FreeDer(&x509->derCert); + x509->derCert = newDer; WC_FREE_VAR_EX(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return ret; } int wolfSSL_X509_REQ_add_extensions(WOLFSSL_X509* req, @@ -18610,7 +18695,8 @@ int wolfSSL_X509_REQ_add_extensions(WOLFSSL_X509* req, ext_sk = ext_sk->next; } - return regenX509REQDerBuffer(req); + regenX509REQDerBuffer(req); + return WOLFSSL_SUCCESS; } int wolfSSL_X509_REQ_add1_attr_by_txt(WOLFSSL_X509 *req, @@ -18763,6 +18849,9 @@ int wolfSSL_X509_REQ_add1_attr_by_NID(WOLFSSL_X509 *req, if (ret != WOLFSSL_SUCCESS) wolfSSL_X509_ATTRIBUTE_free(attr); } + if (ret == WOLFSSL_SUCCESS) { + regenX509REQDerBuffer(req); + } return ret; } @@ -18779,12 +18868,20 @@ WOLFSSL_X509 *wolfSSL_X509_to_X509_REQ(WOLFSSL_X509 *x, int wolfSSL_X509_REQ_set_subject_name(WOLFSSL_X509 *req, WOLFSSL_X509_NAME *name) { - return wolfSSL_X509_set_subject_name(req, name); + if (wolfSSL_X509_set_subject_name(req, name) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + regenX509REQDerBuffer(req); + return WOLFSSL_SUCCESS; } int wolfSSL_X509_REQ_set_pubkey(WOLFSSL_X509 *req, WOLFSSL_EVP_PKEY *pkey) { - return wolfSSL_X509_set_pubkey(req, pkey); + if (wolfSSL_X509_set_pubkey(req, pkey) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + regenX509REQDerBuffer(req); + return WOLFSSL_SUCCESS; } #endif /* OPENSSL_ALL && WOLFSSL_CERT_GEN && WOLFSSL_CERT_REQ */ diff --git a/tests/api.c b/tests/api.c index 45dd3a43341..1cebde6e51a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22704,6 +22704,7 @@ static int test_othername_and_SID_ext(void) sid_oid = NULL; ASN1_OCTET_STRING_free(sid_data); X509_REQ_free(x509); + x509 = NULL; EVP_PKEY_free(priv); /* At this point everything used to generate what is in der is cleaned up. diff --git a/tests/api/test_ossl_x509_name.c b/tests/api/test_ossl_x509_name.c index 3b21c0b2f69..ac83e173470 100644 --- a/tests/api/test_ossl_x509_name.c +++ b/tests/api/test_ossl_x509_name.c @@ -419,6 +419,7 @@ int test_wolfSSL_X509_NAME_print_ex(void) membio = NULL; X509_free(x509); + x509 = NULL; BIO_free(bio); name = NULL; diff --git a/tests/api/test_x509.c b/tests/api/test_x509.c index 85df3aba4bc..46077df7bb2 100644 --- a/tests/api/test_x509.c +++ b/tests/api/test_x509.c @@ -840,6 +840,9 @@ int test_x509_CertFromX509_akid_overflow(void) unsigned char* akid_val = NULL; WOLFSSL_X509* x = NULL; WOLFSSL_BIO* bio = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + const unsigned char* keyDer = ecc_key_der_256; + char* bioData = NULL; #ifdef WOLFSSL_SMALL_STACK buf = (unsigned char*)XMALLOC(16384, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -1038,13 +1041,21 @@ int test_x509_CertFromX509_akid_overflow(void) x = wolfSSL_X509_d2i(NULL, buf, (int)pos); ExpectNotNull(x); - /* Attempt re-encode via i2d_X509_bio -- must fail gracefully, not - * overflow. Before the fix this would write ~4000 bytes past the - * end of cert->akid[]. */ + /* i2d_X509_bio writes the parsed DER, it does not re-encode. */ bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); ExpectNotNull(bio); - ExpectIntEQ(wolfSSL_i2d_X509_bio(bio, x), 0); + ExpectIntEQ(wolfSSL_i2d_X509_bio(bio, x), 1); + ExpectIntEQ(wolfSSL_BIO_get_mem_data(bio, &bioData), (int)pos); + ExpectBufEQ(bioData, buf, pos); + + /* Attempt re-encode via X509_sign -- must fail gracefully, not + * overflow. Before the fix this would write ~4000 bytes past the + * end of cert->akid[]. */ + ExpectNotNull(pkey = wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_EC, NULL, &keyDer, + (long)sizeof_ecc_key_der_256)); + ExpectIntEQ(wolfSSL_X509_sign(x, pkey, wolfSSL_EVP_sha256()), 0); + wolfSSL_EVP_PKEY_free(pkey); wolfSSL_BIO_free(bio); wolfSSL_X509_free(x); XFREE(akid_val, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -1134,6 +1145,9 @@ int test_x509_ReqCertFromX509_skid_overflow(void) WOLFSSL_X509* req = NULL; WOLFSSL_BIO* bio = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + const unsigned char* keyDer = ecc_key_der_256; + char* bioData = NULL; /* Step 1: Parse the crafted CSR -- this should succeed (the parser * dynamically allocates subjKeyId to the parsed size). */ @@ -1141,13 +1155,23 @@ int test_x509_ReqCertFromX509_skid_overflow(void) (int)sizeof(crafted_csr_der)); ExpectNotNull(req); - /* Step 2: Attempt to re-encode. Before the fix, this triggered a - * heap buffer overflow in ReqCertFromX509() writing 64 bytes into - * cert->skid[32]. With the fix, it must return failure. */ + /* i2d_X509_REQ_bio writes the parsed DER, it does not re-encode. */ bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); ExpectNotNull(bio); - ExpectIntNE(wolfSSL_i2d_X509_REQ_bio(bio, req), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_i2d_X509_REQ_bio(bio, req), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_BIO_get_mem_data(bio, &bioData), + (int)sizeof(crafted_csr_der)); + ExpectBufEQ(bioData, crafted_csr_der, sizeof(crafted_csr_der)); + + /* Step 2: Attempt to re-encode via X509_REQ_sign. Before the fix, this + * triggered a heap buffer overflow in ReqCertFromX509() writing 64 bytes + * into cert->skid[32]. With the fix, it must return failure. */ + ExpectNotNull(pkey = wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_EC, NULL, &keyDer, + (long)sizeof_ecc_key_der_256)); + ExpectIntNE(wolfSSL_X509_REQ_sign(req, pkey, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + wolfSSL_EVP_PKEY_free(pkey); wolfSSL_BIO_free(bio); wolfSSL_X509_free(req); #endif @@ -1485,3 +1509,438 @@ int test_x509_decision_coverage(void) #endif return EXPECT_RESULT(); } + +/* Test that i2d_X509_REQ() writes a parsed request as it was read and writes + * nothing for a request that was never signed. */ +int test_x509_REQ_i2d_stored_der(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \ + defined(WOLFSSL_CERT_EXT) && !defined(NO_BIO) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && defined(HAVE_ECC) + const char* csrFiles[] = { + "./certs/csr.attr.der", + "./certs/csr.ext.der", + "./certs/csr.signed.der", + #ifndef NO_DSA + "./certs/csr.dsa.der", + #endif + }; + byte buf[1024]; + int bufSz = 0; + XFILE f = XBADFILE; + WOLFSSL_X509* req = NULL; + WOLFSSL_X509* parsed = NULL; + WOLFSSL_X509_NAME* name = NULL; + WOLFSSL_X509_EXTENSION* ext = NULL; + WOLF_STACK_OF(WOLFSSL_X509_EXTENSION)* exts = NULL; + WOLFSSL_EVP_PKEY* priv = NULL; + WOLFSSL_EVP_PKEY* pub = NULL; + const unsigned char* ecPriv = ecc_clikey_der_256; + const unsigned char* ecPub = ecc_clikeypub_der_256; + unsigned char* der = NULL; + int derSz = 0; + size_t i; + + for (i = 0; i < sizeof(csrFiles) / sizeof(csrFiles[0]); i++) { + ExpectTrue((f = XFOPEN(csrFiles[i], "rb")) != XBADFILE); + ExpectIntGT(bufSz = (int)XFREAD(buf, 1, sizeof(buf), f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + ExpectNotNull(req = wolfSSL_X509_REQ_d2i(NULL, buf, bufSz)); + ExpectIntEQ(derSz = wolfSSL_i2d_X509_REQ(req, &der), bufSz); + ExpectBufEQ(der, buf, bufSz); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + der = NULL; + wolfSSL_X509_free(req); + req = NULL; + } + + ExpectNotNull(priv = wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_EC, NULL, &ecPriv, + (long)sizeof_ecc_clikey_der_256)); + ExpectNotNull(pub = wolfSSL_d2i_PUBKEY(NULL, &ecPub, + (long)sizeof_ecc_clikeypub_der_256)); + ExpectNotNull(req = wolfSSL_X509_REQ_new()); + ExpectNotNull(name = wolfSSL_X509_NAME_new()); + ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "commonName", + MBSTRING_UTF8, (const byte*)"Test", 4, -1, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, name), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_set_pubkey(req, pub), WOLFSSL_SUCCESS); + ExpectNotNull(ext = wolfSSL_X509V3_EXT_nconf_nid(NULL, NULL, + WC_NID_key_usage, "digitalSignature")); + ExpectNotNull(exts = wolfSSL_sk_X509_EXTENSION_new_null()); + ExpectIntEQ(wolfSSL_sk_X509_EXTENSION_push(exts, ext), 1); + if (EXPECT_FAIL()) { + wolfSSL_X509_EXTENSION_free(ext); + } + ExpectIntEQ(wolfSSL_X509_REQ_add_extensions(req, exts), WOLFSSL_SUCCESS); + + /* Not signed yet. */ + ExpectNull(wolfSSL_X509_get_der(req, &derSz)); + ExpectIntLE(wolfSSL_i2d_X509_REQ(req, &der), 0); + ExpectNull(der); + + ExpectIntEQ(wolfSSL_X509_REQ_sign(req, priv, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + ExpectIntGT(derSz = wolfSSL_i2d_X509_REQ(req, &der), 0); + ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz)); + ExpectIntEQ(wolfSSL_X509_REQ_verify(parsed, pub), WOLFSSL_SUCCESS); + if (parsed != NULL) { + ExpectIntEQ(parsed->keyUsageSet, 1); + ExpectIntEQ(parsed->keyUsage, KEYUSE_DIGITAL_SIG); + } + + wolfSSL_X509_free(parsed); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + wolfSSL_sk_X509_EXTENSION_pop_free(exts, wolfSSL_X509_EXTENSION_free); + wolfSSL_X509_NAME_free(name); + wolfSSL_X509_free(req); + wolfSSL_EVP_PKEY_free(pub); + wolfSSL_EVP_PKEY_free(priv); +#endif + return EXPECT_RESULT(); +} + +/* Test that X509_REQ_add_extensions() on a parsed request stores the whole + * request, with the new extension and the old signature. */ +int test_x509_REQ_add_extensions_parsed(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \ + defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_ALT_NAMES) && \ + !defined(NO_BIO) && (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + byte buf[1024]; + int bufSz = 0; + XFILE f = XBADFILE; + WOLFSSL_X509* req = NULL; + WOLFSSL_X509* parsed = NULL; + WOLFSSL_X509_EXTENSION* ext = NULL; + WOLF_STACK_OF(WOLFSSL_X509_EXTENSION)* exts = NULL; + WOLF_STACK_OF(WOLFSSL_X509_EXTENSION)* reqExts = NULL; + WOLFSSL_EVP_PKEY* pub = NULL; + const unsigned char* der = NULL; + int derSz = 0; + unsigned char* out = NULL; + + /* Has subjectAltName and keyUsage. */ + ExpectTrue((f = XFOPEN("./certs/csr.ext.der", "rb")) != XBADFILE); + ExpectIntGT(bufSz = (int)XFREAD(buf, 1, sizeof(buf), f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + } + ExpectNotNull(req = wolfSSL_X509_REQ_d2i(NULL, buf, bufSz)); + ExpectNotNull(pub = wolfSSL_X509_get_pubkey(req)); + ExpectIntEQ(wolfSSL_X509_REQ_verify(req, pub), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_get_ext_count(req), 2); + + ExpectNotNull(ext = wolfSSL_X509V3_EXT_nconf_nid(NULL, NULL, + WC_NID_ext_key_usage, "serverAuth")); + ExpectNotNull(exts = wolfSSL_sk_X509_EXTENSION_new_null()); + ExpectIntEQ(wolfSSL_sk_X509_EXTENSION_push(exts, ext), 1); + if (EXPECT_FAIL()) { + wolfSSL_X509_EXTENSION_free(ext); + } + ExpectIntEQ(wolfSSL_X509_REQ_add_extensions(req, exts), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_X509_get_ext_count(req), 3); + ExpectNotNull(reqExts = (WOLF_STACK_OF(WOLFSSL_X509_EXTENSION)*) + wolfSSL_X509_REQ_get_extensions(req)); + ExpectIntEQ(wolfSSL_sk_X509_EXTENSION_num(reqExts), 3); + + /* The stored encoding is the whole request. */ + ExpectNotNull(der = wolfSSL_X509_get_der(req, &derSz)); + ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz)); + ExpectIntEQ(wolfSSL_X509_get_ext_count(parsed), 3); + ExpectIntGE(wolfSSL_X509_get_ext_by_NID(parsed, WC_NID_ext_key_usage, -1), + 0); + ExpectIntEQ(wolfSSL_i2d_X509_REQ(req, &out), derSz); + ExpectBufEQ(out, der, derSz); + + /* The old signature does not cover the new extension. */ + ExpectIntNE(wolfSSL_X509_REQ_verify(req, pub), WOLFSSL_SUCCESS); + + XFREE(out, NULL, DYNAMIC_TYPE_OPENSSL); + wolfSSL_X509_free(parsed); + wolfSSL_sk_X509_EXTENSION_pop_free(reqExts, wolfSSL_X509_EXTENSION_free); + wolfSSL_sk_X509_EXTENSION_pop_free(exts, wolfSSL_X509_EXTENSION_free); + wolfSSL_EVP_PKEY_free(pub); + wolfSSL_X509_free(req); +#endif + return EXPECT_RESULT(); +} + +/* Test that a signed request changed by X509_REQ_add_extensions() verifies + * again once it is signed again. */ +int test_x509_REQ_add_extensions_resign(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \ + defined(WOLFSSL_CERT_EXT) && !defined(NO_BIO) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_RSA) + WOLFSSL_X509* req = NULL; + WOLFSSL_X509* parsed = NULL; + WOLFSSL_X509_NAME* name = NULL; + WOLFSSL_X509_EXTENSION* ext = NULL; + WOLF_STACK_OF(WOLFSSL_X509_EXTENSION)* exts = NULL; + WOLFSSL_EVP_PKEY* priv = NULL; + WOLFSSL_EVP_PKEY* pub = NULL; + const unsigned char* rsaPriv = client_key_der_2048; + const unsigned char* rsaPub = client_keypub_der_2048; + unsigned char* der = NULL; + int derSz = 0; + + ExpectNotNull(priv = wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_RSA, NULL, + &rsaPriv, (long)sizeof_client_key_der_2048)); + ExpectNotNull(pub = wolfSSL_d2i_PUBKEY(NULL, &rsaPub, + (long)sizeof_client_keypub_der_2048)); + ExpectNotNull(req = wolfSSL_X509_REQ_new()); + ExpectNotNull(name = wolfSSL_X509_NAME_new()); + ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "commonName", + MBSTRING_UTF8, (const byte*)"Test", 4, -1, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, name), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_set_pubkey(req, pub), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_sign(req, priv, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + ExpectIntGT(derSz = wolfSSL_i2d_X509_REQ(req, &der), 0); + ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz)); + ExpectIntEQ(wolfSSL_X509_REQ_verify(parsed, pub), WOLFSSL_SUCCESS); + + ExpectNotNull(ext = wolfSSL_X509V3_EXT_nconf_nid(NULL, NULL, + WC_NID_ext_key_usage, "serverAuth")); + ExpectNotNull(exts = wolfSSL_sk_X509_EXTENSION_new_null()); + ExpectIntEQ(wolfSSL_sk_X509_EXTENSION_push(exts, ext), 1); + if (EXPECT_FAIL()) { + wolfSSL_X509_EXTENSION_free(ext); + } + ExpectIntEQ(wolfSSL_X509_REQ_add_extensions(parsed, exts), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_get_ext_count(parsed), 1); + ExpectIntNE(wolfSSL_X509_REQ_verify(parsed, pub), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_X509_REQ_sign(parsed, priv, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_get_ext_count(parsed), 1); + ExpectIntEQ(wolfSSL_X509_REQ_verify(parsed, pub), WOLFSSL_SUCCESS); + + wolfSSL_X509_free(parsed); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + wolfSSL_sk_X509_EXTENSION_pop_free(exts, wolfSSL_X509_EXTENSION_free); + wolfSSL_X509_NAME_free(name); + wolfSSL_X509_free(req); + wolfSSL_EVP_PKEY_free(pub); + wolfSSL_EVP_PKEY_free(priv); +#endif + return EXPECT_RESULT(); +} + +/* Test that a request changed by a setter is encoded again, with its old + * signature, as in OpenSSL, while a certificate keeps its stored encoding + * until it is signed again, as in OpenSSL 3.0. */ +int test_x509_REQ_setters_reencode(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_CERT_REQ) && defined(WOLFSSL_CERT_GEN) && \ + !defined(NO_BIO) && (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLFSSL_X509* req = NULL; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_X509* parsed = NULL; + WOLFSSL_X509_NAME* name = NULL; + WOLFSSL_X509_NAME* same = NULL; + WOLFSSL_EVP_PKEY* pub = NULL; + WOLFSSL_EVP_PKEY* newPub = NULL; + WOLFSSL_BIO* bio = NULL; + XFILE f = XBADFILE; + byte fileDer[1024]; + int fileSz = 0; + WOLFSSL_EVP_PKEY* parsedPub = NULL; + unsigned char* keyA = NULL; + unsigned char* keyB = NULL; + int keySz = 0; + unsigned char* der = NULL; + int derSz = 0; + char cn[64]; + int i; + + ExpectNotNull(name = wolfSSL_X509_NAME_new()); + ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "commonName", + MBSTRING_UTF8, (const byte*)"changed", -1, -1, 0), WOLFSSL_SUCCESS); + + for (i = 0; i < 2; i++) { + ExpectNotNull(bio = wolfSSL_BIO_new_file("./certs/csr.signed.der", + "rb")); + ExpectNotNull(req = wolfSSL_d2i_X509_REQ_bio(bio, NULL)); + wolfSSL_BIO_free(bio); + bio = NULL; + ExpectNotNull(pub = wolfSSL_X509_get_pubkey(req)); + ExpectIntEQ(wolfSSL_X509_REQ_verify(req, pub), WOLFSSL_SUCCESS); + if (i == 0) { + ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, name), + WOLFSSL_SUCCESS); + } + else { + ExpectIntEQ(wolfSSL_X509_REQ_add1_attr_by_NID(req, + WC_NID_pkcs9_challengePassword, WOLFSSL_MBSTRING_ASC, + (const unsigned char*)"password", -1), WOLFSSL_SUCCESS); + } + /* The old signature no longer verifies */ + ExpectIntNE(wolfSSL_X509_REQ_verify(req, pub), WOLFSSL_SUCCESS); + + ExpectNotNull(bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem())); + ExpectIntEQ(wolfSSL_i2d_X509_REQ_bio(bio, req), WOLFSSL_SUCCESS); + ExpectNotNull(parsed = wolfSSL_d2i_X509_REQ_bio(bio, NULL)); + if (i == 0) { + ExpectIntGT(wolfSSL_X509_NAME_get_text_by_NID( + wolfSSL_X509_get_subject_name(parsed), WC_NID_commonName, cn, + (int)sizeof(cn)), 0); + ExpectStrEQ(cn, "changed"); + } + else { + if (parsed != NULL) { + ExpectStrEQ(parsed->challengePw, "password"); + } + #ifdef OPENSSL_ALL + /* Only then are the attributes of a parsed request kept */ + ExpectIntGE(wolfSSL_X509_REQ_get_attr_by_NID(parsed, + WC_NID_pkcs9_challengePassword, -1), 0); + #endif + } + wolfSSL_X509_free(parsed); + parsed = NULL; + wolfSSL_BIO_free(bio); + bio = NULL; + wolfSSL_EVP_PKEY_free(pub); + pub = NULL; + wolfSSL_X509_free(req); + req = NULL; + } + + /* As OpenSSL, the version is the value of its field, and a field set to + * what it holds leaves the request as it was */ + ExpectTrue((f = XFOPEN("./certs/csr.signed.der", "rb")) != XBADFILE); + ExpectIntGT(fileSz = (int)XFREAD(fileDer, 1, sizeof(fileDer), f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + } + ExpectNotNull(req = wolfSSL_X509_REQ_d2i(NULL, fileDer, fileSz)); + ExpectIntEQ(wolfSSL_X509_REQ_get_version(req), 0); + ExpectNotNull(same = wolfSSL_X509_NAME_dup( + wolfSSL_X509_get_subject_name(req))); + ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, same), WOLFSSL_SUCCESS); + ExpectNotNull(pub = wolfSSL_X509_get_pubkey(req)); + ExpectIntEQ(wolfSSL_X509_REQ_set_pubkey(req, pub), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_REQ_set_version(req, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(derSz = wolfSSL_i2d_X509_REQ(req, &der), fileSz); + ExpectBufEQ(der, fileDer, fileSz); + ExpectIntEQ(wolfSSL_X509_REQ_verify(req, pub), WOLFSSL_SUCCESS); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + der = NULL; + + /* Another version and key are encoded */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(newPub = wolfSSL_X509_get_pubkey(x509)); + ExpectIntEQ(wolfSSL_X509_REQ_set_version(req, 1), WOLFSSL_SUCCESS); + ExpectIntGT(derSz = wolfSSL_i2d_X509_REQ(req, &der), 0); + ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz)); + ExpectIntEQ(wolfSSL_X509_REQ_get_version(parsed), 1); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + der = NULL; + wolfSSL_X509_free(parsed); + parsed = NULL; + ExpectIntEQ(wolfSSL_X509_REQ_set_pubkey(req, newPub), WOLFSSL_SUCCESS); + ExpectIntGT(derSz = wolfSSL_i2d_X509_REQ(req, &der), 0); + ExpectNotNull(parsed = wolfSSL_X509_REQ_d2i(NULL, der, derSz)); + ExpectNotNull(parsedPub = wolfSSL_X509_get_pubkey(parsed)); + ExpectIntGT(keySz = wolfSSL_i2d_PUBKEY(parsedPub, &keyA), 0); + ExpectIntEQ(wolfSSL_i2d_PUBKEY(newPub, &keyB), keySz); + ExpectBufEQ(keyA, keyB, keySz); + XFREE(keyA, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(keyB, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + der = NULL; + wolfSSL_EVP_PKEY_free(parsedPub); + wolfSSL_X509_free(parsed); + parsed = NULL; + wolfSSL_EVP_PKEY_free(newPub); + wolfSSL_EVP_PKEY_free(pub); + wolfSSL_X509_NAME_free(same); + wolfSSL_X509_free(req); + req = NULL; + +#ifndef NO_DSA + /* A DSA request cannot be encoded again: the change stands and no + * encoding out of date is kept */ + ExpectNotNull(bio = wolfSSL_BIO_new_file("./certs/csr.dsa.der", "rb")); + ExpectNotNull(req = wolfSSL_d2i_X509_REQ_bio(bio, NULL)); + wolfSSL_BIO_free(bio); + bio = NULL; + ExpectNotNull(wolfSSL_X509_get_der(req, &derSz)); + ExpectIntEQ(wolfSSL_X509_REQ_set_subject_name(req, name), WOLFSSL_SUCCESS); + ExpectNull(wolfSSL_X509_get_der(req, &derSz)); + ExpectNotNull(bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem())); + ExpectIntEQ(wolfSSL_i2d_X509_REQ_bio(bio, req), WOLFSSL_FAILURE); + wolfSSL_BIO_free(bio); + bio = NULL; + wolfSSL_X509_free(req); + req = NULL; +#endif + + ExpectNotNull(bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem())); + ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_i2d_X509_bio(bio, x509), WOLFSSL_SUCCESS); + ExpectNotNull(parsed = wolfSSL_d2i_X509_bio(bio, NULL)); + ExpectIntGT(wolfSSL_X509_NAME_get_text_by_NID( + wolfSSL_X509_get_subject_name(parsed), WC_NID_commonName, cn, + (int)sizeof(cn)), 0); + ExpectStrEQ(cn, "www.wolfssl.com"); + + wolfSSL_X509_free(parsed); + wolfSSL_BIO_free(bio); + wolfSSL_X509_free(x509); + wolfSSL_X509_NAME_free(name); +#endif + return EXPECT_RESULT(); +} + +/* Test that d2i_X509_bio() replaces the object passed in and, as OpenSSL, + * frees it when what it read does not decode. */ +int test_x509_d2i_bio_reuse(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_BIO) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + static const byte bad[] = { 0x30, 0x04, 0x02, 0x01, 0x01, 0x05 }; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_X509* ret = NULL; + WOLFSSL_BIO* bio = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(bio = wolfSSL_BIO_new_file("./certs/ca-cert.der", "rb")); + ExpectNotNull(ret = wolfSSL_d2i_X509_bio(bio, &x509)); + ExpectPtrEq(ret, x509); + wolfSSL_BIO_free(bio); + bio = NULL; + + ExpectNotNull(bio = wolfSSL_BIO_new_mem_buf(bad, (int)sizeof(bad))); + ExpectNull(wolfSSL_d2i_X509_bio(bio, &x509)); + ExpectNull(x509); + wolfSSL_BIO_free(bio); + bio = NULL; + + /* Nothing read: kept */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(bio = wolfSSL_BIO_new(wolfSSL_BIO_s_mem())); + ExpectNull(wolfSSL_d2i_X509_bio(bio, &x509)); + ExpectNotNull(x509); + wolfSSL_BIO_free(bio); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_x509.h b/tests/api/test_x509.h index d475b120a38..32277035ba4 100644 --- a/tests/api/test_x509.h +++ b/tests/api/test_x509.h @@ -35,6 +35,11 @@ int test_x509_ReqCertFromX509_skid_boundary(void); int test_x509_ReqCertFromX509_ext_critical(void); int test_x509_REQ_sign_mldsa(void); int test_x509_decision_coverage(void); +int test_x509_REQ_i2d_stored_der(void); +int test_x509_REQ_add_extensions_parsed(void); +int test_x509_REQ_add_extensions_resign(void); +int test_x509_REQ_setters_reencode(void); +int test_x509_d2i_bio_reuse(void); #define TEST_X509_DECLS \ TEST_DECL_GROUP("x509", test_x509_rfc2818_verification_callback), \ @@ -48,7 +53,12 @@ int test_x509_decision_coverage(void); TEST_DECL_GROUP("x509", test_x509_ReqCertFromX509_skid_overflow), \ TEST_DECL_GROUP("x509", test_x509_ReqCertFromX509_skid_boundary), \ TEST_DECL_GROUP("x509", test_x509_ReqCertFromX509_ext_critical), \ - TEST_DECL_GROUP("x509", test_x509_REQ_sign_mldsa), \ - TEST_DECL_GROUP("x509", test_x509_decision_coverage) + TEST_DECL_GROUP("x509", test_x509_REQ_sign_mldsa), \ + TEST_DECL_GROUP("x509", test_x509_decision_coverage), \ + TEST_DECL_GROUP("x509", test_x509_REQ_i2d_stored_der), \ + TEST_DECL_GROUP("x509", test_x509_REQ_add_extensions_parsed), \ + TEST_DECL_GROUP("x509", test_x509_REQ_add_extensions_resign), \ + TEST_DECL_GROUP("x509", test_x509_REQ_setters_reencode), \ + TEST_DECL_GROUP("x509", test_x509_d2i_bio_reuse) #endif /* WOLFCRYPT_TEST_X509_H */ From 0a2bc6d6d43ea7029b29de49a9c41c7bc4206d33 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 2 Nov 2026 16:02:01 +0000 Subject: [PATCH 08/31] Fix ASN1_INTEGER_dup, ASN1_INTEGER_get and d2i_ASN1_INTEGER ASN1_INTEGER_dup of an INTEGER too long for the inline buffer, such as a 20 byte certificate serial, did not set the length of the copy. ASN1_INTEGER_get of a negative INTEGER from d2i_ASN1_INTEGER returned its magnitude. d2i_ASN1_INTEGER took an encoding with a redundant leading octet, which OpenSSL rejects. --- src/ssl_asn1.c | 10 +++++++++- tests/api/test_ossl_asn1.c | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 1b4390b26d0..8eb647f6744 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -1261,6 +1261,7 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_ASN1_INTEGER_dup(const WOLFSSL_ASN1_INTEGER* src) } else { XMEMCPY(dst->data, src->data, (size_t)src->length); + dst->length = src->length; } } @@ -1361,7 +1362,7 @@ static int wolfssl_asn1_int_twos_compl(byte* data, int length, byte* neg) } else { if (neg != NULL) { - *neg = data[idx] & 0x80; + *neg = (data[idx] & 0x80) ? 1 : 0; } if ((neg == NULL) || (*neg != 0)) { wolfssl_twos_compl(data + idx, length - (int)idx); @@ -1504,6 +1505,13 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_d2i_ASN1_INTEGER(WOLFSSL_ASN1_INTEGER** a, WOLFSSL_MSG("ASN.1 length too large."); err = 1; } + /* DER has no redundant leading octet. OpenSSL rejects one. */ + if ((!err) && (len > 1) && + ((((*in)[idx] == 0x00) && (((*in)[idx + 1] & 0x80) == 0)) || + (((*in)[idx] == 0xff) && (((*in)[idx + 1] & 0x80) != 0)))) { + WOLFSSL_MSG("ASN.1 INTEGER not minimal."); + err = 1; + } /* Allocate a new ASN.1 INTEGER object. */ if ((!err) && ((ret = wolfSSL_ASN1_INTEGER_new()) == NULL)) { err = 1; diff --git a/tests/api/test_ossl_asn1.c b/tests/api/test_ossl_asn1.c index 24207f9e171..8b731e8db07 100644 --- a/tests/api/test_ossl_asn1.c +++ b/tests/api/test_ossl_asn1.c @@ -134,6 +134,8 @@ int test_wolfSSL_ASN1_INTEGER(void) ExpectNotNull(a = d2i_ASN1_INTEGER(NULL, &p, sizeof(longDer))); ExpectPtrNE(p, longDer); ExpectNotNull(dup = wolfSSL_ASN1_INTEGER_dup(a)); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(dup, a), 0); + ExpectIntEQ(wolfSSL_i2d_ASN1_INTEGER(dup, NULL), (int)sizeof(longDer)); ASN1_INTEGER_free(dup); ASN1_INTEGER_free(a); #endif @@ -498,6 +500,9 @@ int test_wolfSSL_d2i_ASN1_INTEGER(void) 0xd2, 0x96, 0xdf, 0xd9, 0xd0, 0x4f, 0xad, 0xd7 }; static const byte garbageDer[] = {0xDE, 0xAD, 0xBE, 0xEF}; + /* Not minimal: OpenSSL rejects them. */ + static const byte padZeroDer[] = {0x02, 0x02, 0x00, 0x01}; + static const byte padOnesDer[] = {0x02, 0x02, 0xFF, 0x80}; /* Long-form length with INT_MAX content bytes for overflow testing */ static const byte overflowLenDer[] = {0x02, 0x84, 0x7F, 0xFF, 0xFF, 0xFF}; @@ -530,6 +535,12 @@ int test_wolfSSL_d2i_ASN1_INTEGER(void) p = garbageDer; ExpectNull((a = wolfSSL_d2i_ASN1_INTEGER(&b, &p, sizeof(garbageDer)))); ExpectNull(b); + p = padZeroDer; + ExpectNull((a = wolfSSL_d2i_ASN1_INTEGER(&b, &p, sizeof(padZeroDer)))); + ExpectNull(b); + p = padOnesDer; + ExpectNull((a = wolfSSL_d2i_ASN1_INTEGER(&b, &p, sizeof(padOnesDer)))); + ExpectNull(b); /* Only run on 64-bit systems where the `long` inSz param fits */ if (LONG_MAX >= 0x80000005L) { p = overflowLenDer; @@ -578,6 +589,7 @@ int test_wolfSSL_d2i_ASN1_INTEGER(void) c = NULL; if (testVectors[i].derSz <= sizeof(long)) { + ExpectIntEQ(wolfSSL_ASN1_INTEGER_get(a), testVectors[i].value); ExpectNotNull(c = wolfSSL_ASN1_INTEGER_new()); ExpectIntEQ(wolfSSL_ASN1_INTEGER_set(c, testVectors[i].value), 1); ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(a, c), 0); From e906a9c5f93be4cf81f243f724d09426eff57a85 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:20:31 +0000 Subject: [PATCH 09/31] Dispatch ASN1_STRING_length, get0_data and type on ASN1_INTEGER In OpenSSL an ASN1_INTEGER is an ASN1_STRING. _Generic dispatched ASN1_STRING_length and ASN1_STRING_get0_data on one with C11 only: GCC and clang without C11 passed the ASN1_INTEGER to the ASN1_STRING functions and failed with -Werror. Choose at compile time with __builtin_choose_expr there, keeping the argument type checked. ASN1_STRING_type did not take an ASN1_INTEGER at all. Dispatch it the same way to the new wolfSSL_ASN1_INTEGER_get_type, which returns V_ASN1_NEG_INTEGER for a negative INTEGER and V_ASN1_INTEGER otherwise, as OpenSSL. It reads the negative flag, as the other INTEGER functions do: the type member is 0 in an INTEGER from ASN1_INTEGER_new. The ASN1_STRING_type macro moves from openssl/asn1.h, which includes openssl/ssl.h, to the others there. As with _Generic, these are function-like macros: a function pointer is taken of the wolfSSL_ASN1_STRING_* or wolfSSL_ASN1_INTEGER_* function. test_wolfSSL_ASN1_INTEGER_STRING calls the three on INTEGERs, const or not: on NULL, for the type of INTEGERs from ASN1_INTEGER_new, ASN1_INTEGER_set, BN_to_ASN1_INTEGER, d2i_ASN1_INTEGER and ASN1_INTEGER_dup, and for the length and data of 256 and of a cert serial. --- src/ssl_asn1.c | 20 ++++++++++ tests/api/test_ossl_asn1.c | 75 ++++++++++++++++++++++++++++++++++++++ tests/api/test_ossl_asn1.h | 2 + wolfssl/openssl/asn1.h | 1 - wolfssl/openssl/ssl.h | 37 ++++++++++++++++++- wolfssl/ssl.h | 1 + 6 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 8eb647f6744..fc2b9bc1740 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -1094,6 +1094,26 @@ const unsigned char* wolfSSL_ASN1_INTEGER_get0_data(const WOLFSSL_ASN1_INTEGER* return ai->data; } +/* Get the type of the INTEGER, as ASN1_STRING_type() on an ASN1_INTEGER in + * OpenSSL. The sign is in the negative flag, as for the other INTEGER APIs. + * + * @param [in] ai ASN.1 INTEGER object. + * @return WOLFSSL_V_ASN1_NEG_INTEGER when negative. + * @return WOLFSSL_V_ASN1_INTEGER when not negative. + * @return 0 when ai is NULL. + */ +int wolfSSL_ASN1_INTEGER_get_type(const WOLFSSL_ASN1_INTEGER* ai) +{ + int type = 0; + + if (ai != NULL) { + type = ai->negative ? WOLFSSL_V_ASN1_NEG_INTEGER : + WOLFSSL_V_ASN1_INTEGER; + } + + return type; +} + #if defined(OPENSSL_EXTRA) /* Reset the data of ASN.1 INTEGER object back to empty fixed array. * diff --git a/tests/api/test_ossl_asn1.c b/tests/api/test_ossl_asn1.c index 8b731e8db07..6fa7c9e10cd 100644 --- a/tests/api/test_ossl_asn1.c +++ b/tests/api/test_ossl_asn1.c @@ -441,6 +441,81 @@ int test_wolfSSL_ASN1_INTEGER_get_set(void) return EXPECT_RESULT(); } +int test_wolfSSL_ASN1_INTEGER_STRING(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_ASN) + static const unsigned char p128[] = { 0x02, 0x02, 0x00, 0x80 }; + static const unsigned char m129[] = { 0x02, 0x02, 0xff, 0x7f }; + static const unsigned char v100[] = { 0x01, 0x00 }; + ASN1_INTEGER* a = NULL; + const ASN1_INTEGER* ca = NULL; + BIGNUM* bn = NULL; + const unsigned char* p = NULL; + ASN1_INTEGER* dup = NULL; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(HAVE_ECC) + static const unsigned char serial[] = { + 0xe3, 0x81, 0x4b, 0x48, 0xa5, 0x70, 0x61, 0x70 + }; + X509* x509 = NULL; +#endif + + ExpectIntEQ(ASN1_STRING_length(ca), 0); + ExpectNull(ASN1_STRING_get0_data(ca)); + ExpectIntEQ(ASN1_STRING_type(ca), 0); + + ExpectNotNull(a = ASN1_INTEGER_new()); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); + ExpectIntEQ(ASN1_INTEGER_set(a, 0x80), 1); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); + ExpectIntEQ(ASN1_INTEGER_set(a, -128), 1); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); + ExpectIntEQ(ASN1_INTEGER_set(a, 0), 1); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); + ExpectIntEQ(ASN1_INTEGER_set(a, 256), 1); + ca = a; + ExpectIntEQ(ASN1_STRING_length(ca), 2); + ExpectBufEQ(ASN1_STRING_get0_data(ca), v100, 2); + ExpectIntEQ(ASN1_STRING_type(ca), V_ASN1_INTEGER); + ASN1_INTEGER_free(a); + a = NULL; + + ExpectIntGT(BN_hex2bn(&bn, "-80"), 0); + ExpectNotNull(a = BN_to_ASN1_INTEGER(bn, NULL)); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); + ASN1_INTEGER_free(a); + a = NULL; + BN_free(bn); + + p = p128; + ExpectNotNull(a = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(p128))); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); + ASN1_INTEGER_free(a); + a = NULL; + p = m129; + ExpectNotNull(a = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(m129))); + ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); + ExpectNotNull(dup = wolfSSL_ASN1_INTEGER_dup(a)); + ExpectIntEQ(ASN1_STRING_type(dup), V_ASN1_NEG_INTEGER); + ASN1_INTEGER_free(dup); + ASN1_INTEGER_free(a); + a = NULL; + +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(HAVE_ECC) + ExpectNotNull(x509 = X509_load_certificate_file("./certs/test/digsigku.pem", + SSL_FILETYPE_PEM)); + ExpectIntEQ(ASN1_STRING_type(X509_get_serialNumber(x509)), + V_ASN1_INTEGER); + ExpectIntEQ(ASN1_STRING_length(X509_get_serialNumber(x509)), + (int)sizeof(serial)); + ExpectBufEQ(ASN1_STRING_get0_data(X509_get_serialNumber(x509)), serial, + sizeof(serial)); + X509_free(x509); +#endif +#endif + return EXPECT_RESULT(); +} + #if defined(OPENSSL_EXTRA) typedef struct ASN1IntTestVector { const byte* der; diff --git a/tests/api/test_ossl_asn1.h b/tests/api/test_ossl_asn1.h index 7ae66eb0672..6c96929b93e 100644 --- a/tests/api/test_ossl_asn1.h +++ b/tests/api/test_ossl_asn1.h @@ -29,6 +29,7 @@ int test_wolfSSL_ASN1_INTEGER(void); int test_wolfSSL_ASN1_INTEGER_cmp(void); int test_wolfSSL_ASN1_INTEGER_BN(void); int test_wolfSSL_ASN1_INTEGER_get_set(void); +int test_wolfSSL_ASN1_INTEGER_STRING(void); int test_wolfSSL_d2i_ASN1_INTEGER(void); int test_wolfSSL_a2i_ASN1_INTEGER(void); int test_wolfSSL_i2c_ASN1_INTEGER(void); @@ -68,6 +69,7 @@ int test_ASN1_strings(void); TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_ASN1_INTEGER_cmp), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_ASN1_INTEGER_BN), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_ASN1_INTEGER_get_set), \ + TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_ASN1_INTEGER_STRING), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_d2i_ASN1_INTEGER), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_a2i_ASN1_INTEGER), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_i2c_ASN1_INTEGER) diff --git a/wolfssl/openssl/asn1.h b/wolfssl/openssl/asn1.h index 17746ae4289..0f18a153a64 100644 --- a/wolfssl/openssl/asn1.h +++ b/wolfssl/openssl/asn1.h @@ -30,7 +30,6 @@ #define ASN1_STRING_new wolfSSL_ASN1_STRING_new #define ASN1_STRING_type_new wolfSSL_ASN1_STRING_type_new -#define ASN1_STRING_type wolfSSL_ASN1_STRING_type #define ASN1_STRING_set wolfSSL_ASN1_STRING_set #define ASN1_OCTET_STRING_set wolfSSL_ASN1_STRING_set #define ASN1_STRING_free wolfSSL_ASN1_STRING_free diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index fa669e3c618..2eae85b7253 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -1074,8 +1074,8 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define ASN1_OCTET_STRING_cmp wolfSSL_ASN1_STRING_cmp #define ASN1_STRING_data wolfSSL_ASN1_STRING_data /* In OpenSSL, ASN1_INTEGER is a typedef alias of ASN1_STRING (same struct), - * so ASN1_STRING_length/get0_data work on ASN1_INTEGER* as well. - * In wolfSSL they are distinct structs, so dispatch by type using _Generic. */ + * so ASN1_STRING_length/get0_data/type work on ASN1_INTEGER* as well. + * In wolfSSL they are distinct structs, so dispatch by type. */ #if !defined(__cplusplus) && defined(__STDC_VERSION__) && \ __STDC_VERSION__ >= 201112L #define ASN1_STRING_length(x) \ @@ -1090,9 +1090,42 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ const WOLFSSL_ASN1_INTEGER*: wolfSSL_ASN1_INTEGER_get0_data, \ default: wolfSSL_ASN1_STRING_get0_data \ )(x) +#define ASN1_STRING_type(x) \ + _Generic((x), \ + WOLFSSL_ASN1_INTEGER*: wolfSSL_ASN1_INTEGER_get_type, \ + const WOLFSSL_ASN1_INTEGER*: wolfSSL_ASN1_INTEGER_get_type, \ + default: wolfSSL_ASN1_STRING_type \ + )(x) +#elif !defined(__cplusplus) && (defined(__GNUC__) || defined(__clang__)) +/* Before C11. The call not taken gets a NULL of its parameter type, so x is + * still type checked and evaluated once. As with _Generic, these are + * function-like macros: for a function pointer use + * wolfSSL_ASN1_STRING_length or wolfSSL_ASN1_INTEGER_get_length. */ +#define WOLFSSL_ASN1_IS_INTEGER(x) \ + (__builtin_types_compatible_p(__typeof__(x), WOLFSSL_ASN1_INTEGER*) || \ + __builtin_types_compatible_p(__typeof__(x), const WOLFSSL_ASN1_INTEGER*)) +#define WOLFSSL_ASN1_INTEGER_ARG(x) \ + __builtin_choose_expr(WOLFSSL_ASN1_IS_INTEGER(x), (x), \ + (const WOLFSSL_ASN1_INTEGER*)0) +#define WOLFSSL_ASN1_STRING_ARG(x) \ + __builtin_choose_expr(WOLFSSL_ASN1_IS_INTEGER(x), \ + (const WOLFSSL_ASN1_STRING*)0, (x)) +#define ASN1_STRING_length(x) \ + __builtin_choose_expr(WOLFSSL_ASN1_IS_INTEGER(x), \ + wolfSSL_ASN1_INTEGER_get_length(WOLFSSL_ASN1_INTEGER_ARG(x)), \ + wolfSSL_ASN1_STRING_length(WOLFSSL_ASN1_STRING_ARG(x))) +#define ASN1_STRING_get0_data(x) \ + __builtin_choose_expr(WOLFSSL_ASN1_IS_INTEGER(x), \ + wolfSSL_ASN1_INTEGER_get0_data(WOLFSSL_ASN1_INTEGER_ARG(x)), \ + wolfSSL_ASN1_STRING_get0_data(WOLFSSL_ASN1_STRING_ARG(x))) +#define ASN1_STRING_type(x) \ + __builtin_choose_expr(WOLFSSL_ASN1_IS_INTEGER(x), \ + wolfSSL_ASN1_INTEGER_get_type(WOLFSSL_ASN1_INTEGER_ARG(x)), \ + wolfSSL_ASN1_STRING_type(WOLFSSL_ASN1_STRING_ARG(x))) #else #define ASN1_STRING_get0_data wolfSSL_ASN1_STRING_get0_data #define ASN1_STRING_length wolfSSL_ASN1_STRING_length +#define ASN1_STRING_type wolfSSL_ASN1_STRING_type #endif #define ASN1_STRING_to_UTF8 wolfSSL_ASN1_STRING_to_UTF8 #define ASN1_UNIVERSALSTRING_to_string wolfSSL_ASN1_UNIVERSALSTRING_to_string diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index eff03702b5b..be1dd24f64d 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2571,6 +2571,7 @@ WOLFSSL_API int wolfSSL_ASN1_STRING_length(const WOLFSSL_ASN1_STRING* asn); WOLFSSL_API int wolfSSL_ASN1_INTEGER_get_length(const WOLFSSL_ASN1_INTEGER* ai); WOLFSSL_API const unsigned char* wolfSSL_ASN1_INTEGER_get0_data( const WOLFSSL_ASN1_INTEGER* ai); +WOLFSSL_API int wolfSSL_ASN1_INTEGER_get_type(const WOLFSSL_ASN1_INTEGER* ai); WOLFSSL_API int wolfSSL_ASN1_STRING_copy(WOLFSSL_ASN1_STRING* dst, const WOLFSSL_ASN1_STRING* src); /* NOTE: a single WOLFSSL_X509_STORE must not be shared across threads that From bcb981a7efd318342f165ca6a465c1df8bac77af Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:20:36 +0000 Subject: [PATCH 10/31] Return the ASN1_INTEGER magnitude as OpenSSL does ASN1_STRING_length and ASN1_STRING_get0_data on an ASN1_INTEGER made by ASN1_INTEGER_set, BN_to_ASN1_INTEGER or d2i_ASN1_INTEGER returned the DER content, with the zero octet before a high bit. OpenSSL returns the magnitude. Return it from wolfSSL_ASN1_INTEGER_get_length and get0_data, which those macros call on an ASN1_INTEGER, whatever made the INTEGER, and print it with i2a_ASN1_INTEGER. X509_get_serialNumber encoded a serial with the high bit set as a negative INTEGER. Add the zero octet, and set the type to V_ASN1_INTEGER as the other INTEGERs made by the library have it. Serials are taken as positive, as RFC 5280 requires: a negative one reads as positive. test_wolfSSL_ASN1_INTEGER_STRING checks the length and data of each INTEGER it makes, the type member, and the DER and i2a output of the serial. --- src/ssl_asn1.c | 95 ++++++++++++++++++++++---------------- src/x509.c | 17 +++++-- tests/api/test_ossl_asn1.c | 41 ++++++++++++++++ 3 files changed, 108 insertions(+), 45 deletions(-) diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index fc2b9bc1740..7844a5202b6 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -1040,58 +1040,68 @@ void wolfSSL_ASN1_INTEGER_free(WOLFSSL_ASN1_INTEGER* in) XFREE(in, NULL, DYNAMIC_TYPE_OPENSSL); } -/* Get the length of the raw integer value bytes, stripping the DER tag/length - * header if present. Required for OpenSSL compatibility where ASN1_INTEGER is - * typedef'd to ASN1_STRING and callers use ASN1_STRING_length() on integers. +/* Get the value of the INTEGER as OpenSSL holds it: the magnitude without the + * DER header and leading zero octets. Data without a DER header is the value + * (WOLFSSL_QT / WOLFSSL_HAPROXY format). + * + * @param [in] ai ASN.1 INTEGER object. + * @param [out] len Length of the value in bytes. + * @return Pointer to the value on success. + * @return NULL when ai is NULL or has no data. + */ +static const unsigned char* wolfssl_asn1_integer_value( + const WOLFSSL_ASN1_INTEGER* ai, int* len) +{ + const unsigned char* data = NULL; + word32 idx = 1; + int derLen = 0; + + *len = 0; + if ((ai != NULL) && (ai->data != NULL) && (ai->length > 0)) { + data = ai->data; + *len = ai->length; + if ((data[0] == ASN_INTEGER) && + (GetLength(data, &idx, &derLen, (word32)ai->length) >= 0) && + (idx + (word32)derLen == (word32)ai->length)) { + data += idx; + *len = derLen; + } + while ((*len > 1) && (data[0] == 0x00)) { + data++; + (*len)--; + } + } + + return data; +} + +/* Get the length of the value of the INTEGER, as ASN1_STRING_length() on an + * ASN1_INTEGER in OpenSSL. * * @param [in] ai ASN.1 INTEGER object. - * @return Length of the raw integer value on success. - * @return 0 when ai is NULL or data is invalid. + * @return Length of the value on success. + * @return 0 when ai is NULL or has no data. */ int wolfSSL_ASN1_INTEGER_get_length(const WOLFSSL_ASN1_INTEGER* ai) { - if (ai == NULL || ai->data == NULL || ai->length <= 0) { - return 0; - } - if (ai->data[0] == ASN_INTEGER) { - word32 idx = 1; - int len = 0; - if (GetLength(ai->data, &idx, &len, (word32)ai->length) >= 0 && - idx + (word32)len == (word32)ai->length) { - return len; - } - } - /* WOLFSSL_QT / WOLFSSL_HAPROXY format: raw bytes without DER header, - * or data that coincidentally starts with 0x02 but whose header+value - * boundaries do not span exactly ai->length. */ - return ai->length; + int len; + + (void)wolfssl_asn1_integer_value(ai, &len); + return len; } -/* Get a pointer to the raw integer value bytes, skipping the DER tag/length - * header if present. Required for OpenSSL compatibility where ASN1_INTEGER is - * typedef'd to ASN1_STRING and callers use ASN1_STRING_get0_data() on integers. +/* Get the value of the INTEGER, as ASN1_STRING_get0_data() on an ASN1_INTEGER + * in OpenSSL: the magnitude, big-endian. The sign is in the type. * * @param [in] ai ASN.1 INTEGER object. - * @return Pointer to the raw integer value bytes on success. - * @return NULL when ai is NULL or data is invalid. + * @return Pointer to the value on success. + * @return NULL when ai is NULL or has no data. */ const unsigned char* wolfSSL_ASN1_INTEGER_get0_data(const WOLFSSL_ASN1_INTEGER* ai) { - if (ai == NULL || ai->data == NULL || ai->length <= 0) { - return NULL; - } - if (ai->data[0] == ASN_INTEGER) { - word32 idx = 1; - int len = 0; - if (GetLength(ai->data, &idx, &len, (word32)ai->length) >= 0 && - idx + (word32)len == (word32)ai->length) { - return ai->data + idx; - } - } - /* WOLFSSL_QT / WOLFSSL_HAPROXY format: raw bytes without DER header, - * or data that coincidentally starts with 0x02 but whose header+value - * boundaries do not span exactly ai->length. */ - return ai->data; + int len; + + return wolfssl_asn1_integer_value(ai, &len); } /* Get the type of the INTEGER, as ASN1_STRING_type() on an ASN1_INTEGER in @@ -1742,6 +1752,11 @@ int wolfSSL_i2a_ASN1_INTEGER(WOLFSSL_BIO *bp, const WOLFSSL_ASN1_INTEGER *a) err = 1; } } + /* Write the magnitude, without the DER zero octet, as OpenSSL. */ + while ((!err) && (len > 1) && (a->data[idx] == 0x00)) { + idx++; + len--; + } /* Keep encoding and writing while no error and bytes in data. */ while ((!err) && (idx < (word32)a->length)) { diff --git a/src/x509.c b/src/x509.c index b80cb0f99e9..9a90b7d5fb0 100644 --- a/src/x509.c +++ b/src/x509.c @@ -12164,6 +12164,7 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509) { WOLFSSL_ASN1_INTEGER* a; int i = 0; + int pad; WOLFSSL_ENTER("wolfSSL_X509_get_serialNumber"); @@ -12184,16 +12185,19 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509) if (a == NULL) return NULL; + /* Serials are positive: a high bit needs a zero octet in DER. */ + pad = ((x509->serialSz > 0) && (x509->serial[0] & 0x80)) ? 1 : 0; + /* Make sure there is space for the data, ASN.1 type and length. */ - if (x509->serialSz > (WOLFSSL_ASN1_INTEGER_MAX - 2)) { + if (x509->serialSz + pad > (WOLFSSL_ASN1_INTEGER_MAX - 2)) { /* dynamically create data buffer, +2 for type and length */ - a->data = (unsigned char*)XMALLOC(x509->serialSz + 2, NULL, + a->data = (unsigned char*)XMALLOC(x509->serialSz + pad + 2, NULL, DYNAMIC_TYPE_OPENSSL); if (a->data == NULL) { wolfSSL_ASN1_INTEGER_free(a); return NULL; } - a->dataMax = (unsigned int)x509->serialSz + 2; + a->dataMax = (unsigned int)(x509->serialSz + pad + 2); a->isDynamic = 1; } else { @@ -12207,10 +12211,13 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509) a->length = x509->serialSz; #else a->data[i++] = ASN_INTEGER; - i += SetLength(x509->serialSz, a->data + i); + i += SetLength(x509->serialSz + pad, a->data + i); + if (pad) + a->data[i++] = 0x00; XMEMCPY(&a->data[i], x509->serial, x509->serialSz); - a->length = x509->serialSz + 2; + a->length = i + x509->serialSz; #endif + a->type = WOLFSSL_V_ASN1_INTEGER; x509->serialNumber = a; diff --git a/tests/api/test_ossl_asn1.c b/tests/api/test_ossl_asn1.c index 6fa7c9e10cd..b9479e35a9d 100644 --- a/tests/api/test_ossl_asn1.c +++ b/tests/api/test_ossl_asn1.c @@ -441,12 +441,17 @@ int test_wolfSSL_ASN1_INTEGER_get_set(void) return EXPECT_RESULT(); } +/* ASN1_STRING_length/get0_data on an ASN1_INTEGER give the magnitude, as in + * OpenSSL. */ int test_wolfSSL_ASN1_INTEGER_STRING(void) { EXPECT_DECLS; #if defined(OPENSSL_EXTRA) && !defined(NO_ASN) static const unsigned char p128[] = { 0x02, 0x02, 0x00, 0x80 }; static const unsigned char m129[] = { 0x02, 0x02, 0xff, 0x7f }; + static const unsigned char v0[] = { 0x00 }; + static const unsigned char v80[] = { 0x80 }; + static const unsigned char v81[] = { 0x81 }; static const unsigned char v100[] = { 0x01, 0x00 }; ASN1_INTEGER* a = NULL; const ASN1_INTEGER* ca = NULL; @@ -457,7 +462,15 @@ int test_wolfSSL_ASN1_INTEGER_STRING(void) static const unsigned char serial[] = { 0xe3, 0x81, 0x4b, 0x48, 0xa5, 0x70, 0x61, 0x70 }; + static const unsigned char serialDer[] = { + 0x02, 0x09, 0x00, 0xe3, 0x81, 0x4b, 0x48, 0xa5, 0x70, 0x61, 0x70 + }; + unsigned char* der = NULL; X509* x509 = NULL; +#ifndef NO_BIO + BIO* bio = NULL; + char* str = NULL; +#endif #endif ExpectIntEQ(ASN1_STRING_length(ca), 0); @@ -467,10 +480,17 @@ int test_wolfSSL_ASN1_INTEGER_STRING(void) ExpectNotNull(a = ASN1_INTEGER_new()); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); ExpectIntEQ(ASN1_INTEGER_set(a, 0x80), 1); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v80, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); ExpectIntEQ(ASN1_INTEGER_set(a, -128), 1); + ExpectIntEQ(a->type, V_ASN1_NEG_INTEGER); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v80, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); ExpectIntEQ(ASN1_INTEGER_set(a, 0), 1); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v0, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); ExpectIntEQ(ASN1_INTEGER_set(a, 256), 1); ca = a; @@ -482,6 +502,9 @@ int test_wolfSSL_ASN1_INTEGER_STRING(void) ExpectIntGT(BN_hex2bn(&bn, "-80"), 0); ExpectNotNull(a = BN_to_ASN1_INTEGER(bn, NULL)); + ExpectIntEQ(a->type, V_ASN1_NEG_INTEGER); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v80, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); ASN1_INTEGER_free(a); a = NULL; @@ -489,11 +512,16 @@ int test_wolfSSL_ASN1_INTEGER_STRING(void) p = p128; ExpectNotNull(a = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(p128))); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v80, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_INTEGER); ASN1_INTEGER_free(a); a = NULL; p = m129; ExpectNotNull(a = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(m129))); + ExpectIntEQ(a->type, V_ASN1_NEG_INTEGER); + ExpectIntEQ(ASN1_STRING_length(a), 1); + ExpectBufEQ(ASN1_STRING_get0_data(a), v81, 1); ExpectIntEQ(ASN1_STRING_type(a), V_ASN1_NEG_INTEGER); ExpectNotNull(dup = wolfSSL_ASN1_INTEGER_dup(a)); ExpectIntEQ(ASN1_STRING_type(dup), V_ASN1_NEG_INTEGER); @@ -502,14 +530,27 @@ int test_wolfSSL_ASN1_INTEGER_STRING(void) a = NULL; #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && defined(HAVE_ECC) + /* The DER serial has a leading zero octet. */ ExpectNotNull(x509 = X509_load_certificate_file("./certs/test/digsigku.pem", SSL_FILETYPE_PEM)); + ExpectIntEQ(X509_get_serialNumber(x509)->type, V_ASN1_INTEGER); ExpectIntEQ(ASN1_STRING_type(X509_get_serialNumber(x509)), V_ASN1_INTEGER); ExpectIntEQ(ASN1_STRING_length(X509_get_serialNumber(x509)), (int)sizeof(serial)); ExpectBufEQ(ASN1_STRING_get0_data(X509_get_serialNumber(x509)), serial, sizeof(serial)); + ExpectIntEQ(i2d_ASN1_INTEGER(X509_get_serialNumber(x509), &der), + (int)sizeof(serialDer)); + ExpectBufEQ(der, serialDer, sizeof(serialDer)); + XFREE(der, NULL, DYNAMIC_TYPE_ASN1); +#ifndef NO_BIO + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectIntEQ(i2a_ASN1_INTEGER(bio, X509_get_serialNumber(x509)), 16); + ExpectIntEQ(BIO_get_mem_data(bio, &str), 16); + ExpectBufEQ(str, "E3814B48A5706170", 16); + BIO_free(bio); +#endif X509_free(x509); #endif #endif From 4315edb5d0b9bb4daeae30449a76509337ad204d Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:20:41 +0000 Subject: [PATCH 11/31] Implement X509_CRL_verify and fix CRL serials and sk_X509_CRL_free X509_CRL_verify was a stub that returned 0, so applications that drop CRLs that do not verify, as xmlsec does for CRLs in a signed document, dropped every CRL and accepted the certificates they revoke. Check the CRL signature with the public key. As in OpenSSL, return 1 when it verifies, 0 when it does not, and -1 when it cannot be checked: a key of the wrong type, an algorithm that is not compiled in, or a CRL with nothing signed. The check uses the to-be-signed bytes and signature kept for a CRL that was not verified on load, as with d2i and PEM. Key handling is shared with X509_verify. As in OpenSSL, X509_verify and X509_CRL_verify also take a private key and verify with its public part. An RSA or EC private key gave 0 for a valid signature, as the key DER kept for it is the private key. When that public key cannot be encoded, as for DSA, -1 is returned. An Ed448 key is taken as an Ed25519 key is, its public key made from the private key when it has none; it gave -1. wolfSSL makes no EVP_PKEY of type RSA-PSS (it reads neither such a key nor the key of such a certificate), so that type stays -1. X509_CRL_get_REVOKED built entry serials from the content bytes, while X509_get_serialNumber holds the DER encoding unless WOLFSSL_QT or WOLFSSL_HAPROXY is defined. ASN1_INTEGER_to_BN and ASN1_INTEGER_get failed on entry serials. The INTEGER functions had to guess the form: content bytes that read as a DER INTEGER, such as 02 01 05, were taken for one, so ASN1_INTEGER_get read serial 0x020105 as 5, and a revoked certificate with that serial did not match its CRL entry. Store the DER encoding of entry serials in every build, with the zero octet before a high bit as X509_get_serialNumber has it, so they read the same in all of them. X509_CRL_add_revoked took the bytes of the INTEGER, so the serial of X509_get_serialNumber went in with its DER header and the cert was not found revoked. Take the value of an INTEGER made by the library, the magnitude, as the serials of parsed CRLs and certs are held. The bytes of one built by hand are still taken as they are. sk_X509_CRL_free also freed the CRLs on the stack. OpenSSL frees the stack only, and callers that keep their own references then freed the CRLs twice. Use sk_X509_CRL_pop_free with X509_CRL_free to free both. --- src/crl.c | 7 + src/x509.c | 245 ++++++++++++++++--- tests/api.c | 13 +- tests/api/test_ossl_x509_crypto.c | 376 ++++++++++++++++++++++++++++++ tests/api/test_ossl_x509_crypto.h | 10 + 5 files changed, 610 insertions(+), 41 deletions(-) diff --git a/src/crl.c b/src/crl.c index 1a62f7f521e..d5350ab608c 100644 --- a/src/crl.c +++ b/src/crl.c @@ -2653,6 +2653,13 @@ int wolfSSL_X509_CRL_add_revoked(WOLFSSL_X509_CRL* crl, int i; int allZero = 1; + /* An INTEGER made by the library holds the DER encoding. One built + * by hand holds the content bytes. */ + if (rev->serialNumber->type == WOLFSSL_V_ASN1_INTEGER) { + serial = wolfSSL_ASN1_INTEGER_get0_data(rev->serialNumber); + serialSz = wolfSSL_ASN1_INTEGER_get_length(rev->serialNumber); + } + if (serial == NULL || serialSz <= 0) { return BAD_FUNC_ARG; } diff --git a/src/x509.c b/src/x509.c index 9a90b7d5fb0..3a2e2f73e03 100644 --- a/src/x509.c +++ b/src/x509.c @@ -5249,9 +5249,11 @@ void wolfSSL_sk_X509_CRL_pop_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk, wolfSSL_sk_pop_free(sk, (wolfSSL_sk_freefunc)f); } +/* Free the stack only, not the CRLs, as OpenSSL does. */ void wolfSSL_sk_X509_CRL_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk) { - wolfSSL_sk_X509_CRL_pop_free(sk, NULL); + WOLFSSL_ENTER("wolfSSL_sk_X509_CRL_free"); + wolfSSL_sk_free(sk); } /* return number of elements on success 0 on fail */ @@ -9843,34 +9845,62 @@ WOLFSSL_X509* wolfSSL_d2i_X509_REQ_bio(WOLFSSL_BIO* bio, WOLFSSL_X509** x509) #endif /* OPENSSL_EXTRA */ #ifdef OPENSSL_EXTRA -/* Use the public key to verify the signature. Note: this only verifies - * the certificate signature. - * returns WOLFSSL_SUCCESS on successful signature verification */ -static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, - int req) +/* Public key to verify a signature with. */ +typedef struct X509VerifyKey { + const byte* pubKey; + int pubKeySz; + int type; + byte* pubDer; /* public key of a private key, freed by caller */ +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + byte edPubKey[ED25519_PUB_KEY_SIZE]; +#endif +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + byte ed448PubKey[ED448_PUB_KEY_SIZE]; +#endif +} X509VerifyKey; + +/* Returns 1 when pkey is an RSA, EC or DSA key with its private part, whose + * cached DER is then the private key, otherwise 0. */ +static int X509PkeyIsPrivate(WOLFSSL_EVP_PKEY* pkey) +{ +#ifndef NO_RSA + if (pkey->type == WC_EVP_PKEY_RSA) { + return pkey->rsa != NULL && pkey->rsa->internal != NULL && + ((RsaKey*)pkey->rsa->internal)->type == RSA_PRIVATE; + } +#endif +#ifdef HAVE_ECC + if (pkey->type == WC_EVP_PKEY_EC) { + return pkey->ecc != NULL && pkey->ecc->internal != NULL && + (((ecc_key*)pkey->ecc->internal)->type == ECC_PRIVATEKEY || + ((ecc_key*)pkey->ecc->internal)->type == ECC_PRIVATEKEY_ONLY); + } +#endif +#ifndef NO_DSA + if (pkey->type == WC_EVP_PKEY_DSA) { + return pkey->dsa != NULL && pkey->dsa->internal != NULL && + ((DsaKey*)pkey->dsa->internal)->type == DSA_PRIVATE; + } +#endif + (void)pkey; + return 0; +} + +/* Get the public key and key type of pkey to verify a signature with. + * returns 0 on success */ +static int X509GetVerifyKey(WOLFSSL_EVP_PKEY* pkey, X509VerifyKey* key) { - int ret; - const byte* der; - int derSz = 0; int type; const byte* pubKey; int pubKeySz; #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) - byte edPubKey[ED25519_PUB_KEY_SIZE]; - word32 edPubKeySz = (word32)sizeof(edPubKey); + word32 edPubKeySz = (word32)sizeof(key->edPubKey); +#endif +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + word32 ed448PubKeySz = (word32)sizeof(key->ed448PubKey); #endif - (void)req; - - if (x509 == NULL || pkey == NULL) { - return WOLFSSL_FATAL_ERROR; - } - - der = wolfSSL_X509_get_der(x509, &derSz); - if (der == NULL) { - WOLFSSL_MSG("Error getting WOLFSSL_X509 DER"); - return WOLFSSL_FATAL_ERROR; - } + key->pubDer = NULL; /* Most key types verify against the cached public-key DER. */ pubKey = (const byte*)pkey->pkey.ptr; @@ -9904,30 +9934,102 @@ static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, * pkey->pkey.ptr holds a PKCS#8 private blob (or nothing), so * export the public key from the ed25519_key instead. */ if (pkey->ed25519 == NULL || - wc_ed25519_export_public(pkey->ed25519, edPubKey, + wc_ed25519_export_public(pkey->ed25519, key->edPubKey, &edPubKeySz) != 0) { WOLFSSL_MSG("Unable to export Ed25519 public key"); return WOLFSSL_FATAL_ERROR; } - pubKey = edPubKey; + pubKey = key->edPubKey; pubKeySz = (int)edPubKeySz; type = ED25519k; break; #endif + #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + case WC_EVP_PKEY_ED448: + /* As Ed25519: the raw public key of the ed448_key, made from + * the private key when it was decoded without one */ + if (pkey->ed448 == NULL || + (wc_ed448_export_public(pkey->ed448, key->ed448PubKey, + &ed448PubKeySz) != 0 && + wc_ed448_make_public(pkey->ed448, key->ed448PubKey, + (word32)sizeof(key->ed448PubKey)) != 0)) { + WOLFSSL_MSG("Unable to export Ed448 public key"); + return WOLFSSL_FATAL_ERROR; + } + pubKey = key->ed448PubKey; + pubKeySz = (int)sizeof(key->ed448PubKey); + type = ED448k; + break; + #endif default: WOLFSSL_MSG("Unknown pkey key type"); return WOLFSSL_FATAL_ERROR; } + /* As OpenSSL, a private key verifies with its public part. When that + * cannot be encoded, as for DSA, the signature cannot be checked. */ + if (X509PkeyIsPrivate(pkey)) { + byte* der; + + pubKeySz = wolfSSL_i2d_PUBKEY(pkey, NULL); + if (pubKeySz <= 0) { + WOLFSSL_MSG("Unable to get the public key of a private key"); + return WOLFSSL_FATAL_ERROR; + } + key->pubDer = (byte*)XMALLOC((size_t)pubKeySz, pkey->heap, + DYNAMIC_TYPE_PUBLIC_KEY); + der = key->pubDer; + if (der == NULL || wolfSSL_i2d_PUBKEY(pkey, &der) != pubKeySz) { + XFREE(key->pubDer, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); + key->pubDer = NULL; + return WOLFSSL_FATAL_ERROR; + } + pubKey = key->pubDer; + } + + key->pubKey = pubKey; + key->pubKeySz = pubKeySz; + key->type = type; + return 0; +} + +/* Use the public key to verify the signature. Note: this only verifies + * the certificate signature. + * returns WOLFSSL_SUCCESS on successful signature verification */ +static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, + int req) +{ + int ret; + const byte* der; + int derSz = 0; + X509VerifyKey key; + + (void)req; + + if (x509 == NULL || pkey == NULL) { + return WOLFSSL_FATAL_ERROR; + } + + der = wolfSSL_X509_get_der(x509, &derSz); + if (der == NULL) { + WOLFSSL_MSG("Error getting WOLFSSL_X509 DER"); + return WOLFSSL_FATAL_ERROR; + } + + if (X509GetVerifyKey(pkey, &key) != 0) { + return WOLFSSL_FATAL_ERROR; + } + #ifdef WOLFSSL_CERT_REQ if (req) ret = CheckCSRSignaturePubKey(der, (word32)derSz, x509->heap, - (unsigned char*)pubKey, pubKeySz, type); + (unsigned char*)key.pubKey, key.pubKeySz, key.type); else #endif ret = CheckCertSignaturePubKey(der, (word32)derSz, x509->heap, - (unsigned char*)pubKey, pubKeySz, type); + (unsigned char*)key.pubKey, key.pubKeySz, key.type); + XFREE(key.pubDer, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); if (ret == 0) { return WOLFSSL_SUCCESS; } @@ -11190,15 +11292,73 @@ int wolfSSL_X509_CRL_set_nextUpdate(WOLFSSL_X509_CRL* crl, return WOLFSSL_FAILURE; } -#ifndef NO_WOLFSSL_STUB -int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* key) +/* Verify the signature of a CRL with pkey. + * Uses the TBS and signature kept for a CRL that was not verified when it was + * loaded, as with d2i and PEM. A CRL verified on load does not keep them. + * returns 1 when the signature verifies, 0 when it does not and -1 when it + * cannot be checked, as for a key of the wrong type */ +int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* pkey) { - (void)crl; - (void)key; - WOLFSSL_STUB("X509_CRL_verify"); - return 0; + int ret; + CRL_Entry* ent; + X509VerifyKey key; + const byte* sigParams = NULL; + word32 sigParamsSz = 0; + WC_DECLARE_VAR(sigCtx, SignatureCtx, 1, 0); + + WOLFSSL_ENTER("wolfSSL_X509_CRL_verify"); + + if (crl == NULL || pkey == NULL || X509GetVerifyKey(pkey, &key) != 0) { + return WOLFSSL_FATAL_ERROR; + } + + WC_ALLOC_VAR_EX(sigCtx, SignatureCtx, 1, crl->heap, DYNAMIC_TYPE_SIGNATURE, + { XFREE(key.pubDer, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); + return WOLFSSL_FATAL_ERROR; }); + + if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { + WC_FREE_VAR_EX(sigCtx, crl->heap, DYNAMIC_TYPE_SIGNATURE); + XFREE(key.pubDer, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); + return WOLFSSL_FATAL_ERROR; + } + ent = crl->crlList; + if (ent == NULL || ent->toBeSigned == NULL || ent->signature == NULL) { + WOLFSSL_MSG("No TBS and signature kept for the CRL"); + ret = WOLFSSL_FATAL_ERROR; + } + else { + #ifdef WC_RSA_PSS + sigParams = ent->sigParams; + sigParamsSz = ent->sigParamsSz; + #endif + InitSignatureCtx(sigCtx, crl->heap, INVALID_DEVID); + ret = ConfirmSignature(sigCtx, ent->toBeSigned, ent->tbsSz, + key.pubKey, (word32)key.pubKeySz, (word32)key.type, + ent->signature, ent->signatureSz, ent->signatureOID, + sigParams, sigParamsSz, NULL); + FreeSignatureCtx(sigCtx); + if (ret == 0) { + ret = WOLFSSL_SUCCESS; + } + else if (ret == WC_NO_ERR_TRACE(ASN_SIG_OID_E) || + ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E) || + ret == WC_NO_ERR_TRACE(HASH_TYPE_E) || + ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN) || + ret == WC_NO_ERR_TRACE(MEMORY_E)) { + WOLFSSL_MSG("CRL signature cannot be checked with this key"); + ret = WOLFSSL_FATAL_ERROR; + } + else { + WOLFSSL_MSG("CRL signature did not verify"); + ret = WOLFSSL_FAILURE; + } + } + wc_UnLockRwLock(&crl->crlLock); + + WC_FREE_VAR_EX(sigCtx, crl->heap, DYNAMIC_TYPE_SIGNATURE); + XFREE(key.pubDer, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); + return ret; } -#endif /* Encode CRL to DER format in memory. * @@ -12024,17 +12184,26 @@ static WOLFSSL_X509_REVOKED* RevokedCertToRevoked(RevokedCert* rc, int seq) return NULL; } if (rc->serialSz > 0 && rc->serialSz <= EXTERNAL_SERIAL_SIZE) { - serial->data = (unsigned char*)XMALLOC((size_t)rc->serialSz, NULL, - DYNAMIC_TYPE_OPENSSL); + /* Serials are positive: a high bit needs a zero octet. */ + int pad = (rc->serialNumber[0] & 0x80) ? 1 : 0; + + /* DER encoding: tag, length, content. */ + serial->data = (unsigned char*)XMALLOC( + (size_t)(rc->serialSz + 2 + pad), NULL, DYNAMIC_TYPE_OPENSSL); if (serial->data == NULL) { wolfSSL_ASN1_INTEGER_free(serial); XFREE(rev, NULL, DYNAMIC_TYPE_OPENSSL); return NULL; } - XMEMCPY(serial->data, rc->serialNumber, (size_t)rc->serialSz); - serial->length = rc->serialSz; - serial->dataMax = rc->serialSz; + serial->data[0] = ASN_INTEGER; + serial->data[1] = (byte)(rc->serialSz + pad); + serial->data[2] = 0x00; + XMEMCPY(serial->data + 2 + pad, rc->serialNumber, + (size_t)rc->serialSz); + serial->length = rc->serialSz + 2 + pad; + serial->dataMax = (unsigned int)serial->length; serial->isDynamic = 1; + serial->type = WOLFSSL_V_ASN1_INTEGER; } rev->serialNumber = serial; diff --git a/tests/api.c b/tests/api.c index 1cebde6e51a..e6950004a6b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -31352,7 +31352,6 @@ static int test_sk_X509_CRL_decode(void) ExpectNull(wolfSSL_sk_X509_REVOKED_value(NULL, 0)); } #ifndef NO_WOLFSSL_STUB - ExpectIntEQ(wolfSSL_X509_CRL_verify(NULL, NULL), 0); ExpectIntEQ(X509_OBJECT_set1_X509_CRL(NULL, NULL), 0); ExpectIntEQ(X509_OBJECT_set1_X509(NULL, NULL), 0); #endif @@ -31398,7 +31397,16 @@ static int test_sk_X509_CRL_decode(void) ExpectIntEQ(sk_X509_CRL_num(s), 0); ExpectIntEQ(sk_X509_CRL_push(s, crl), 1); + /* Frees the stack only. */ sk_X509_CRL_free(s); + s = NULL; + ExpectNotNull(X509_CRL_get_issuer(crl)); + ExpectNotNull(s = sk_X509_CRL_new()); + ExpectIntEQ(sk_X509_CRL_push(s, crl), 1); + if (EXPECT_FAIL()) { + X509_CRL_free(crl); + } + sk_X509_CRL_pop_free(s, X509_CRL_free); #endif return EXPECT_RESULT(); } @@ -31491,8 +31499,7 @@ static int test_wolfSSL_X509_CRL_get_REVOKED_after_update(void) ExpectIntEQ(wolfSSL_sk_X509_REVOKED_num(revokedSk), 1); ExpectNotNull(rev = wolfSSL_sk_X509_REVOKED_value(revokedSk, 0)); ExpectNotNull(serial = wolfSSL_X509_REVOKED_get0_serial_number(rev)); - ExpectIntEQ(serial->length, 1); - ExpectIntEQ(serial->data[0], 0x02); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_get(serial), 0x02); wolfSSL_CTX_free(ctx); diff --git a/tests/api/test_ossl_x509_crypto.c b/tests/api/test_ossl_x509_crypto.c index 230cfaf3938..5992938259e 100644 --- a/tests/api/test_ossl_x509_crypto.c +++ b/tests/api/test_ossl_x509_crypto.c @@ -30,6 +30,9 @@ #include #include +#ifdef OPENSSL_EXTRA + #include +#endif #include #include #include @@ -364,6 +367,379 @@ int test_wolfSSL_X509_verify_mldsa_privkey_pkey(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(NO_RSA) +static int test_X509_CRL_verify_file(const char* crlFile, const char* caFile, + int expected) +{ + EXPECT_DECLS; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + XFILE fp = XBADFILE; + + ExpectTrue((fp = XFOPEN(crlFile, "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(pkey = X509_get_pubkey(ca)); + ExpectIntEQ(X509_CRL_verify(crl, pkey), expected); + + EVP_PKEY_free(pkey); + X509_free(ca); + X509_CRL_free(crl); + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_X509_CRL_verify(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(NO_RSA) + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + byte der[4096]; + int derSz = 0; + XFILE fp = XBADFILE; + + ExpectIntEQ(test_X509_CRL_verify_file("./certs/crl/crl.pem", caCertFile, + 1), TEST_SUCCESS); + ExpectIntEQ(test_X509_CRL_verify_file("./certs/crl/crl.pem", svrCertFile, + 0), TEST_SUCCESS); +#ifdef HAVE_ECC + ExpectIntEQ(test_X509_CRL_verify_file("./certs/crl/caEccCrl.pem", + caEccCertFile, 1), TEST_SUCCESS); + /* Key type does not match the signature. */ + ExpectIntEQ(test_X509_CRL_verify_file("./certs/crl/caEccCrl.pem", + caCertFile, -1), TEST_SUCCESS); +#endif + + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(pkey = X509_get_pubkey(ca)); + ExpectIntEQ(X509_CRL_verify(NULL, pkey), -1); + + /* Changed signature. */ + ExpectTrue((fp = XFOPEN("./certs/crl/crl.der", "rb")) != XBADFILE); + ExpectIntGT(derSz = (int)XFREAD(der, 1, sizeof(der), fp), 0); + if (fp != XBADFILE) + XFCLOSE(fp); + if (derSz > 0) + der[derSz - 1] ^= 0x01; + ExpectNotNull(crl = wolfSSL_d2i_X509_CRL(NULL, der, derSz)); + ExpectIntEQ(X509_CRL_verify(crl, NULL), -1); + ExpectIntEQ(X509_CRL_verify(crl, pkey), 0); + X509_CRL_free(crl); + crl = NULL; + + /* Nothing signed yet. */ + ExpectNotNull(crl = X509_CRL_new()); + ExpectIntEQ(X509_CRL_verify(crl, pkey), -1); + X509_CRL_free(crl); + + EVP_PKEY_free(pkey); + X509_free(ca); +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_BIO) && \ + !defined(NO_CERTS) +static WOLFSSL_EVP_PKEY* test_x509_verify_privkey_load(const char* file) +{ + WOLFSSL_BIO* bio = wolfSSL_BIO_new_file(file, "rb"); + WOLFSSL_EVP_PKEY* pkey = NULL; + + if (bio != NULL) { + pkey = wolfSSL_PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + wolfSSL_BIO_free(bio); + } + return pkey; +} + +/* X509_verify() of certFile and, with crlFile, X509_CRL_verify() with the + * private key in keyFile. */ +static int test_x509_verify_privkey_file(const char* certFile, + const char* crlFile, const char* keyFile, int expected) +{ + EXPECT_DECLS; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; +#ifdef HAVE_CRL + WOLFSSL_X509_CRL* crl = NULL; + XFILE fp = XBADFILE; +#endif + + ExpectNotNull(pkey = test_x509_verify_privkey_load(keyFile)); + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(certFile, + WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_verify(x509, pkey), expected); +#ifdef HAVE_CRL + if (crlFile != NULL) { + ExpectTrue((fp = XFOPEN(crlFile, "rb")) != XBADFILE); + ExpectNotNull(crl = wolfSSL_PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectIntEQ(wolfSSL_X509_CRL_verify(crl, pkey), expected); + wolfSSL_X509_CRL_free(crl); + } +#else + (void)crlFile; +#endif + + wolfSSL_X509_free(x509); + wolfSSL_EVP_PKEY_free(pkey); + return EXPECT_RESULT(); +} +#endif + +/* As OpenSSL, a private key verifies with its public part. */ +int test_wolfSSL_X509_verify_privkey(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_BIO) && \ + !defined(NO_CERTS) +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \ + defined(HAVE_ED448_KEY_IMPORT) + WOLFSSL_X509* root = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_EVP_PKEY* pub = NULL; + unsigned char buf[256]; + const unsigned char* p = NULL; + int bufSz = (int)sizeof(buf); +#endif + +#ifndef NO_RSA + ExpectIntEQ(test_x509_verify_privkey_file(caCertFile, + "./certs/crl/crl.pem", "./certs/ca-key.pem", WOLFSSL_SUCCESS), + TEST_SUCCESS); + ExpectIntEQ(test_x509_verify_privkey_file(caCertFile, + "./certs/crl/crl.pem", "./certs/server-key.pem", WOLFSSL_FAILURE), + TEST_SUCCESS); +#endif +#ifdef HAVE_ECC + ExpectIntEQ(test_x509_verify_privkey_file(caEccCertFile, + "./certs/crl/caEccCrl.pem", "./certs/ca-ecc-key.pem", + WOLFSSL_SUCCESS), TEST_SUCCESS); + ExpectIntEQ(test_x509_verify_privkey_file(caEccCertFile, + "./certs/crl/caEccCrl.pem", "./certs/ecc-key.pem", WOLFSSL_FAILURE), + TEST_SUCCESS); +#endif +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \ + defined(HAVE_ED448_KEY_IMPORT) + ExpectIntEQ(test_x509_verify_privkey_file("./certs/ed448/ca-ed448.pem", + NULL, "./certs/ed448/root-ed448-priv.pem", WOLFSSL_SUCCESS), + TEST_SUCCESS); + ExpectIntEQ(test_x509_verify_privkey_file("./certs/ed448/ca-ed448.pem", + NULL, "./certs/ed448/ca-ed448-priv.pem", WOLFSSL_FAILURE), + TEST_SUCCESS); + /* And with the public key */ + ExpectNotNull(root = wolfSSL_X509_load_certificate_file( + "./certs/ed448/root-ed448.pem", WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file( + "./certs/ed448/ca-ed448.pem", WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_get_pubkey_buffer(root, buf, &bufSz), + WOLFSSL_SUCCESS); + p = buf; + ExpectNotNull(pub = wolfSSL_d2i_PUBKEY(NULL, &p, bufSz)); + ExpectIntEQ(wolfSSL_X509_verify(ca, pub), WOLFSSL_SUCCESS); + wolfSSL_EVP_PKEY_free(pub); + wolfSSL_X509_free(ca); + wolfSSL_X509_free(root); +#endif +#if !defined(NO_DSA) && !defined(NO_RSA) && \ + (defined(WOLFSSL_QT) || defined(OPENSSL_ALL)) + /* The public part of a DSA key cannot be encoded: not checked. A DSA PEM + * private key is read with WOLFSSL_QT or OPENSSL_ALL. */ + ExpectIntEQ(test_x509_verify_privkey_file(caCertFile, NULL, + "./certs/dsa2048.pem", WOLFSSL_FATAL_ERROR), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_X509_CRL_get_REVOKED_serial(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) + /* CRL that revokes serial 0x020105. These bytes also read as the DER + * encoding of 5. */ + static const unsigned char crlDer[] = { + 0x30, 0x81, 0xba, 0x30, 0x63, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x1a, 0x31, 0x18, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x43, 0x52, 0x4c, + 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x74, 0x65, 0x73, 0x74, + 0x17, 0x0d, 0x32, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, 0x35, 0x31, 0x32, 0x30, + 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x16, 0x30, 0x14, + 0x02, 0x03, 0x02, 0x01, 0x05, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, + 0x44, 0x02, 0x20, 0x7a, 0xfe, 0xd1, 0xa0, 0x33, 0x2a, 0xff, 0xad, 0x51, + 0x36, 0x78, 0x60, 0x63, 0x46, 0x52, 0x8e, 0x74, 0x2e, 0xf1, 0xe5, 0x72, + 0x5f, 0x0e, 0x81, 0xd9, 0x5c, 0xca, 0x2b, 0xbb, 0x32, 0xb6, 0x80, 0x02, + 0x20, 0x2e, 0xa2, 0x8b, 0x7a, 0x2c, 0x44, 0x3f, 0x2b, 0xee, 0xe4, 0x64, + 0x73, 0x04, 0xea, 0x83, 0x49, 0x62, 0x0c, 0x44, 0xa3, 0x63, 0xc2, 0xfd, + 0xbe, 0x01, 0x7c, 0x5f, 0x31, 0x72, 0x9a, 0xd7, 0x76, + }; +#ifdef WOLFSSL_CERT_GEN + /* Content of a serial built by hand, and its DER */ + static unsigned char highSerial[] = { 0x8a, 0x01 }; + static const unsigned char highDer[] = { 0x02, 0x03, 0x00, 0x8a, 0x01 }; + WOLFSSL_ASN1_INTEGER high; + WOLFSSL_X509_REVOKED highRev; + unsigned char* highOut = NULL; +#endif + WOLFSSL_X509_CRL* crl = NULL; + WOLF_STACK_OF(WOLFSSL_X509_REVOKED)* revs = NULL; + WOLFSSL_X509_REVOKED* rev = NULL; + const WOLFSSL_ASN1_INTEGER* serial = NULL; + WOLFSSL_ASN1_INTEGER* ai = NULL; + WOLFSSL_BIGNUM* bn = NULL; +#ifdef WOLFSSL_CERT_GEN + static const unsigned char p128[] = { 0x02, 0x02, 0x00, 0x80 }; + WOLFSSL_X509_REVOKED add; + unsigned char* der = NULL; +#endif + + ExpectNotNull(crl = wolfSSL_d2i_X509_CRL(NULL, crlDer, + (int)sizeof(crlDer))); + ExpectNotNull(revs = X509_CRL_get_REVOKED(crl)); + ExpectIntEQ(sk_X509_REVOKED_num(revs), 1); + ExpectNotNull(rev = sk_X509_REVOKED_value(revs, 0)); + ExpectNotNull(serial = X509_REVOKED_get0_serialNumber(rev)); + ExpectIntEQ(ASN1_INTEGER_get(serial), 0x020105); + ExpectNotNull(bn = ASN1_INTEGER_to_BN(serial, NULL)); + ExpectIntEQ(BN_get_word(bn), 0x020105); + ExpectNotNull(ai = ASN1_INTEGER_new()); + ExpectIntEQ(ASN1_INTEGER_set(ai, 0x020105), 1); + ExpectIntEQ(ASN1_INTEGER_cmp(serial, ai), 0); + ExpectIntEQ(ASN1_INTEGER_set(ai, 5), 1); + ExpectIntNE(ASN1_INTEGER_cmp(serial, ai), 0); +#ifdef WOLFSSL_CERT_GEN + /* An entry added back keeps its serial. */ + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, rev), WOLFSSL_SUCCESS); + ExpectNotNull(revs = X509_CRL_get_REVOKED(crl)); + ExpectIntEQ(sk_X509_REVOKED_num(revs), 2); + ExpectNotNull(rev = sk_X509_REVOKED_value(revs, 1)); + ExpectIntEQ(ASN1_INTEGER_get(X509_REVOKED_get0_serialNumber(rev)), + 0x020105); + /* A serial with the top bit set is encoded with a zero octet. */ + XMEMSET(&add, 0, sizeof(add)); + ExpectIntEQ(ASN1_INTEGER_set(ai, 0x80), 1); + add.serialNumber = ai; + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &add), WOLFSSL_SUCCESS); + ExpectNotNull(revs = X509_CRL_get_REVOKED(crl)); + ExpectIntEQ(sk_X509_REVOKED_num(revs), 3); + ExpectNotNull(rev = sk_X509_REVOKED_value(revs, 2)); + ExpectIntEQ(i2d_ASN1_INTEGER(X509_REVOKED_get0_serialNumber(rev), &der), + (int)sizeof(p128)); + ExpectBufEQ(der, p128, sizeof(p128)); + XFREE(der, NULL, DYNAMIC_TYPE_ASN1); +#endif + + BN_free(bn); + ASN1_INTEGER_free(ai); + X509_CRL_free(crl); +#ifdef WOLFSSL_CERT_GEN + /* An entry serial with the top bit set is encoded with a zero octet. */ + crl = NULL; + XMEMSET(&high, 0, sizeof(high)); + high.data = highSerial; + high.length = (int)sizeof(highSerial); + XMEMSET(&highRev, 0, sizeof(highRev)); + highRev.serialNumber = &high; + ExpectNotNull(crl = wolfSSL_d2i_X509_CRL(NULL, crlDer, + (int)sizeof(crlDer))); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &highRev), WOLFSSL_SUCCESS); + ExpectNotNull(revs = X509_CRL_get_REVOKED(crl)); + ExpectIntEQ(sk_X509_REVOKED_num(revs), 2); + ExpectNotNull(rev = sk_X509_REVOKED_value(revs, 1)); + ExpectIntEQ(i2d_ASN1_INTEGER(X509_REVOKED_get0_serialNumber(rev), + &highOut), (int)sizeof(highDer)); + ExpectBufEQ(highOut, highDer, sizeof(highDer)); + XFREE(highOut, NULL, DYNAMIC_TYPE_ASN1); + X509_CRL_free(crl); +#endif +#endif + return EXPECT_RESULT(); +} + +/* A cert whose serial has the top bit set is revoked by the entry that + * X509_CRL_add_revoked makes from X509_get_serialNumber. */ +int test_wolfSSL_X509_CRL_add_revoked_serial(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && \ + defined(WOLFSSL_CERT_GEN) && !defined(NO_FILESYSTEM) && \ + !defined(NO_BIO) && !defined(NO_CERTS) && !defined(NO_RSA) + /* 0x8a0102030405060708 */ + static const unsigned char serialDer[] = { + 0x02, 0x0a, 0x00, 0x8a, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + }; + const unsigned char* p = serialDer; + unsigned char* der = NULL; + int derSz = 0; + WOLFSSL_ASN1_INTEGER* ai = NULL; + WOLFSSL_EVP_PKEY* key = NULL; + WOLFSSL_X509* tmp = NULL; + WOLFSSL_X509* leaf = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509_STORE* store = NULL; + WOLFSSL_X509_STORE_CTX* ctx = NULL; + WOLFSSL_X509_REVOKED rev; + XFILE fp = XBADFILE; + + /* Issue the server cert again with the serial. */ + ExpectNotNull(ai = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(serialDer))); + ExpectNotNull(tmp = X509_load_certificate_file(svrCertFile, + SSL_FILETYPE_PEM)); + ExpectIntEQ(X509_set_serialNumber(tmp, ai), WOLFSSL_SUCCESS); + ExpectNotNull(key = test_x509_verify_privkey_load("./certs/ca-key.pem")); + ExpectIntGT(X509_sign(tmp, key, EVP_sha256()), 0); + ExpectIntGT(derSz = i2d_X509(tmp, &der), 0); + p = der; + ExpectNotNull(leaf = d2i_X509(NULL, &p, derSz)); + ExpectIntEQ(ASN1_INTEGER_cmp(X509_get_serialNumber(leaf), ai), 0); + + ExpectTrue((fp = XFOPEN("./certs/crl/crl.pem", "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + XMEMSET(&rev, 0, sizeof(rev)); + rev.serialNumber = X509_get_serialNumber(leaf); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &rev), WOLFSSL_SUCCESS); + + ExpectNotNull(ca = X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK), + WOLFSSL_SUCCESS); + ExpectIntEQ(X509_STORE_add_cert(store, ca), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_STORE_add_crl(store, crl), WOLFSSL_SUCCESS); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), WOLFSSL_SUCCESS); + ExpectIntNE(X509_verify_cert(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_REVOKED); + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + X509_free(ca); + X509_CRL_free(crl); + X509_free(leaf); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + X509_free(tmp); + EVP_PKEY_free(key); + ASN1_INTEGER_free(ai); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_sign(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_crypto.h b/tests/api/test_ossl_x509_crypto.h index 68ce053d6b0..0cb0b9b4427 100644 --- a/tests/api/test_ossl_x509_crypto.h +++ b/tests/api/test_ossl_x509_crypto.h @@ -29,6 +29,10 @@ int test_wolfSSL_X509_check_private_key_mldsa(void); int test_wolfSSL_X509_verify(void); int test_wolfSSL_X509_verify_mldsa_cross_level(void); int test_wolfSSL_X509_verify_mldsa_privkey_pkey(void); +int test_wolfSSL_X509_CRL_verify(void); +int test_wolfSSL_X509_verify_privkey(void); +int test_wolfSSL_X509_CRL_get_REVOKED_serial(void); +int test_wolfSSL_X509_CRL_add_revoked_serial(void); int test_wolfSSL_X509_sign(void); int test_wolfSSL_X509_sign2(void); int test_wolfSSL_make_cert(void); @@ -42,6 +46,12 @@ int test_wolfSSL_make_cert(void); test_wolfSSL_X509_verify_mldsa_cross_level), \ TEST_DECL_GROUP("ossl_x509_crypto", \ test_wolfSSL_X509_verify_mldsa_privkey_pkey), \ + TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_CRL_verify), \ + TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_verify_privkey), \ + TEST_DECL_GROUP("ossl_x509_crypto", \ + test_wolfSSL_X509_CRL_get_REVOKED_serial), \ + TEST_DECL_GROUP("ossl_x509_crypto", \ + test_wolfSSL_X509_CRL_add_revoked_serial), \ TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_sign), \ TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_sign2), \ TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_make_cert) From 92d110eda9f750e642fd7191818ab5ea795e6f7e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:20:41 +0000 Subject: [PATCH 12/31] Fix and add ASN1, OBJ and X509_NAME functions for xmlsec - ASN1_INTEGER_cmp compared the stored bytes: the DER encoding, or only the content in an INTEGER built by hand. A negative value is stored as the encoding of its magnitude, which BN_to_ASN1_INTEGER and d2i_ASN1_INTEGER can start with a zero byte that ASN1_INTEGER_set leaves out. So equal values compared unequal, as -128 from ASN1_INTEGER_set and from BN_to_ASN1_INTEGER. Compare the magnitudes, as ASN1_STRING_get0_data and ASN1_STRING_length give them. An empty INTEGER still does not equal 0, as in OpenSSL. - OBJ_nid2obj gives an object with only the tag and a zero length for a NID without an OID encoding here, as for name attribute types. OBJ_cmp compared those bytes, so any two such types compared equal, and commonName did not equal 2.5.4.3 made with OBJ_txt2obj. Compare the NIDs when either object has no encoding. - For a subjectKeyIdentifier extension X509V3_EXT_d2i copied extSubjKeyIdSz bytes from extSubjKeyId. That array holds a hash of the identifier when it is not KEYID_SIZE bytes long, so a short identifier came back as the start of the hash, and a longer one read past the end of the array. Copy the identifier from the extension instead. - X509_NAME_oneline left the caller's buffer untouched for an empty or NULL name, so the caller read whatever was there. It now writes an empty string first, as OpenSSL does. X509_NAME_hash returned 0 for an empty name. OpenSSL hashes the empty encoding, which gives 0xeea339da with SHA-1. Do the same, so lookups by hash find certificates with an empty subject or issuer. - Add X509_NAME_ENTRY_dup and sk_X509_NAME_ENTRY_pop_free. Applications copy the entries of a name to sort and compare them, as xmlsec does. X509_NAME_ENTRY_dup copies the object and the value, so the copy outlives the name it came from, as in OpenSSL. sk_X509_NAME_ENTRY_pop_free frees the stack and each entry with the given function. - Add B_ASN1_OCTET_STRING, the type mask bit of an OCTET STRING, 0x0200 as in OpenSSL. xmlsec passes it to X509_NAME_add_entry_by_txt for a name value given as '#' and hex, the raw encoding of the value. wolfSSL keeps the bytes as the entry value, as it does for any type that is not an MBSTRING_* type. OpenSSL 3.0 fails to add such an entry, so xmlsec '#hex' subject and issuer names find a cert only with wolfSSL. This is lookup only; the cert is still verified. --- src/ssl_asn1.c | 28 ++++++++-- src/x509.c | 83 ++++++++++++++++++++++++----- tests/api/test_ossl_asn1.c | 85 ++++++++++++++++++++++++++++++ tests/api/test_ossl_obj.c | 27 ++++++++++ tests/api/test_ossl_x509.c | 14 ++++- tests/api/test_ossl_x509_crypto.c | 4 ++ tests/api/test_ossl_x509_ext.c | 62 ++++++++++++++++++++++ tests/api/test_ossl_x509_ext.h | 2 + tests/api/test_ossl_x509_name.c | 86 ++++++++++++++++++++++++++++++- tests/api/test_ossl_x509_name.h | 5 ++ wolfssl/openssl/asn1.h | 2 + wolfssl/openssl/ssl.h | 2 + wolfssl/ssl.h | 10 ++++ 13 files changed, 391 insertions(+), 19 deletions(-) diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 7844a5202b6..ccb578c935f 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -1331,13 +1331,20 @@ int wolfSSL_ASN1_INTEGER_cmp(const WOLFSSL_ASN1_INTEGER* a, ret = 1; } else { + /* Compare the magnitudes from the accessors: data may hold the DER + * encoding or only the content. */ + const unsigned char* aData = wolfSSL_ASN1_INTEGER_get0_data(a); + const unsigned char* bData = wolfSSL_ASN1_INTEGER_get0_data(b); + int aLen = wolfSSL_ASN1_INTEGER_get_length(a); + int bLen = wolfSSL_ASN1_INTEGER_get_length(b); + /* Check for difference in length. */ - if (a->length != b->length) { - ret = a->length - b->length; + if (aLen != bLen) { + ret = aLen - bLen; } - else { + else if (aLen > 0) { /* Compare data given they are the same length. */ - ret = XMEMCMP(a->data, b->data, (size_t)a->length); + ret = XMEMCMP(aData, bData, (size_t)aLen); } /* Reverse comparison result when both negative. */ if (a->negative) { @@ -6343,6 +6350,19 @@ int wc_OBJ_sn2nid(const char *sn) WOLFSSL_ENTER("wolfSSL_OBJ_cmp"); if (a && b && a->obj && b->obj) { + /* An object made from a NID that has no OID encoding here, as for + * name attribute types, is only the tag and a zero length: + * compare NIDs. */ + if (a->objSz <= 2 || b->objSz <= 2) { + int nidA = wolfSSL_OBJ_obj2nid(a); + int nidB = wolfSSL_OBJ_obj2nid(b); + + if (nidA > 0 && nidB > 0) { + if (nidA == nidB) + return 0; + return (nidA < nidB) ? -1 : 1; + } + } if (a->objSz == b->objSz) { return XMEMCMP(a->obj, b->obj, a->objSz); } diff --git a/src/x509.c b/src/x509.c index 3a2e2f73e03..b4120607e4e 100644 --- a/src/x509.c +++ b/src/x509.c @@ -2218,8 +2218,10 @@ void* wolfSSL_X509V3_EXT_d2i(WOLFSSL_X509_EXTENSION* ext) WOLFSSL_MSG("Failed to malloc ASN1_STRING"); break; } - ret = wolfSSL_ASN1_STRING_set(newString, cert->extSubjKeyId, - cert->extSubjKeyIdSz); + /* The identifier itself. extSubjKeyId holds a hash of it when + * it is not KEYID_SIZE bytes long. */ + ret = wolfSSL_ASN1_STRING_set(newString, cert->extSubjKeyIdSrc, + (int)cert->extSubjKeyIdSz); if (ret != WOLFSSL_SUCCESS) { WOLFSSL_MSG("ASN1_STRING_set() failed"); wolfSSL_ASN1_STRING_free(newString); @@ -4314,6 +4316,11 @@ char* wolfSSL_X509_NAME_oneline(WOLFSSL_X509_NAME* name, char* in, int sz) WOLFSSL_ENTER("wolfSSL_X509_NAME_oneline"); + /* An empty name gives an empty string, as OpenSSL. */ + if (in != NULL && sz > 0) { + in[0] = '\0'; + } + if (name == NULL) { WOLFSSL_MSG("WOLFSSL_X509_NAME pointer was NULL"); return NULL; @@ -4365,20 +4372,22 @@ static unsigned long X509NameHash(WOLFSSL_X509_NAME* name, return 0; } - if (name->sz == 0) { - WOLFSSL_ERROR_MSG("Nothing to hash in WOLFSSL_X509_NAME"); - return 0; + if (name->sz == 0 || wolfSSL_X509_NAME_entry_count(name) == 0) { + /* Empty name: hash of an empty encoding, as OpenSSL. */ + size = 0; } + else { + size = wolfSSL_i2d_X509_NAME_canon(name, &canonName); - size = wolfSSL_i2d_X509_NAME_canon(name, &canonName); - - if (size <= 0 || canonName == NULL) { - WOLFSSL_ERROR_MSG("wolfSSL_i2d_X509_NAME_canon error"); - return 0; + if (size <= 0 || canonName == NULL) { + WOLFSSL_ERROR_MSG("wolfSSL_i2d_X509_NAME_canon error"); + return 0; + } } - rc = wc_Hash(hashType, (const byte*)canonName, (word32)size, digest, - sizeof(digest)); + rc = wc_Hash(hashType, + (canonName != NULL) ? (const byte*)canonName : (const byte*)"", + (word32)size, digest, sizeof(digest)); if (rc == 0) { hash = (((unsigned long)digest[3] << 24) | @@ -16069,6 +16078,48 @@ int wolfSSL_write_X509_CRL(WOLFSSL_X509_CRL* crl, const char* path, int type) return ne; } + +#ifdef OPENSSL_EXTRA + /* Duplicate a name entry with copies of its object and value. + * Returns the new entry or NULL on failure. */ + WOLFSSL_X509_NAME_ENTRY* wolfSSL_X509_NAME_ENTRY_dup( + const WOLFSSL_X509_NAME_ENTRY* ne) + { + WOLFSSL_X509_NAME_ENTRY* dupl = NULL; + int err = 0; + + WOLFSSL_ENTER("wolfSSL_X509_NAME_ENTRY_dup"); + + if (ne == NULL) { + return NULL; + } + dupl = wolfSSL_X509_NAME_ENTRY_new(); + if (dupl == NULL) { + return NULL; + } + dupl->nid = ne->nid; + dupl->set = ne->set; + dupl->size = ne->size; + if (ne->object != NULL) { + dupl->object = wolfSSL_ASN1_OBJECT_dup(ne->object); + if (dupl->object == NULL) { + err = 1; + } + } + if ((err == 0) && (ne->value != NULL)) { + dupl->value = wolfSSL_ASN1_STRING_dup(ne->value); + if (dupl->value == NULL) { + err = 1; + } + } + if (err != 0) { + wolfSSL_X509_NAME_ENTRY_free(dupl); + dupl = NULL; + } + + return dupl; + } +#endif /* OPENSSL_EXTRA */ #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \ @@ -16759,6 +16810,14 @@ void wolfSSL_sk_X509_NAME_ENTRY_free(WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk) wolfSSL_sk_free(sk); } +/* Free the stack and each entry in it with f. */ +void wolfSSL_sk_X509_NAME_ENTRY_pop_free( + WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, + void (*f)(WOLFSSL_X509_NAME_ENTRY*)) +{ + wolfSSL_sk_pop_free(sk, (wolfSSL_sk_freefunc)f); +} + #endif /* OPENSSL_EXTRA || HAVE_STUNNEL || WOLFSSL_NGINX || HAVE_LIGHTY || WOLFSSL_HAPROXY || WOLFSSL_OPENSSH || HAVE_SBLIM_SFCB */ diff --git a/tests/api/test_ossl_asn1.c b/tests/api/test_ossl_asn1.c index b9479e35a9d..c45818c9c1f 100644 --- a/tests/api/test_ossl_asn1.c +++ b/tests/api/test_ossl_asn1.c @@ -170,6 +170,91 @@ int test_wolfSSL_ASN1_INTEGER_cmp(void) ExpectIntEQ(ASN1_INTEGER_set(b, 0x1000), 1); ExpectIntLT(wolfSSL_ASN1_INTEGER_cmp(a, b), 0); ExpectIntGT(wolfSSL_ASN1_INTEGER_cmp(b, a), 0); +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_RSA) + /* Equal values compare equal with or without a leading zero byte. + * This serial has the top bit set. */ + { + X509* x509 = NULL; + BIGNUM* bn = NULL; + ASN1_INTEGER* serial = NULL; + ASN1_INTEGER* other = NULL; + + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file( + "./certs/ocsp/imposter-root-ca-cert.pem", WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(serial = X509_get_serialNumber(x509)); + ExpectNotNull(bn = ASN1_INTEGER_to_BN(serial, NULL)); + ExpectNotNull(other = BN_to_ASN1_INTEGER(bn, NULL)); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(serial, other), 0); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(other, serial), 0); + ExpectIntEQ(BN_set_word(bn, 0x80), 1); + ExpectNotNull(BN_to_ASN1_INTEGER(bn, other)); + ExpectIntEQ(ASN1_INTEGER_set(a, 0x80), 1); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(a, other), 0); + ExpectIntGT(wolfSSL_ASN1_INTEGER_cmp(b, other), 0); + ASN1_INTEGER_free(other); + BN_free(bn); + X509_free(x509); + } +#endif + /* Equal negative values made in different ways compare equal. */ + { + static const unsigned char m128[] = { 0x02, 0x01, 0x80 }; + static const unsigned char m129[] = { 0x02, 0x02, 0xff, 0x7f }; + const unsigned char* p = NULL; + BIGNUM* bn = NULL; + ASN1_INTEGER* c = NULL; + ASN1_INTEGER* d = NULL; + + ExpectIntEQ(ASN1_INTEGER_set(a, -128), 1); + ExpectNotNull(bn = ASN1_INTEGER_to_BN(a, NULL)); + ExpectNotNull(c = BN_to_ASN1_INTEGER(bn, NULL)); + p = m128; + ExpectNotNull(d = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(m128))); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(a, c), 0); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(c, d), 0); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(d, a), 0); + BN_free(bn); + bn = NULL; + ASN1_INTEGER_free(d); + d = NULL; + + ExpectIntEQ(ASN1_INTEGER_set(b, -129), 1); + ExpectNotNull(bn = ASN1_INTEGER_to_BN(b, NULL)); + ExpectNotNull(BN_to_ASN1_INTEGER(bn, c)); + p = m129; + ExpectNotNull(d = d2i_ASN1_INTEGER(NULL, &p, (long)sizeof(m129))); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(b, c), 0); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(b, d), 0); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(c, d), 0); + ExpectIntLT(wolfSSL_ASN1_INTEGER_cmp(c, a), 0); + ExpectIntGT(wolfSSL_ASN1_INTEGER_cmp(a, d), 0); + BN_free(bn); + ASN1_INTEGER_free(d); + d = NULL; + ASN1_INTEGER_free(c); + c = NULL; + + /* Only the content, as built by hand. */ + ExpectNotNull(c = ASN1_INTEGER_new()); + if (c != NULL) { + c->data[0] = 0x01; + c->data[1] = 0x00; + c->length = 2; + } + ExpectIntEQ(ASN1_INTEGER_set(a, 0x100), 1); + ExpectIntEQ(wolfSSL_ASN1_INTEGER_cmp(c, a), 0); + ExpectIntEQ(ASN1_INTEGER_set(a, 0xff), 1); + ExpectIntGT(wolfSSL_ASN1_INTEGER_cmp(c, a), 0); + ASN1_INTEGER_free(c); + c = NULL; + + /* An empty INTEGER is not 0, as in OpenSSL. */ + ExpectNotNull(c = ASN1_INTEGER_new()); + ExpectIntEQ(ASN1_INTEGER_set(a, 0), 1); + ExpectIntLT(wolfSSL_ASN1_INTEGER_cmp(c, a), 0); + ExpectIntGT(wolfSSL_ASN1_INTEGER_cmp(a, c), 0); + ASN1_INTEGER_free(c); + } ASN1_INTEGER_free(b); ASN1_INTEGER_free(a); diff --git a/tests/api/test_ossl_obj.c b/tests/api/test_ossl_obj.c index 178aa9444e5..0f8b9557c13 100644 --- a/tests/api/test_ossl_obj.c +++ b/tests/api/test_ossl_obj.c @@ -259,6 +259,33 @@ int test_wolfSSL_OBJ_cmp(void) ExpectIntEQ(OBJ_cmp(obj, obj), 0); ExpectIntEQ(OBJ_cmp(obj2, obj2), 0); + ASN1_OBJECT_free(obj); + obj = NULL; + ASN1_OBJECT_free(obj2); + obj2 = NULL; + + /* Name attribute types differ. */ + ExpectNotNull(obj = OBJ_nid2obj(NID_commonName)); + ExpectNotNull(obj2 = OBJ_nid2obj(NID_organizationName)); + ExpectIntNE(OBJ_cmp(obj, obj2), 0); + ExpectIntEQ(OBJ_cmp(obj, obj2), -OBJ_cmp(obj2, obj)); + ExpectIntEQ(OBJ_cmp(obj, obj), 0); + ASN1_OBJECT_free(obj2); + obj2 = NULL; + ExpectNotNull(obj2 = OBJ_nid2obj(NID_commonName)); + ExpectIntEQ(OBJ_cmp(obj, obj2), 0); +#if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN) + ASN1_OBJECT_free(obj2); + obj2 = NULL; + ExpectNotNull(obj2 = OBJ_txt2obj("2.5.4.3", 1)); + ExpectIntEQ(OBJ_cmp(obj, obj2), 0); + ExpectIntEQ(OBJ_cmp(obj2, obj), 0); + ASN1_OBJECT_free(obj2); + obj2 = NULL; + ExpectNotNull(obj2 = OBJ_txt2obj("2.5.4.10", 1)); + ExpectIntNE(OBJ_cmp(obj, obj2), 0); +#endif + ASN1_OBJECT_free(obj); ASN1_OBJECT_free(obj2); #endif diff --git a/tests/api/test_ossl_x509.c b/tests/api/test_ossl_x509.c index bfa4b0b6981..8f30e21df24 100644 --- a/tests/api/test_ossl_x509.c +++ b/tests/api/test_ossl_x509.c @@ -306,7 +306,12 @@ int test_wolfSSL_X509_subject_name_hash(void) ExpectNotNull(x509 = X509_new()); ExpectIntEQ(X509_subject_name_hash(NULL), 0); - ExpectIntEQ(X509_subject_name_hash(x509), 0); + /* Empty name: hash of an empty encoding, as OpenSSL. */ +#ifndef NO_SHA + ExpectIntEQ(X509_subject_name_hash(x509), 0xeea339daUL); +#else + ExpectIntEQ(X509_subject_name_hash(x509), 0x42c4b0e3UL); +#endif X509_free(x509); x509 = NULL; @@ -348,7 +353,12 @@ int test_wolfSSL_X509_issuer_name_hash(void) ExpectNotNull(x509 = X509_new()); ExpectIntEQ(X509_issuer_name_hash(NULL), 0); - ExpectIntEQ(X509_issuer_name_hash(x509), 0); + /* Empty name: hash of an empty encoding, as OpenSSL. */ +#ifndef NO_SHA + ExpectIntEQ(X509_issuer_name_hash(x509), 0xeea339daUL); +#else + ExpectIntEQ(X509_issuer_name_hash(x509), 0x42c4b0e3UL); +#endif X509_free(x509); x509 = NULL; diff --git a/tests/api/test_ossl_x509_crypto.c b/tests/api/test_ossl_x509_crypto.c index 5992938259e..d90c86a4f50 100644 --- a/tests/api/test_ossl_x509_crypto.c +++ b/tests/api/test_ossl_x509_crypto.c @@ -937,8 +937,12 @@ int test_wolfSSL_X509_sign(void) ExpectIntGT(cnSz = X509_NAME_get_sz(name), 0); ExpectNotNull(cn = (char*)XMALLOC(cnSz, HEAP_HINT, DYNAMIC_TYPE_OPENSSL)); ExpectNull(X509_NAME_oneline(NULL, cn, (int)cnSz)); + ExpectTrue(cn == NULL || cn[0] == '\0'); ExpectPtrEq(X509_NAME_oneline(name, cn, 0), cn); + if (cn != NULL) + cn[0] = 'x'; ExpectPtrEq(X509_NAME_oneline(emptyName, cn, (int)cnSz), cn); + ExpectTrue(cn == NULL || cn[0] == '\0'); ExpectNull(X509_NAME_oneline(emptyName, NULL, 0)); ExpectPtrEq(X509_NAME_oneline(name, cn, (int)cnSz), cn); ExpectIntEQ(0, XSTRNCMP(cn, dCert.subject, XSTRLEN(cn))); diff --git a/tests/api/test_ossl_x509_ext.c b/tests/api/test_ossl_x509_ext.c index af2bedc46e2..3606946435f 100644 --- a/tests/api/test_ossl_x509_ext.c +++ b/tests/api/test_ossl_x509_ext.c @@ -1714,6 +1714,68 @@ int test_wolfSSL_X509V3_EXT(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509V3_EXT_d2i_skid(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) + /* Self-signed P-256 cert with the 8 byte subjectKeyIdentifier + * 01:02:03:04:05:06:07:08. */ + static const unsigned char certDer[] = { + 0x30, 0x82, 0x01, 0x66, 0x30, 0x82, 0x01, 0x0C, 0xA0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x15, 0x97, 0xDB, 0x0A, 0x35, 0x44, 0x83, 0x46, 0x36, + 0x38, 0xDF, 0xB6, 0x18, 0x9C, 0x99, 0xED, 0xFC, 0xEE, 0x78, 0x17, 0x30, + 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, + 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x04, + 0x53, 0x4B, 0x49, 0x38, 0x30, 0x20, 0x17, 0x0D, 0x32, 0x36, 0x31, 0x30, + 0x31, 0x33, 0x31, 0x39, 0x35, 0x34, 0x31, 0x32, 0x5A, 0x18, 0x0F, 0x32, + 0x31, 0x32, 0x36, 0x30, 0x39, 0x31, 0x39, 0x31, 0x39, 0x35, 0x34, 0x31, + 0x32, 0x5A, 0x30, 0x0F, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0C, 0x04, 0x53, 0x4B, 0x49, 0x38, 0x30, 0x59, 0x30, 0x13, 0x06, + 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x22, 0x25, + 0x14, 0x25, 0x6B, 0x70, 0x38, 0xB3, 0x93, 0x5E, 0x18, 0x77, 0x58, 0x35, + 0xCE, 0x42, 0x4A, 0x95, 0xD4, 0xA5, 0xDD, 0x92, 0xB7, 0xC9, 0xEC, 0xF8, + 0xEB, 0x39, 0x5F, 0x44, 0xCB, 0x06, 0x69, 0xD3, 0xE0, 0x26, 0xAF, 0xC3, + 0xF3, 0xC0, 0x07, 0x40, 0xAC, 0x1C, 0x15, 0xEE, 0xBD, 0x5C, 0x15, 0x70, + 0x0A, 0x2A, 0x34, 0xB2, 0xF8, 0xCD, 0x34, 0xEC, 0xAB, 0x82, 0x82, 0x88, + 0x82, 0x81, 0xA3, 0x44, 0x30, 0x42, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xA7, 0xFC, 0xEC, 0x51, 0x8B, + 0xA3, 0x66, 0x18, 0x6C, 0xB2, 0xB4, 0x7C, 0x31, 0xF6, 0x7A, 0x22, 0x51, + 0xAB, 0x9D, 0xC4, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x0A, + 0x04, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x30, 0x0C, + 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04, 0x02, 0x30, 0x00, + 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, + 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x66, 0x9B, 0x36, 0xE5, 0xA5, + 0x82, 0x34, 0x11, 0xE2, 0x62, 0x94, 0x5E, 0xAE, 0xD6, 0xAD, 0x4B, 0x73, + 0x2E, 0xD1, 0x09, 0x6C, 0x57, 0x76, 0xF7, 0xD6, 0xCC, 0xC0, 0x24, 0x05, + 0x74, 0xD9, 0x14, 0x02, 0x21, 0x00, 0xC3, 0xA8, 0x3B, 0xC6, 0x8D, 0x4D, + 0x58, 0xF0, 0x0B, 0x59, 0xDE, 0x8F, 0x53, 0x59, 0x8C, 0xA9, 0xC3, 0x2A, + 0x8F, 0x6D, 0x6C, 0x09, 0x25, 0xFB, 0x56, 0x1C, 0xBF, 0xD1, 0x35, 0x1B, + 0x8B, 0xC2, + }; + static const unsigned char skid[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + }; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_X509_EXTENSION* ext = NULL; + WOLFSSL_ASN1_STRING* str = NULL; + int idx = -1; + + ExpectNotNull(x509 = wolfSSL_X509_d2i(NULL, certDer, + (int)sizeof(certDer))); + ExpectIntGE(idx = wolfSSL_X509_get_ext_by_NID(x509, + NID_subject_key_identifier, -1), 0); + ExpectNotNull(ext = wolfSSL_X509_get_ext(x509, idx)); + ExpectNotNull(str = (WOLFSSL_ASN1_STRING*)wolfSSL_X509V3_EXT_d2i(ext)); + ExpectIntEQ(wolfSSL_ASN1_STRING_length(str), (int)sizeof(skid)); + ExpectBufEQ(wolfSSL_ASN1_STRING_get0_data(str), skid, sizeof(skid)); + + wolfSSL_ASN1_STRING_free(str); + wolfSSL_X509_free(x509); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509V3_EXT_print(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_ext.h b/tests/api/test_ossl_x509_ext.h index a95b34a8050..5bb3ee93e3a 100644 --- a/tests/api/test_ossl_x509_ext.h +++ b/tests/api/test_ossl_x509_ext.h @@ -48,6 +48,7 @@ int test_wolfSSL_X509_get_ext_d2i_basic_constraints(void); int test_wolfSSL_X509V3_EXT_san(void); int test_wolfSSL_X509V3_EXT_aia(void); int test_wolfSSL_X509V3_EXT(void); +int test_wolfSSL_X509V3_EXT_d2i_skid(void); int test_wolfSSL_X509V3_EXT_print(void); int test_wolfSSL_X509_get_ext_d2i_name_constraints(void); int test_wolfSSL_sk_GENERAL_SUBTREE(void); @@ -89,6 +90,7 @@ int test_wolfSSL_X509_set_ext_oid_collision(void); TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_san), \ TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_aia), \ TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT), \ + TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_d2i_skid), \ TEST_DECL_GROUP("ossl_x509_ext", test_wolfSSL_X509V3_EXT_print), \ TEST_DECL_GROUP("ossl_x509_ext", \ test_wolfSSL_X509_get_ext_d2i_name_constraints), \ diff --git a/tests/api/test_ossl_x509_name.c b/tests/api/test_ossl_x509_name.c index ac83e173470..22525afb494 100644 --- a/tests/api/test_ossl_x509_name.c +++ b/tests/api/test_ossl_x509_name.c @@ -323,7 +323,8 @@ int test_wolfSSL_X509_NAME_hash(void) ExpectIntEQ(X509_NAME_hash(NULL), 0); ExpectNotNull(name = wolfSSL_X509_NAME_new_ex(NULL)); - ExpectIntEQ(X509_NAME_hash(name), 0); + /* SHA-1 of an empty encoding, as OpenSSL. */ + ExpectIntEQ(X509_NAME_hash(name), 0xeea339daUL); X509_NAME_free(name); ExpectNotNull(bio = BIO_new(BIO_s_file())); @@ -930,6 +931,89 @@ int test_wolfSSL_X509_NAME_ENTRY(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_NAME_ENTRY_dup(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + X509* x509 = NULL; + X509_NAME* name = NULL; + X509_NAME_ENTRY* ne = NULL; + X509_NAME_ENTRY* dup = NULL; + STACK_OF(X509_NAME_ENTRY)* sk = NULL; + ASN1_STRING* val = NULL; + ASN1_STRING* dupVal = NULL; + int i; + int cnt = 0; + int nid = 0; + + ExpectNull(X509_NAME_ENTRY_dup(NULL)); + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(cliCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(name = X509_get_subject_name(x509)); + ExpectIntGT(cnt = X509_NAME_entry_count(name), 0); + + /* Copies of all entries, freed with the stack. */ + ExpectNotNull(sk = sk_X509_NAME_ENTRY_new(NULL)); + for (i = 0; i < cnt; i++) { + ExpectNotNull(ne = X509_NAME_get_entry(name, i)); + ExpectNotNull(dup = X509_NAME_ENTRY_dup(ne)); + ExpectTrue(dup != ne); + ExpectNotNull(val = X509_NAME_ENTRY_get_data(ne)); + ExpectNotNull(dupVal = X509_NAME_ENTRY_get_data(dup)); + ExpectTrue(val != dupVal); + ExpectIntEQ(ASN1_STRING_length(dupVal), ASN1_STRING_length(val)); + ExpectIntEQ(ASN1_STRING_type(dupVal), ASN1_STRING_type(val)); + ExpectIntEQ(XMEMCMP(ASN1_STRING_get0_data(dupVal), + ASN1_STRING_get0_data(val), (size_t)ASN1_STRING_length(val)), 0); + ExpectIntEQ(OBJ_cmp(X509_NAME_ENTRY_get_object(dup), + X509_NAME_ENTRY_get_object(ne)), 0); + ExpectIntEQ(OBJ_obj2nid(X509_NAME_ENTRY_get_object(dup)), + OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne))); + if (EXPECT_SUCCESS() && (sk_X509_NAME_ENTRY_push(sk, dup) > 0)) { + dup = NULL; + } + X509_NAME_ENTRY_free(dup); + dup = NULL; + } + ExpectIntEQ(sk_X509_NAME_ENTRY_num(sk), cnt); + sk_X509_NAME_ENTRY_pop_free(sk, X509_NAME_ENTRY_free); + sk_X509_NAME_ENTRY_pop_free(NULL, X509_NAME_ENTRY_free); + + /* The copy outlives the original. */ + ExpectNotNull(ne = X509_NAME_get_entry(name, 0)); + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)); + ExpectNotNull(dup = X509_NAME_ENTRY_dup(ne)); + X509_free(x509); + ExpectNotNull(val = X509_NAME_ENTRY_get_data(dup)); + ExpectIntGT(ASN1_STRING_length(val), 0); + ExpectIntEQ(OBJ_obj2nid(X509_NAME_ENTRY_get_object(dup)), nid); + X509_NAME_ENTRY_free(dup); +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_X509_NAME_ENTRY_octet_string(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) + X509_NAME_ENTRY* ne = NULL; + ASN1_STRING* val = NULL; + const unsigned char raw[] = { 0x04, 0x03, 0x00, 0xab, 0xcd }; + + /* Same value as OpenSSL. */ + ExpectIntEQ(B_ASN1_OCTET_STRING, 0x0200); + /* Not an MBSTRING_* type: the type is kept as given. */ + ExpectNotNull(ne = X509_NAME_ENTRY_create_by_txt(NULL, "CN", + B_ASN1_OCTET_STRING, raw, (int)sizeof(raw))); + ExpectNotNull(val = X509_NAME_ENTRY_get_data(ne)); + ExpectIntEQ(ASN1_STRING_type(val), B_ASN1_OCTET_STRING); + ExpectIntEQ(ASN1_STRING_length(val), (int)sizeof(raw)); + ExpectIntEQ(XMEMCMP(ASN1_STRING_get0_data(val), raw, sizeof(raw)), 0); + X509_NAME_ENTRY_free(ne); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_NAME_ENTRY_get_object(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_name.h b/tests/api/test_ossl_x509_name.h index 2e99c65ed88..c973a3fff23 100644 --- a/tests/api/test_ossl_x509_name.h +++ b/tests/api/test_ossl_x509_name.h @@ -29,6 +29,8 @@ int test_wolfSSL_X509_NAME(void); int test_wolfSSL_X509_NAME_hash(void); int test_wolfSSL_X509_NAME_print_ex(void); int test_wolfSSL_X509_NAME_ENTRY(void); +int test_wolfSSL_X509_NAME_ENTRY_dup(void); +int test_wolfSSL_X509_NAME_ENTRY_octet_string(void); int test_wolfSSL_X509_NAME_ENTRY_get_object(void); int test_wolfSSL_X509_NAME_ENTRY_shared_object(void); @@ -38,6 +40,9 @@ int test_wolfSSL_X509_NAME_ENTRY_shared_object(void); TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_hash), \ TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_print_ex), \ TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_ENTRY), \ + TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_ENTRY_dup), \ + TEST_DECL_GROUP("ossl_x509_name", \ + test_wolfSSL_X509_NAME_ENTRY_octet_string), \ TEST_DECL_GROUP("ossl_x509_name", \ test_wolfSSL_X509_NAME_ENTRY_get_object), \ TEST_DECL_GROUP("ossl_x509_name", \ diff --git a/wolfssl/openssl/asn1.h b/wolfssl/openssl/asn1.h index 0f18a153a64..51d48c6e721 100644 --- a/wolfssl/openssl/asn1.h +++ b/wolfssl/openssl/asn1.h @@ -86,6 +86,8 @@ #define V_ASN1_CONSTRUCTED WOLFSSL_V_ASN1_CONSTRUCTED +#define B_ASN1_OCTET_STRING WOLFSSL_B_ASN1_OCTET_STRING + #define ASN1_STRING_FLAG_BITS_LEFT WOLFSSL_ASN1_STRING_FLAG_BITS_LEFT #define ASN1_STRING_FLAG_NDEF WOLFSSL_ASN1_STRING_FLAG_NDEF #define ASN1_STRING_FLAG_CONT WOLFSSL_ASN1_STRING_FLAG_CONT diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 2eae85b7253..df8c9cd562e 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -720,6 +720,7 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define X509_NAME_cmp wolfSSL_X509_NAME_cmp #define X509_NAME_ENTRY_new wolfSSL_X509_NAME_ENTRY_new #define X509_NAME_ENTRY_free wolfSSL_X509_NAME_ENTRY_free +#define X509_NAME_ENTRY_dup wolfSSL_X509_NAME_ENTRY_dup #define X509_NAME_ENTRY_create_by_NID wolfSSL_X509_NAME_ENTRY_create_by_NID #define X509_NAME_ENTRY_create_by_txt wolfSSL_X509_NAME_ENTRY_create_by_txt #define X509_NAME_add_entry wolfSSL_X509_NAME_add_entry @@ -766,6 +767,7 @@ typedef WOLFSSL_X509_NAME_ENTRY X509_NAME_ENTRY; #define sk_X509_NAME_ENTRY_num wolfSSL_sk_X509_NAME_ENTRY_num #define sk_X509_NAME_ENTRY_value wolfSSL_sk_X509_NAME_ENTRY_value #define sk_X509_NAME_ENTRY_free wolfSSL_sk_X509_NAME_ENTRY_free +#define sk_X509_NAME_ENTRY_pop_free wolfSSL_sk_X509_NAME_ENTRY_pop_free #define X509_V_FLAG_CRL_CHECK WOLFSSL_X509_V_FLAG_CRL_CHECK #define X509_V_FLAG_CRL_CHECK_ALL WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index be1dd24f64d..3b1cd7aa5a0 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -410,6 +410,9 @@ typedef int (*WOLFSSL_X509_STORE_CTX_check_crl_cb)(WOLFSSL_X509_STORE_CTX *, #define WOLFSSL_V_ASN1_CONSTRUCTED 0x20 +/* Type bit of an OCTET STRING, as in OpenSSL */ +#define WOLFSSL_B_ASN1_OCTET_STRING 0x0200 + #define WOLFSSL_ASN1_STRING_FLAG_BITS_LEFT 0x008 #define WOLFSSL_ASN1_STRING_FLAG_NDEF 0x010 #define WOLFSSL_ASN1_STRING_FLAG_CONT 0x020 @@ -5850,6 +5853,10 @@ WOLFSSL_API int wolfSSL_X509_NAME_ENTRY_set(const WOLFSSL_X509_NAME_ENTRY *ne); || defined(OPENSSL_EXTRA_X509_SMALL) WOLFSSL_API void wolfSSL_X509_NAME_ENTRY_free(WOLFSSL_X509_NAME_ENTRY* ne); WOLFSSL_API WOLFSSL_X509_NAME_ENTRY* wolfSSL_X509_NAME_ENTRY_new(void); +#ifdef OPENSSL_EXTRA +WOLFSSL_API WOLFSSL_X509_NAME_ENTRY* wolfSSL_X509_NAME_ENTRY_dup( + const WOLFSSL_X509_NAME_ENTRY* ne); +#endif #ifndef wolfSSL_X509_NAME_free WOLFSSL_API void wolfSSL_X509_NAME_free(WOLFSSL_X509_NAME* name); #endif @@ -6054,6 +6061,9 @@ WOLFSSL_API WOLFSSL_X509_NAME_ENTRY* wolfSSL_sk_X509_NAME_ENTRY_value(const WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, int i); WOLFSSL_API int wolfSSL_sk_X509_NAME_ENTRY_num(const WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk); WOLFSSL_API void wolfSSL_sk_X509_NAME_ENTRY_free(WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk); +WOLFSSL_API void wolfSSL_sk_X509_NAME_ENTRY_pop_free( + WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, + void (*f)(WOLFSSL_X509_NAME_ENTRY*)); WOLFSSL_API int wolfSSL_sk_X509_OBJECT_num(const WOLF_STACK_OF(WOLFSSL_X509_OBJECT) *s); From cee1516beb58e9881fd0e85b221132ed124c4c92 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:20:41 +0000 Subject: [PATCH 13/31] Add stack sorting, sk_X509_insert and sk_X509_delete_ptr Applications sort the entries of two names to compare them regardless of order, as xmlsec does. With OPENSSL_ALL, keep a comparator in the stack, as OpenSSL does, and keep the one given to sk_X509_NAME_ENTRY_new. Add wolfSSL_sk_set_cmp_func, mapped to OPENSSL_sk_set_cmp_func, which sets the comparator and returns the previous one, and wolfSSL_sk_sort, mapped to OPENSSL_sk_sort, a stable insertion sort. Each node's hash moves with its data. Cipher stacks hold their data in the node and are left as they are. Add sk_X509_NAME_ENTRY_set_cmp_func and sk_X509_NAME_ENTRY_sort. Applications reorder a cert stack in place, as xmlsec does to put the key cert first and to drop duplicates. Add wolfSSL_sk_delete_ptr, which removes the first node that holds the pointer and returns it, or NULL when the pointer is not in the stack, as OpenSSL's sk_delete_ptr does. The data is not freed. Cipher stacks hold copies, not pointers, so they are not searched. Map sk_X509_insert to wolfSSL_sk_insert and add sk_X509_delete_ptr. --- src/ssl_sk.c | 108 ++++++++++++++++++++++++ src/x509.c | 22 +++++ tests/api/test_ossl_sk.c | 142 ++++++++++++++++++++++++++++++++ tests/api/test_ossl_sk.h | 7 ++ tests/api/test_ossl_x509_name.c | 90 ++++++++++++++++++++ tests/api/test_ossl_x509_name.h | 2 + wolfssl/internal.h | 1 + wolfssl/openssl/compat_types.h | 2 + wolfssl/openssl/ssl.h | 7 ++ wolfssl/ssl.h | 16 ++++ 10 files changed, 397 insertions(+) diff --git a/src/ssl_sk.c b/src/ssl_sk.c index 15370830e61..630a6fa4570 100644 --- a/src/ssl_sk.c +++ b/src/ssl_sk.c @@ -756,6 +756,7 @@ int wolfSSL_sk_insert(WOLFSSL_STACK *stack, const void *data, int idx) node->type = stack->type; #ifdef OPENSSL_ALL node->hash_fn = stack->hash_fn; + node->comp = stack->comp; #endif /* Put data into new node. */ wolfssl_sk_node_set_data(node, stack->type, data); @@ -815,6 +816,113 @@ void* wolfSSL_sk_delete(WOLFSSL_STACK* stack, int idx) return wolfSSL_sk_pop_node(stack, idx); } +/* Remove the first node that holds the data pointer and return the data. + * + * @param [in, out] stack Stack of nodes with data. + * @param [in] data Data to find. + * @return data when found and removed. + * @return NULL when stack or data is NULL or data is not in the stack. + */ +void* wolfSSL_sk_delete_ptr(WOLFSSL_STACK* stack, const void* data) +{ + WOLFSSL_STACK* node; + int idx = 0; + + WOLFSSL_ENTER("wolfSSL_sk_delete_ptr"); + + /* Cipher stacks hold copies, not pointers. */ + if ((stack == NULL) || (data == NULL) || + (stack->type == STACK_TYPE_CIPHER)) { + return NULL; + } + + for (node = stack; (node != NULL) && ((unsigned long)idx < stack->num); + node = node->next) { + if (node->data.generic == data) { + return wolfSSL_sk_pop_node(stack, idx); + } + idx++; + } + + return NULL; +} + +#ifdef OPENSSL_ALL +/* Set the comparator used by sort. + * + * Set on every node as a removed first node is replaced by the next one. + * + * @param [in, out] stack Stack of data. + * @param [in] cb Comparator. May be NULL. + * @return Previous comparator. + * @return NULL when stack is NULL or no comparator was set. + */ +wolf_sk_compare_cb wolfSSL_sk_set_cmp_func(WOLFSSL_STACK* stack, + wolf_sk_compare_cb cb) +{ + wolf_sk_compare_cb old = NULL; + + WOLFSSL_ENTER("wolfSSL_sk_set_cmp_func"); + + if (stack != NULL) { + old = stack->comp; + } + for (; stack != NULL; stack = stack->next) { + stack->comp = cb; + } + + return old; +} + +/* Sort the data in the stack with the stack's comparator. + * + * Stable insertion sort: the first node starts as the sorted part and each + * following node's data is moved into place. Nodes stay, data moves. + * Cipher stacks hold their data in the node and are not sorted. + * + * @param [in, out] stack Stack of data. + */ +void wolfSSL_sk_sort(WOLFSSL_STACK* stack) +{ + WOLFSSL_STACK* cur; + WOLFSSL_STACK* pos; + void* data; + void* next; + unsigned long hash; + unsigned long nextHash; + + WOLFSSL_ENTER("wolfSSL_sk_sort"); + + if ((stack == NULL) || (stack->comp == NULL) || + (stack->type == STACK_TYPE_CIPHER)) { + return; + } + + for (cur = stack->next; cur != NULL; cur = cur->next) { + /* Find the first entry greater than cur. */ + for (pos = stack; pos != cur; pos = pos->next) { + if (stack->comp((const void* const*)&cur->data.generic, + (const void* const*)&pos->data.generic) < 0) { + break; + } + } + /* Move cur's data and hash there and shift the rest up. */ + data = cur->data.generic; + hash = cur->hash; + for (; pos != cur; pos = pos->next) { + next = pos->data.generic; + nextHash = pos->hash; + pos->data.generic = data; + pos->hash = hash; + data = next; + hash = nextHash; + } + cur->data.generic = data; + cur->hash = hash; + } +} +#endif /* OPENSSL_ALL */ + /* Pop off data from the stack. Checks that the type matches the stack type. * * This is an internal API. diff --git a/src/x509.c b/src/x509.c index b4120607e4e..bbf391a9b8b 100644 --- a/src/x509.c +++ b/src/x509.c @@ -16780,6 +16780,9 @@ WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* wolfSSL_sk_X509_NAME_ENTRY_new( WOLFSSL_STACK* sk = wolfSSL_sk_new_node(NULL); if (sk != NULL) { sk->type = STACK_TYPE_X509_NAME_ENTRY; + #ifdef OPENSSL_ALL + sk->comp = (wolf_sk_compare_cb)cb; + #endif (void)cb; } return sk; @@ -16818,6 +16821,25 @@ void wolfSSL_sk_X509_NAME_ENTRY_pop_free( wolfSSL_sk_pop_free(sk, (wolfSSL_sk_freefunc)f); } +#ifdef OPENSSL_ALL +/* Sets the comparator used by sort. Returns the previous one. */ +wolf_sk_X509_NAME_ENTRY_cmp_cb wolfSSL_sk_X509_NAME_ENTRY_set_cmp_func( + WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, + wolf_sk_X509_NAME_ENTRY_cmp_cb cb) +{ + return (wolf_sk_X509_NAME_ENTRY_cmp_cb)wolfSSL_sk_set_cmp_func(sk, + (wolf_sk_compare_cb)cb); +} + +/* Sorts the entries with the comparator. */ +void wolfSSL_sk_X509_NAME_ENTRY_sort(WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk) +{ + WOLFSSL_ENTER("wolfSSL_sk_X509_NAME_ENTRY_sort"); + + wolfSSL_sk_sort(sk); +} +#endif /* OPENSSL_ALL */ + #endif /* OPENSSL_EXTRA || HAVE_STUNNEL || WOLFSSL_NGINX || HAVE_LIGHTY || WOLFSSL_HAPROXY || WOLFSSL_OPENSSH || HAVE_SBLIM_SFCB */ diff --git a/tests/api/test_ossl_sk.c b/tests/api/test_ossl_sk.c index db7ad9ec66e..fae469582e2 100644 --- a/tests/api/test_ossl_sk.c +++ b/tests/api/test_ossl_sk.c @@ -179,6 +179,148 @@ int test_wolfSSL_sk_delete(void) return EXPECT_RESULT(); } +int test_wolfSSL_sk_delete_ptr(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) + WOLFSSL_STACK* stack = NULL; + unsigned char data_1[1] = { 1 }; + unsigned char data_2[1] = { 2 }; + unsigned char data_3[1] = { 3 }; + + ExpectNotNull(stack = wolfSSL_sk_new_node(HEAP_HINT)); + + ExpectNull(wolfSSL_sk_delete_ptr(NULL, data_1)); + ExpectNull(wolfSSL_sk_delete_ptr(stack, NULL)); + ExpectNull(wolfSSL_sk_delete_ptr(stack, data_1)); + + ExpectIntEQ(wolfSSL_sk_push(stack, data_1), 1); + ExpectIntEQ(wolfSSL_sk_push(stack, data_2), 2); + ExpectIntEQ(wolfSSL_sk_push(stack, data_3), 3); + ExpectIntEQ(wolfSSL_sk_push(stack, data_2), 4); + + /* Only the first match is removed. */ + ExpectPtrEq(wolfSSL_sk_delete_ptr(stack, data_2), data_2); + ExpectIntEQ(wolfSSL_sk_num(stack), 3); + ExpectPtrEq(wolfSSL_sk_value(stack, 0), data_1); + ExpectPtrEq(wolfSSL_sk_value(stack, 1), data_3); + ExpectPtrEq(wolfSSL_sk_value(stack, 2), data_2); + /* Last, first and only. */ + ExpectPtrEq(wolfSSL_sk_delete_ptr(stack, data_2), data_2); + ExpectNull(wolfSSL_sk_delete_ptr(stack, data_2)); + ExpectPtrEq(wolfSSL_sk_delete_ptr(stack, data_1), data_1); + ExpectPtrEq(wolfSSL_sk_value(stack, 0), data_3); + ExpectPtrEq(wolfSSL_sk_delete_ptr(stack, data_3), data_3); + ExpectIntEQ(wolfSSL_sk_num(stack), 0); + ExpectNull(wolfSSL_sk_delete_ptr(stack, data_3)); + + wolfSSL_sk_free(stack); +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_sk_X509_insert_delete_ptr(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLF_STACK_OF(WOLFSSL_X509)* certs = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509* srv = NULL; + WOLFSSL_X509* cli = NULL; + + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(srv = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(cli = wolfSSL_X509_load_certificate_file(cliCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(certs = sk_X509_new_null()); + + ExpectIntLE(sk_X509_insert(NULL, ca, 0), 0); + ExpectIntEQ(sk_X509_insert(certs, ca, 0), 1); + ExpectIntEQ(sk_X509_insert(certs, srv, -1), 2); + /* Key cert goes first. */ + ExpectIntEQ(sk_X509_insert(certs, cli, 0), 3); + ExpectPtrEq(sk_X509_value(certs, 0), cli); + ExpectPtrEq(sk_X509_value(certs, 1), ca); + ExpectPtrEq(sk_X509_value(certs, 2), srv); + + ExpectNull(sk_X509_delete_ptr(NULL, ca)); + ExpectPtrEq(sk_X509_delete_ptr(certs, ca), ca); + ExpectNull(sk_X509_delete_ptr(certs, ca)); + ExpectIntEQ(sk_X509_num(certs), 2); + ExpectPtrEq(sk_X509_value(certs, 0), cli); + ExpectPtrEq(sk_X509_value(certs, 1), srv); + /* Insert in the middle. */ + ExpectIntEQ(sk_X509_insert(certs, ca, 1), 3); + ExpectPtrEq(sk_X509_value(certs, 1), ca); + ExpectPtrEq(sk_X509_value(certs, 2), srv); + + /* Stack only borrows the certs. */ + sk_X509_free(certs); + X509_free(ca); + X509_free(srv); + X509_free(cli); +#endif + return EXPECT_RESULT(); +} + +#ifdef OPENSSL_ALL +static int test_sk_sort_cmp(const void* const* a, const void* const* b) +{ + return (int)**(const unsigned char* const*)a - + (int)**(const unsigned char* const*)b; +} +#endif + +int test_wolfSSL_sk_sort(void) +{ + EXPECT_DECLS; +#ifdef OPENSSL_ALL + WOLFSSL_STACK* stack = NULL; + WOLFSSL_STACK* node; + unsigned char data_3[1] = { 3 }; + unsigned char data_1[1] = { 1 }; + unsigned char data_2[1] = { 2 }; + unsigned char data_1b[1] = { 1 }; + + wolfSSL_sk_sort(NULL); + ExpectTrue(wolfSSL_sk_set_cmp_func(NULL, test_sk_sort_cmp) == NULL); + ExpectNotNull(stack = wolfSSL_sk_new_node(HEAP_HINT)); + wolfSSL_sk_sort(stack); + ExpectIntEQ(wolfSSL_sk_push(stack, data_3), 1); + ExpectIntEQ(wolfSSL_sk_push(stack, data_1), 2); + ExpectIntEQ(wolfSSL_sk_push(stack, data_2), 3); + ExpectIntEQ(wolfSSL_sk_push(stack, data_1b), 4); + + /* No comparator: order kept. */ + wolfSSL_sk_sort(stack); + ExpectPtrEq(wolfSSL_sk_value(stack, 0), data_3); + ExpectPtrEq(wolfSSL_sk_value(stack, 3), data_1b); + + /* Stable, and each hash moves with its data. */ + ExpectTrue(wolfSSL_sk_set_cmp_func(stack, test_sk_sort_cmp) == NULL); + for (node = stack; EXPECT_SUCCESS() && (node != NULL); node = node->next) { + ExpectTrue(node->comp == test_sk_sort_cmp); + node->hash = *(unsigned char*)node->data.generic; + } + wolfSSL_sk_sort(stack); + ExpectIntEQ(wolfSSL_sk_num(stack), 4); + ExpectPtrEq(wolfSSL_sk_value(stack, 0), data_1); + ExpectPtrEq(wolfSSL_sk_value(stack, 1), data_1b); + ExpectPtrEq(wolfSSL_sk_value(stack, 2), data_2); + ExpectPtrEq(wolfSSL_sk_value(stack, 3), data_3); + for (node = stack; EXPECT_SUCCESS() && (node != NULL); node = node->next) { + ExpectIntEQ(node->hash, *(unsigned char*)node->data.generic); + } + ExpectTrue(wolfSSL_sk_set_cmp_func(stack, NULL) == test_sk_sort_cmp); + + wolfSSL_sk_free(stack); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_sk_insert(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_sk.h b/tests/api/test_ossl_sk.h index 3acb4369a5d..ab54370b8c6 100644 --- a/tests/api/test_ossl_sk.h +++ b/tests/api/test_ossl_sk.h @@ -30,6 +30,9 @@ int test_wolfSSL_sk_free(void); int test_wolfSSL_sk_push_pop(void); int test_wolfSSL_sk_insert(void); int test_wolfSSL_sk_delete(void); +int test_wolfSSL_sk_delete_ptr(void); +int test_wolfSSL_sk_X509_insert_delete_ptr(void); +int test_wolfSSL_sk_sort(void); int test_wolfSSL_shallow_sk_dup(void); int test_wolfSSL_sk_num(void); int test_wolfSSL_sk_value(void); @@ -54,6 +57,10 @@ int test_wolfSSL_CIPHER_current(void); TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_push_pop), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_insert), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_delete), \ + TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_delete_ptr), \ + TEST_DECL_GROUP("ossl_sk", \ + test_wolfSSL_sk_X509_insert_delete_ptr), \ + TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_sort), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_shallow_sk_dup), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_num), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_value), \ diff --git a/tests/api/test_ossl_x509_name.c b/tests/api/test_ossl_x509_name.c index 22525afb494..5ddc7582dce 100644 --- a/tests/api/test_ossl_x509_name.c +++ b/tests/api/test_ossl_x509_name.c @@ -931,6 +931,96 @@ int test_wolfSSL_X509_NAME_ENTRY(void) return EXPECT_RESULT(); } +#ifdef OPENSSL_ALL +/* Orders entries by the first byte of their value. */ +static int test_name_entry_cmp(const X509_NAME_ENTRY* const* a, + const X509_NAME_ENTRY* const* b) +{ + return (int)ASN1_STRING_get0_data( + X509_NAME_ENTRY_get_data((X509_NAME_ENTRY*)*a))[0] - + (int)ASN1_STRING_get0_data( + X509_NAME_ENTRY_get_data((X509_NAME_ENTRY*)*b))[0]; +} + +static int test_name_entry_cmp_rev(const X509_NAME_ENTRY* const* a, + const X509_NAME_ENTRY* const* b) +{ + return test_name_entry_cmp(b, a); +} +#endif + +int test_wolfSSL_sk_X509_NAME_ENTRY_sort(void) +{ + EXPECT_DECLS; +#ifdef OPENSSL_ALL + static const char* vals[] = { "c1", "a1", "d", "b", "a2", "c2" }; + static const char* sorted[] = { "a1", "a2", "b", "c1", "c2", "d" }; + X509_NAME_ENTRY* ents[6]; + STACK_OF(X509_NAME_ENTRY)* sk = NULL; + X509_NAME_ENTRY* e; + int i; + + XMEMSET(ents, 0, sizeof(ents)); + for (i = 0; i < 6; i++) { + ExpectNotNull(ents[i] = X509_NAME_ENTRY_create_by_txt(NULL, + "commonName", MBSTRING_UTF8, (const byte*)vals[i], + (int)XSTRLEN(vals[i]))); + } + + /* NULL stack, empty stack and no comparator. */ + sk_X509_NAME_ENTRY_sort(NULL); + ExpectTrue(sk_X509_NAME_ENTRY_set_cmp_func(NULL, test_name_entry_cmp) == + NULL); + ExpectNotNull(sk = sk_X509_NAME_ENTRY_new(NULL)); + sk_X509_NAME_ENTRY_sort(sk); + for (i = 0; i < 6; i++) { + ExpectIntGT(sk_X509_NAME_ENTRY_push(sk, ents[i]), 0); + } + sk_X509_NAME_ENTRY_sort(sk); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 0), ents[0]); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 5), ents[5]); + + /* Stable sort, equal entries keep their order. */ + ExpectTrue(sk_X509_NAME_ENTRY_set_cmp_func(sk, test_name_entry_cmp) == + NULL); + sk_X509_NAME_ENTRY_sort(sk); + ExpectIntEQ(sk_X509_NAME_ENTRY_num(sk), 6); + for (i = 0; i < 6; i++) { + ExpectNotNull(e = sk_X509_NAME_ENTRY_value(sk, i)); + ExpectIntEQ(XMEMCMP(ASN1_STRING_get0_data(X509_NAME_ENTRY_get_data(e)), + sorted[i], XSTRLEN(sorted[i])), 0); + } + ExpectTrue(sk_X509_NAME_ENTRY_set_cmp_func(sk, test_name_entry_cmp_rev) + == test_name_entry_cmp); + sk_X509_NAME_ENTRY_sort(sk); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 0), ents[2]); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 5), ents[4]); + + /* The comparator survives removing the first node. */ + ExpectPtrEq(wolfSSL_sk_delete(sk, 0), ents[2]); + ExpectIntGT(sk_X509_NAME_ENTRY_push(sk, ents[2]), 0); + sk_X509_NAME_ENTRY_sort(sk); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 0), ents[2]); + sk_X509_NAME_ENTRY_free(sk); + sk = NULL; + + /* Comparator given to new(). */ + ExpectNotNull(sk = sk_X509_NAME_ENTRY_new(test_name_entry_cmp)); + for (i = 0; i < 6; i++) { + ExpectIntGT(sk_X509_NAME_ENTRY_push(sk, ents[5 - i]), 0); + } + sk_X509_NAME_ENTRY_sort(sk); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 0), ents[4]); + ExpectPtrEq(sk_X509_NAME_ENTRY_value(sk, 5), ents[2]); + sk_X509_NAME_ENTRY_free(sk); + + for (i = 0; i < 6; i++) { + X509_NAME_ENTRY_free(ents[i]); + } +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_NAME_ENTRY_dup(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_name.h b/tests/api/test_ossl_x509_name.h index c973a3fff23..632159c3dae 100644 --- a/tests/api/test_ossl_x509_name.h +++ b/tests/api/test_ossl_x509_name.h @@ -29,6 +29,7 @@ int test_wolfSSL_X509_NAME(void); int test_wolfSSL_X509_NAME_hash(void); int test_wolfSSL_X509_NAME_print_ex(void); int test_wolfSSL_X509_NAME_ENTRY(void); +int test_wolfSSL_sk_X509_NAME_ENTRY_sort(void); int test_wolfSSL_X509_NAME_ENTRY_dup(void); int test_wolfSSL_X509_NAME_ENTRY_octet_string(void); int test_wolfSSL_X509_NAME_ENTRY_get_object(void); @@ -40,6 +41,7 @@ int test_wolfSSL_X509_NAME_ENTRY_shared_object(void); TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_hash), \ TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_print_ex), \ TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_ENTRY), \ + TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_sk_X509_NAME_ENTRY_sort), \ TEST_DECL_GROUP("ossl_x509_name", test_wolfSSL_X509_NAME_ENTRY_dup), \ TEST_DECL_GROUP("ossl_x509_name", \ test_wolfSSL_X509_NAME_ENTRY_octet_string), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ad6efa0cf3e..347a42cbba2 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6262,6 +6262,7 @@ struct WOLFSSL_STACK { #if defined(OPENSSL_ALL) wolf_sk_hash_cb hash_fn; unsigned long hash; + wolf_sk_compare_cb comp; /* used by sort */ #endif union { diff --git a/wolfssl/openssl/compat_types.h b/wolfssl/openssl/compat_types.h index 9bddc31a530..2ca5f6b9b44 100644 --- a/wolfssl/openssl/compat_types.h +++ b/wolfssl/openssl/compat_types.h @@ -66,5 +66,7 @@ typedef WOLFSSL_EVP_PKEY_CTX EVP_PKEY_CTX; #endif /* !OPENSSL_COEXIST && (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL) */ typedef unsigned long (*wolf_sk_hash_cb) (const void *v); +typedef int (*wolf_sk_compare_cb) (const void* const* a, + const void* const* b); #endif /* !WOLFSSL_OPENSSL_COMPAT_TYPES_H_ */ diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index df8c9cd562e..654e357ffc3 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -672,6 +672,9 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define sk_X509_dup wolfSSL_shallow_sk_dup #define sk_X509_delete(sk, i) \ ((WOLFSSL_X509*)wolfSSL_sk_delete((sk), (i))) +#define sk_X509_delete_ptr(sk, x) \ + ((WOLFSSL_X509*)wolfSSL_sk_delete_ptr((sk), (x))) +#define sk_X509_insert(sk, x, i) wolfSSL_sk_insert((sk), (x), (i)) #define sk_X509_free wolfSSL_sk_X509_free #define X509_chain_up_ref wolfSSL_X509_chain_up_ref @@ -768,6 +771,8 @@ typedef WOLFSSL_X509_NAME_ENTRY X509_NAME_ENTRY; #define sk_X509_NAME_ENTRY_value wolfSSL_sk_X509_NAME_ENTRY_value #define sk_X509_NAME_ENTRY_free wolfSSL_sk_X509_NAME_ENTRY_free #define sk_X509_NAME_ENTRY_pop_free wolfSSL_sk_X509_NAME_ENTRY_pop_free +#define sk_X509_NAME_ENTRY_set_cmp_func wolfSSL_sk_X509_NAME_ENTRY_set_cmp_func +#define sk_X509_NAME_ENTRY_sort wolfSSL_sk_X509_NAME_ENTRY_sort #define X509_V_FLAG_CRL_CHECK WOLFSSL_X509_V_FLAG_CRL_CHECK #define X509_V_FLAG_CRL_CHECK_ALL WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL @@ -1355,6 +1360,8 @@ typedef wolfSSL_custom_ext_parse_cb custom_ext_parse_cb; #define sk_value wolfSSL_sk_value #define sk_ASN1_OBJECT_value wolfSSL_sk_value #define OPENSSL_sk_value wolfSSL_sk_value +#define OPENSSL_sk_set_cmp_func wolfSSL_sk_set_cmp_func +#define OPENSSL_sk_sort wolfSSL_sk_sort #define d2i_PKCS12_bio wolfSSL_d2i_PKCS12_bio #define d2i_PKCS12_fp wolfSSL_d2i_PKCS12_fp diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 3b1cd7aa5a0..bd9905ed527 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2137,6 +2137,12 @@ WOLFSSL_API int wolfSSL_sk_push(WOLFSSL_STACK *st, const void *data); WOLFSSL_API int wolfSSL_sk_insert(WOLFSSL_STACK *sk, const void *data, int idx); WOLFSSL_API void* wolfSSL_sk_pop(WOLFSSL_STACK* sk); WOLFSSL_API void* wolfSSL_sk_delete(WOLFSSL_STACK* sk, int idx); +WOLFSSL_API void* wolfSSL_sk_delete_ptr(WOLFSSL_STACK* sk, const void* data); +#ifdef OPENSSL_ALL +WOLFSSL_API wolf_sk_compare_cb wolfSSL_sk_set_cmp_func(WOLFSSL_STACK* sk, + wolf_sk_compare_cb cb); +WOLFSSL_API void wolfSSL_sk_sort(WOLFSSL_STACK* sk); +#endif #endif #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(OPENSSL_ALL) @@ -6064,6 +6070,16 @@ WOLFSSL_API void wolfSSL_sk_X509_NAME_ENTRY_free(WOLF_STACK_OF(WOLFSSL_X509_NAME WOLFSSL_API void wolfSSL_sk_X509_NAME_ENTRY_pop_free( WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, void (*f)(WOLFSSL_X509_NAME_ENTRY*)); +#ifdef OPENSSL_ALL +typedef WOLF_SK_COMPARE_CB(WOLFSSL_X509_NAME_ENTRY, + wolf_sk_X509_NAME_ENTRY_cmp_cb); +WOLFSSL_API wolf_sk_X509_NAME_ENTRY_cmp_cb + wolfSSL_sk_X509_NAME_ENTRY_set_cmp_func( + WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk, + wolf_sk_X509_NAME_ENTRY_cmp_cb cb); +WOLFSSL_API void wolfSSL_sk_X509_NAME_ENTRY_sort( + WOLF_STACK_OF(WOLFSSL_X509_NAME_ENTRY)* sk); +#endif WOLFSSL_API int wolfSSL_sk_X509_OBJECT_num(const WOLF_STACK_OF(WOLFSSL_X509_OBJECT) *s); From db808ad7ba1c1c58bb0d7c8be66450dc93a6d107 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:21:55 +0000 Subject: [PATCH 14/31] Fix the X509_STORE default paths and add lookup by subject X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT) asks for the default certificate directory. It failed on the NULL path. Use $SSL_CERT_DIR as OpenSSL does, and add nothing when it is not set, since there is no built-in directory. X509_STORE_set_default_paths failed when neither $SSL_CERT_FILE nor $SSL_CERT_DIR was set, or when they did not load. OpenSSL treats the default locations as optional and returns success. Do the same: load what is set and ignore what does not load. A NULL store still fails. Add X509_STORE_CTX_get_by_subject. Applications look up a cert in the store by subject name, as xmlsec does to find the issuer of a CRL. Search the certs added to the store, then the CAs loaded into the cert manager. Return 1 with a reference in the object, 0 when none is found. CRLs are not looked up. X509_STORE_get_by_subject was a stub and now calls it, as in OpenSSL. X509_OBJECT_free_contents was a stub, so a caller that releases the object with it, as the OpenSSL 1.0 API does, leaked that reference. Implement it: free what the object holds by its type and leave the object empty (type X509_LU_NONE), so a later X509_OBJECT_free does not free it again. X509_OBJECT_free uses it. The OCSP test called it on an object of the store's list, which would free the store's cert; call it on NULL there. The cert manager keeps the DER of its CAs only with WOLFSSL_SIGNER_DER_CERT, which only OPENSSL_ALL set, so X509_STORE_CTX_get_by_subject() could not find them in other OPENSSL_EXTRA builds. Set it in every OPENSSL_EXTRA build: one DER copy per loaded CA, and Signer grows, so a PERSIST_CERT_CACHE file of an older such build is rejected. As with OPENSSL_ALL, those builds then also have X509_STORE_get1_certs() and wolfSSL_CertManagerGetCerts(), and X509_STORE_CTX_get1_issuer() and the issuer that X509_verify_cert() puts on top of its chain come from the cert manager. --- src/x509.c | 44 ++++++++----- src/x509_str.c | 86 +++++++++++++++++++++--- tests/api.c | 5 +- tests/api/test_ossl_x509_str.c | 117 +++++++++++++++++++++++++++++++++ tests/api/test_ossl_x509_str.h | 3 + wolfssl/openssl/ssl.h | 1 + wolfssl/ssl.h | 5 +- wolfssl/wolfcrypt/settings.h | 2 +- 8 files changed, 231 insertions(+), 32 deletions(-) diff --git a/src/x509.c b/src/x509.c index bbf391a9b8b..9f549e5c324 100644 --- a/src/x509.c +++ b/src/x509.c @@ -9485,6 +9485,18 @@ int wolfSSL_X509_LOOKUP_ctrl(WOLFSSL_X509_LOOKUP *ctx, int cmd, case WOLFSSL_X509_L_ADD_DIR: /* store directory location to use it later */ #if !defined(NO_WOLFSSL_DIR) + if (argc == NULL && argl == WOLFSSL_FILETYPE_DEFAULT) { + /* Default dir: $SSL_CERT_DIR, as OpenSSL. There is no + * built-in one, so add nothing without it. */ + #ifdef XGETENV + argc = XGETENV("SSL_CERT_DIR"); + #endif + argl = WOLFSSL_FILETYPE_PEM; + if (argc == NULL || *argc == '\0') { + lret = WOLFSSL_SUCCESS; + break; + } + } lret = x509AddCertDir(ctx->dirs, argc, argl); #else (void)x509AddCertDir; @@ -12052,13 +12064,23 @@ int wolfSSL_X509_VERIFY_PARAM_set1_ip(WOLFSSL_X509_VERIFY_PARAM* param, return ret; } -#ifndef NO_WOLFSSL_STUB +/* Frees what the object holds and leaves it empty. */ void wolfSSL_X509_OBJECT_free_contents(WOLFSSL_X509_OBJECT* obj) { - (void)obj; - WOLFSSL_STUB("X509_OBJECT_free_contents"); -} + WOLFSSL_ENTER("wolfSSL_X509_OBJECT_free_contents"); + if (obj == NULL) + return; + if (obj->type == WOLFSSL_X509_LU_X509) { + wolfSSL_X509_free(obj->data.x509); + } +#ifdef HAVE_CRL + else if (obj->type == WOLFSSL_X509_LU_CRL) { + wolfSSL_X509_CRL_free(obj->data.crl); + } #endif + obj->type = WOLFSSL_X509_LU_NONE; + obj->data.ptr = NULL; +} #ifndef NO_ASN_TIME int wolfSSL_X509_cmp_current_time(const WOLFSSL_ASN1_TIME* asnTime) @@ -17414,19 +17436,7 @@ void wolfSSL_X509_OBJECT_free(WOLFSSL_X509_OBJECT *obj) { WOLFSSL_ENTER("wolfSSL_X509_OBJECT_free"); if (obj != NULL) { - if (obj->type == WOLFSSL_X509_LU_X509) { - wolfSSL_X509_free(obj->data.x509); - } - #ifdef HAVE_CRL - else if (obj->type == WOLFSSL_X509_LU_CRL) { - wolfSSL_X509_CRL_free(obj->data.crl); - } - #endif - else { - /* We don't free as this will point to - * store->cm->crl which we don't own */ - WOLFSSL_MSG("Not free'ing CRL in WOLFSSL_X509_OBJECT"); - } + wolfSSL_X509_OBJECT_free_contents(obj); XFREE(obj, NULL, DYNAMIC_TYPE_OPENSSL); } } diff --git a/src/x509_str.c b/src/x509_str.c index 3e65d833826..e2e74d0d275 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -1697,19 +1697,76 @@ WOLFSSL_X509_STORE_CTX *wolfSSL_X509_STORE_CTX_get0_parent_ctx( WOLFSSL_STUB("wolfSSL_X509_STORE_CTX_get0_parent_ctx"); return NULL; } +#endif -int wolfSSL_X509_STORE_get_by_subject(WOLFSSL_X509_STORE_CTX* ctx, int idx, - WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj) +/* Returns the first cert in certs with the subject name. */ +static WOLFSSL_X509* X509StoreFindBySubject(WOLFSSL_STACK* certs, + const WOLFSSL_X509_NAME* name) { - (void)ctx; - (void)idx; - (void)name; - (void)obj; - WOLFSSL_STUB("X509_STORE_get_by_subject"); - return 0; + int i; + + for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(certs, i); + if ((x509 != NULL) && + (wolfSSL_X509_NAME_cmp(&x509->subject, name) == 0)) { + return x509; + } + } + return NULL; } + +/* Looks up a cert in the store by subject name. CRLs are not looked up. + * Returns 1 and puts a reference in obj when found, 0 otherwise. */ +int wolfSSL_X509_STORE_CTX_get_by_subject(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_LOOKUP_TYPE type, WOLFSSL_X509_NAME* name, + WOLFSSL_X509_OBJECT* obj) +{ + WOLFSSL_X509* x509; +#if defined(WOLFSSL_SIGNER_DER_CERT) && !defined(NO_FILESYSTEM) + WOLF_STACK_OF(WOLFSSL_X509)* sk; #endif + WOLFSSL_ENTER("wolfSSL_X509_STORE_CTX_get_by_subject"); + + if ((ctx == NULL) || (ctx->store == NULL) || (name == NULL) || + (obj == NULL) || (type != WOLFSSL_X509_LU_X509)) { + return 0; + } + + x509 = X509StoreFindBySubject(ctx->store->trusted, name); + if (x509 == NULL) { + x509 = X509StoreFindBySubject(ctx->store->certs, name); + } + if ((x509 != NULL) && (wolfSSL_X509_up_ref(x509) != WOLFSSL_SUCCESS)) { + return 0; + } +#if defined(WOLFSSL_SIGNER_DER_CERT) && !defined(NO_FILESYSTEM) + /* CAs loaded into the cert manager from files. */ + if (x509 == NULL) { + sk = wolfSSL_X509_STORE_get1_certs(ctx, name); + x509 = wolfSSL_sk_X509_value(sk, 0); + if ((x509 != NULL) && (wolfSSL_X509_up_ref(x509) != WOLFSSL_SUCCESS)) { + x509 = NULL; + } + wolfSSL_sk_X509_pop_free(sk, NULL); + } +#endif + if (x509 == NULL) { + return 0; + } + + obj->type = WOLFSSL_X509_LU_X509; + obj->data.x509 = x509; + return 1; +} + +int wolfSSL_X509_STORE_get_by_subject(WOLFSSL_X509_STORE_CTX* ctx, int type, + WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj) +{ + return wolfSSL_X509_STORE_CTX_get_by_subject(ctx, + (WOLFSSL_X509_LOOKUP_TYPE)type, name, obj); +} + WOLFSSL_X509_VERIFY_PARAM *wolfSSL_X509_STORE_CTX_get0_param( WOLFSSL_X509_STORE_CTX *ctx) { @@ -2580,10 +2637,21 @@ int wolfSSL_X509_STORE_set_default_paths(WOLFSSL_X509_STORE *str) WOLFSSL_ENTER("wolfSSL_X509_STORE_set_default_paths"); + if (str == NULL) + return WOLFSSL_FAILURE; + certFile = wc_strdup_ex(XGETENV("SSL_CERT_FILE"), DYNAMIC_TYPE_TMP_BUFFER); certDir = wc_strdup_ex(XGETENV("SSL_CERT_DIR"), DYNAMIC_TYPE_TMP_BUFFER); - ret = wolfSSL_X509_STORE_load_locations(str, certFile, certDir); + /* As OpenSSL: the default locations are optional. There is no built-in + * bundle, and one that does not load is not an error. */ + ret = WOLFSSL_SUCCESS; + if ((certFile != NULL || certDir != NULL) && + wolfSSL_X509_STORE_load_locations(str, certFile, certDir) != + WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Default locations not loaded"); + wolfSSL_ERR_clear_error(); + } XFREE(certFile, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(certDir, NULL, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/tests/api.c b/tests/api.c index e6950004a6b..9dbfb03a6f7 100644 --- a/tests/api.c +++ b/tests/api.c @@ -27950,10 +27950,7 @@ static int test_wolfSSL_OCSP_REQ_CTX(void) X509_LU_X509, X509_get_issuer_name(cert))); ExpectNotNull(issuer = X509_OBJECT_get0_X509(x509_obj)); ExpectTrue(wolfSSL_X509_OBJECT_get_type(NULL) == WOLFSSL_X509_LU_NONE); -#ifndef NO_WOLFSSL_STUB - /* Not implemented and not in OpenSSL 1.1.0+ */ - wolfSSL_X509_OBJECT_free_contents(x509_obj); -#endif + wolfSSL_X509_OBJECT_free_contents(NULL); wolfSSL_X509_OBJECT_free(NULL); ExpectNotNull(req = OCSP_REQUEST_new()); diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 367f39a76f9..b8e5b53f81f 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -4210,6 +4210,16 @@ int test_wolfSSL_X509_STORE_load_locations(void) ExpectIntEQ(wolfSSL_CertManagerLoadCA(store->cm, ca_file, NULL), WOLFSSL_SUCCESS); + /* The default dir is optional. */ + { + X509_LOOKUP* lookup = NULL; + + ExpectNotNull(lookup = X509_STORE_add_lookup(store, + X509_LOOKUP_hash_dir())); + ExpectIntEQ(X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT), + WOLFSSL_SUCCESS); + } + /* Test bad arguments */ ExpectIntEQ(X509_STORE_load_locations(NULL, ca_file, NULL), WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); @@ -4247,6 +4257,16 @@ int test_wolfSSL_X509_STORE_load_locations(void) ExpectIntEQ(setenv("SSL_CERT_FILE", client_pem_file, 1), 0); ExpectIntEQ(setenv("SSL_CERT_DIR", certs_path, 1), 0); ExpectIntEQ(X509_STORE_set_default_paths(store), WOLFSSL_SUCCESS); + /* Default locations are optional, as with OpenSSL. */ + ExpectIntEQ(X509_STORE_set_default_paths(NULL), + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + ExpectIntEQ(unsetenv("SSL_CERT_FILE"), 0); + ExpectIntEQ(unsetenv("SSL_CERT_DIR"), 0); + ExpectIntEQ(X509_STORE_set_default_paths(store), WOLFSSL_SUCCESS); + ExpectIntEQ(setenv("SSL_CERT_DIR", bad_path, 1), 0); + ExpectIntEQ(X509_STORE_set_default_paths(store), WOLFSSL_SUCCESS); + ExpectIntEQ(setenv("SSL_CERT_FILE", client_pem_file, 1), 0); + ExpectIntEQ(setenv("SSL_CERT_DIR", certs_path, 1), 0); #endif #if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE) @@ -4472,6 +4492,103 @@ int test_wolfSSL_X509_STORE_get1_certs(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_STORE_CTX_get_by_subject(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + X509_STORE_CTX* ctx = NULL; + X509_STORE* store = NULL; + X509* ca = NULL; + X509* svr = NULL; + X509* cli = NULL; + X509_OBJECT* obj = NULL; + + ExpectNotNull(ca = X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(svr = X509_load_certificate_file(svrCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(cli = X509_load_certificate_file(cliCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectNotNull(store = X509_STORE_new()); + ExpectNotNull(obj = X509_OBJECT_new()); + + /* Errors */ + ExpectIntEQ(X509_STORE_CTX_get_by_subject(NULL, X509_LU_X509, + X509_get_subject_name(ca), obj), 0); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(ca), obj), 0); + + /* Trusted self-signed cert and untrusted intermediate. */ + ExpectIntEQ(X509_STORE_add_cert(store, ca), 1); + ExpectIntEQ(X509_STORE_add_cert(store, svr), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, NULL, NULL), 1); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, NULL, obj), + 0); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(ca), NULL), 0); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_CRL, + X509_get_subject_name(ca), obj), 0); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(cli), obj), 0); + ExpectTrue(X509_OBJECT_get_type(obj) == X509_LU_NONE); + + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(ca), obj), 1); + ExpectTrue(X509_OBJECT_get_type(obj) == X509_LU_X509); + ExpectPtrEq(X509_OBJECT_get0_X509(obj), ca); + X509_OBJECT_free(obj); + obj = NULL; + + /* free_contents releases the reference and empties the object */ + ExpectNotNull(obj = X509_OBJECT_new()); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(ca), obj), 1); + X509_OBJECT_free_contents(obj); + ExpectTrue(X509_OBJECT_get_type(obj) == X509_LU_NONE); + ExpectNull(X509_OBJECT_get0_X509(obj)); + X509_OBJECT_free_contents(obj); + X509_OBJECT_free(obj); + obj = NULL; + + ExpectNotNull(obj = X509_OBJECT_new()); + ExpectIntEQ(X509_STORE_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(svr), obj), 1); + ExpectPtrEq(X509_OBJECT_get0_X509(obj), svr); + X509_STORE_CTX_cleanup(ctx); + X509_STORE_free(store); + store = NULL; + X509_free(svr); + svr = NULL; + + /* The object holds the last reference to the cert. */ + ExpectIntEQ(X509_check_issued(ca, X509_OBJECT_get0_X509(obj)), X509_V_OK); + X509_OBJECT_free(obj); + obj = NULL; + +#ifdef WOLFSSL_SIGNER_DER_CERT + /* CA loaded from a file into the cert manager. */ + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_load_locations(store, caCertFile, NULL), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, NULL, NULL), 1); + ExpectNotNull(obj = X509_OBJECT_new()); + ExpectIntEQ(X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, + X509_get_subject_name(ca), obj), 1); + ExpectIntEQ(X509_cmp(X509_OBJECT_get0_X509(obj), ca), 0); + X509_STORE_CTX_cleanup(ctx); + X509_STORE_free(store); + ExpectNotNull(X509_get_subject_name(X509_OBJECT_get0_X509(obj))); +#endif + + X509_OBJECT_free(obj); + X509_STORE_CTX_free(ctx); + X509_free(cli); + X509_free(svr); + X509_free(ca); +#endif + return EXPECT_RESULT(); +} + #if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ defined(WOLFSSL_LOCAL_X509_STORE) && \ (defined(OPENSSL_ALL) || defined(WOLFSSL_QT)) && defined(HAVE_CRL) diff --git a/tests/api/test_ossl_x509_str.h b/tests/api/test_ossl_x509_str.h index d69340dd8cd..a58655ca058 100644 --- a/tests/api/test_ossl_x509_str.h +++ b/tests/api/test_ossl_x509_str.h @@ -55,6 +55,7 @@ int test_wolfSSL_X509_STORE_load_locations(void); int test_X509_STORE_get0_objects(void); int test_X509_STORE_get0_objects_borrowed_crl(void); int test_wolfSSL_X509_STORE_get1_certs(void); +int test_wolfSSL_X509_STORE_CTX_get_by_subject(void); int test_wolfSSL_X509_STORE_set_get_crl(void); int test_wolfSSL_X509_STORE_CTX_set0_crls(void); int test_wolfSSL_X509_STORE_add_crl_oom(void); @@ -112,6 +113,8 @@ int test_wolfSSL_CTX_set_cert_store(void); TEST_DECL_GROUP("ossl_x509_store", \ test_X509_STORE_get0_objects_borrowed_crl), \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_get1_certs), \ + TEST_DECL_GROUP("ossl_x509_store", \ + test_wolfSSL_X509_STORE_CTX_get_by_subject), \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_set_get_crl), \ TEST_DECL_GROUP("ossl_x509_store", \ test_wolfSSL_X509_STORE_CTX_set0_crls), \ diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 654e357ffc3..005534f0a74 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -840,6 +840,7 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_STORE_set_flags wolfSSL_X509_STORE_set_flags #define X509_STORE_get1_certs wolfSSL_X509_STORE_get1_certs #define X509_STORE_get_by_subject wolfSSL_X509_STORE_get_by_subject +#define X509_STORE_CTX_get_by_subject wolfSSL_X509_STORE_CTX_get_by_subject #define X509_STORE_set_ex_data wolfSSL_X509_STORE_set_ex_data #define X509_STORE_get_ex_data wolfSSL_X509_STORE_get_ex_data #define X509_STORE_get0_param wolfSSL_X509_STORE_get0_param diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index bd9905ed527..c944e42a764 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2616,7 +2616,10 @@ WOLFSSL_API WOLFSSL_X509_STORE_CTX *wolfSSL_X509_STORE_CTX_get0_parent_ctx( WOLFSSL_API int wolfSSL_X509_STORE_set_flags(WOLFSSL_X509_STORE* store, unsigned long flag); WOLFSSL_API int wolfSSL_X509_STORE_get_by_subject(WOLFSSL_X509_STORE_CTX* ctx, - int idx, WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj); + int type, WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj); +WOLFSSL_API int wolfSSL_X509_STORE_CTX_get_by_subject( + WOLFSSL_X509_STORE_CTX* ctx, WOLFSSL_X509_LOOKUP_TYPE type, + WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj); WOLFSSL_API WOLFSSL_X509_VERIFY_PARAM *wolfSSL_X509_STORE_CTX_get0_param( WOLFSSL_X509_STORE_CTX *ctx); WOLFSSL_API int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 6564f4aa52a..b4756d1f57a 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5254,7 +5254,7 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." /* X509_STORE_get0_objects() can only enumerate a store's certificates when the * CA table keeps their DER. Costs one DER copy per loaded CA, so it stays * optional; define WOLFSSL_NO_SIGNER_DER_CERT to opt back out. */ -#if defined(OPENSSL_ALL) && !defined(WOLFSSL_NO_SIGNER_DER_CERT) +#if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_SIGNER_DER_CERT) #undef WOLFSSL_SIGNER_DER_CERT #define WOLFSSL_SIGNER_DER_CERT #endif From d57c4896590cce84223e88413f29065bbd8e1a05 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:22:04 +0000 Subject: [PATCH 15/31] Keep the PKCS#12 friendlyName and localKeyId of the key cert OpenSSL's PKCS12_parse keeps the friendlyName and localKeyId bag attributes in the certs, and X509_alias_get0 and X509_keyid_get0 return them. Applications name the key from them, as xmlsec does. wc_PKCS12_parse_ex2, internal to the library, also returns the attributes of the cert that matches the key. PKCS12_parse keeps them in that cert: the friendlyName when it is ASCII, as the low byte of each BMPString character, and the localKeyId bytes. Both are NUL terminated after their length, as ASN1_STRING data is. Any other friendlyName is left out, where OpenSSL converts it to UTF-8. xmlsec then names the key from X509_keyid_get0. Add X509_alias_get0 and X509_keyid_get0. Only the key cert gets them; OpenSSL also sets them in the CA certs. X509_dup does not copy them, as in OpenSSL. Malformed bag attributes are skipped as before; OpenSSL's PKCS12_parse rejects the file. --- src/internal.c | 4 + src/ssl_p7p12.c | 95 +++++++- src/x509.c | 30 +++ tests/api/test_ossl_p7p12.c | 471 ++++++++++++++++++++++++++++++++++++ tests/api/test_ossl_p7p12.h | 8 +- wolfcrypt/src/pkcs12.c | 180 ++++++++++++++ wolfssl/internal.h | 7 + wolfssl/openssl/ssl.h | 2 + wolfssl/ssl.h | 6 + wolfssl/wolfcrypt/pkcs12.h | 5 + 10 files changed, 797 insertions(+), 11 deletions(-) diff --git a/src/internal.c b/src/internal.c index c03503a6469..b1283fb3c8f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6055,6 +6055,10 @@ static void FreeX509Contents(WOLFSSL_X509* x509) if (x509->serialNumber != NULL) { wolfSSL_ASN1_INTEGER_free(x509->serialNumber); } + XFREE(x509->alias, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->alias = NULL; + XFREE(x509->keyid, x509->heap, DYNAMIC_TYPE_X509_EXT); + x509->keyid = NULL; #endif if (x509->extKeyUsageSrc != NULL) { XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); diff --git a/src/ssl_p7p12.c b/src/ssl_p7p12.c index d2bc45fd99d..9642c481051 100644 --- a/src/ssl_p7p12.c +++ b/src/ssl_p7p12.c @@ -1921,6 +1921,73 @@ WC_PKCS12* wolfSSL_PKCS12_create(char* pass, char* name, WOLFSSL_EVP_PKEY* pkey, } +/* Get a BMPString as ASCII, the low byte of each character. + * + * @param [in] in BMPString, UCS-2 big endian. + * @param [in] inSz Length of in in bytes. + * @param [out] out Buffer for ASCII. NULL to get the length. + * @return Length of ASCII string on success. + * @return -1 when in has an odd length or a character that is not ASCII. + */ +static int wolfssl_bmp_to_ascii(const byte* in, word32 inSz, byte* out) +{ + word32 i; + + if ((inSz % 2) != 0) { + return WOLFSSL_FATAL_ERROR; + } + for (i = 0; i < inSz; i += 2) { + if ((in[i] != 0) || (in[i + 1] >= 0x80)) { + return WOLFSSL_FATAL_ERROR; + } + if (out != NULL) { + out[i / 2] = in[i + 1]; + } + } + + return (int)(inSz / 2); +} + +/* Keep the friendlyName and the localKeyId of the PKCS#12 bag of the cert, as + * OpenSSL does with X509_alias_set1 and X509_keyid_set1. + * A friendlyName that is not ASCII is left out. */ +static int wolfssl_x509_set_pkcs12_attrs(WOLFSSL_X509* x509, + const byte* name, word32 nameSz, const byte* keyId, word32 keyIdSz) +{ + int ret = 1; + int len; + + if (name != NULL) { + len = wolfssl_bmp_to_ascii(name, nameSz, NULL); + if (len >= 0) { + x509->alias = (byte*)XMALLOC((size_t)len + 1, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->alias == NULL) { + ret = 0; + } + else { + (void)wolfssl_bmp_to_ascii(name, nameSz, x509->alias); + x509->alias[len] = '\0'; + x509->aliasSz = len; + } + } + } + if ((ret == 1) && (keyId != NULL)) { + x509->keyid = (byte*)XMALLOC((size_t)keyIdSz + 1, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->keyid == NULL) { + ret = 0; + } + else { + XMEMCPY(x509->keyid, keyId, keyIdSz); + x509->keyid[keyIdSz] = '\0'; + x509->keyidSz = (int)keyIdSz; + } + } + + return ret; +} + /* return WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE on failure */ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, WOLFSSL_EVP_PKEY** pkey, WOLFSSL_X509** cert, @@ -1932,6 +1999,10 @@ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, word32 certDataSz; byte* pk = NULL; word32 pkSz; + byte* name = NULL; + word32 nameSz = 0; + byte* keyId = NULL; + word32 keyIdSz = 0; WC_DerCertList* certList = NULL; WC_DECLARE_VAR(DeCert, DecodedCert, 1, 0); @@ -1949,14 +2020,8 @@ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, heap = wc_PKCS12_GetHeap(pkcs12); - if (ca == NULL) { - ret = wc_PKCS12_parse(pkcs12, psw, &pk, &pkSz, &certData, &certDataSz, - NULL); - } - else { - ret = wc_PKCS12_parse(pkcs12, psw, &pk, &pkSz, &certData, &certDataSz, - &certList); - } + ret = wc_PKCS12_parse_ex2(pkcs12, psw, &pk, &pkSz, &certData, &certDataSz, + (ca == NULL) ? NULL : &certList, 0, &name, &nameSz, &keyId, &keyIdSz); if (ret < 0) { WOLFSSL_LEAVE("wolfSSL_PKCS12_parse", ret); return WOLFSSL_FAILURE; @@ -1967,6 +2032,8 @@ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, DYNAMIC_TYPE_DCERT); if (DeCert == NULL) { WOLFSSL_MSG("out of memory"); + XFREE(name, heap, DYNAMIC_TYPE_PKCS); + XFREE(keyId, heap, DYNAMIC_TYPE_PKCS); return WOLFSSL_FAILURE; } #endif @@ -2090,6 +2157,16 @@ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, } FreeDecodedCert(DeCert); XFREE(certData, heap, DYNAMIC_TYPE_PKCS); + if (wolfssl_x509_set_pkcs12_attrs(*cert, name, nameSz, keyId, + keyIdSz) != 1) { + XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY); + if (ca != NULL) { + wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL; + } + wolfSSL_X509_free(*cert); *cert = NULL; + ret = WOLFSSL_FAILURE; + goto out; + } } @@ -2150,6 +2227,8 @@ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, out: + XFREE(name, heap, DYNAMIC_TYPE_PKCS); + XFREE(keyId, heap, DYNAMIC_TYPE_PKCS); WC_FREE_VAR_EX(DeCert, heap, DYNAMIC_TYPE_DCERT); return ret; diff --git a/src/x509.c b/src/x509.c index 9f549e5c324..47d8136849c 100644 --- a/src/x509.c +++ b/src/x509.c @@ -7393,6 +7393,36 @@ int wolfSSL_X509_cmp(const WOLFSSL_X509 *a, const WOLFSSL_X509 *b) return ret; } + + /* Get the alias of the cert: the friendlyName of its PKCS#12 bag. + * Returns the UTF-8 alias or NULL when the cert has none. */ + unsigned char* wolfSSL_X509_alias_get0(WOLFSSL_X509* x509, int* len) + { + WOLFSSL_ENTER("wolfSSL_X509_alias_get0"); + + if ((x509 == NULL) || (x509->alias == NULL)) { + return NULL; + } + if (len != NULL) { + *len = x509->aliasSz; + } + return x509->alias; + } + + /* Get the key id of the cert: the localKeyId of its PKCS#12 bag. + * Returns the key id or NULL when the cert has none. */ + unsigned char* wolfSSL_X509_keyid_get0(WOLFSSL_X509* x509, int* len) + { + WOLFSSL_ENTER("wolfSSL_X509_keyid_get0"); + + if ((x509 == NULL) || (x509->keyid == NULL)) { + return NULL; + } + if (len != NULL) { + *len = x509->keyidSz; + } + return x509->keyid; + } #endif /* OPENSSL_EXTRA */ #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ diff --git a/tests/api/test_ossl_p7p12.c b/tests/api/test_ossl_p7p12.c index 60eca703497..36cd7e03061 100644 --- a/tests/api/test_ossl_p7p12.c +++ b/tests/api/test_ossl_p7p12.c @@ -2048,3 +2048,474 @@ int test_wolfSSL_PKCS12_create_mac(void) #endif return EXPECT_RESULT(); } +int test_wolfSSL_PKCS12_alias_keyid(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_PKCS12) && !defined(HAVE_FIPS) && \ + !defined(NO_ASN) && !defined(NO_PWDBASED) && defined(HAVE_ECC) && \ + defined(WOLFSSL_AES_256) && defined(HAVE_AES_CBC) && \ + !defined(NO_SHA256) && !defined(NO_HMAC) && !defined(NO_BIO) + /* certs/ecc-key.pem, certs/server-ecc.pem and certs/ca-ecc-cert.pem + * exported by OpenSSL with friendlyName "wolfSSL e-acute euro", CA + * friendlyName "wolfSSL CA" and password "wolfSSL test". */ + static const unsigned char p12Der[] = { + 0x30, 0x82, 0x08, 0x71, 0x02, 0x01, 0x03, 0x30, 0x82, 0x08, 0x27, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, + 0x08, 0x18, 0x04, 0x82, 0x08, 0x14, 0x30, 0x82, 0x08, 0x10, 0x30, 0x82, + 0x06, 0xa2, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x06, 0xa0, 0x82, 0x06, 0x93, 0x30, 0x82, 0x06, 0x8f, 0x02, 0x01, 0x00, + 0x30, 0x82, 0x06, 0x88, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x01, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0xe3, + 0x7a, 0xf6, 0xc3, 0x04, 0x02, 0x3a, 0x27, 0x02, 0x02, 0x08, 0x00, 0x30, + 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05, + 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x01, 0x2a, 0x04, 0x10, 0xfd, 0x28, 0x04, 0xb1, 0xe9, 0xe7, 0x98, 0x9d, + 0x18, 0x42, 0x8f, 0x77, 0xfc, 0x4a, 0xf7, 0xf5, 0x80, 0x82, 0x06, 0x20, + 0x01, 0xf8, 0xc3, 0x28, 0x3e, 0x45, 0x4e, 0x7a, 0xf1, 0x22, 0xa4, 0x7f, + 0xfa, 0x54, 0x72, 0xaa, 0x3c, 0x47, 0x39, 0xd0, 0x01, 0xdc, 0x10, 0x2b, + 0x2c, 0x4c, 0x45, 0xff, 0x7b, 0xd2, 0x20, 0x1e, 0xb4, 0x3a, 0xb1, 0xc4, + 0x9c, 0xa4, 0x07, 0x14, 0x81, 0x4f, 0xdb, 0xd4, 0x58, 0x2b, 0x2d, 0x6c, + 0xfe, 0xec, 0x68, 0x79, 0xc3, 0x15, 0x38, 0x84, 0x26, 0x6f, 0xc9, 0xb9, + 0x7b, 0xbc, 0x91, 0x9e, 0xa9, 0xea, 0xb3, 0x86, 0x13, 0x35, 0xb7, 0x0c, + 0xee, 0xde, 0x4f, 0xb1, 0x9a, 0x41, 0x5b, 0xc9, 0x3c, 0xfe, 0xd0, 0x1a, + 0xf0, 0x13, 0x62, 0xbb, 0xd4, 0x9d, 0xda, 0xca, 0x91, 0xca, 0x7c, 0x16, + 0x46, 0x6b, 0xad, 0x29, 0x76, 0x29, 0x92, 0x70, 0xfd, 0x1f, 0x8d, 0xfb, + 0x55, 0xf6, 0x3d, 0xd6, 0xdd, 0x72, 0x19, 0xb6, 0x7b, 0x52, 0x42, 0x70, + 0xd8, 0x48, 0x6a, 0x70, 0x79, 0x5f, 0xf8, 0x95, 0xef, 0xb9, 0xe0, 0x01, + 0x5b, 0x69, 0xe8, 0xf4, 0xa8, 0x1f, 0xdc, 0x13, 0x95, 0x08, 0xf2, 0x9a, + 0x87, 0xa2, 0x56, 0x0c, 0x69, 0x18, 0x9e, 0xf7, 0x47, 0x17, 0xcf, 0x53, + 0x9d, 0x58, 0x7f, 0x01, 0xf2, 0x03, 0x8c, 0x0f, 0xe9, 0x71, 0x0e, 0x89, + 0x8f, 0x27, 0x62, 0x29, 0x37, 0xdc, 0xd0, 0x2d, 0x31, 0x53, 0x31, 0x0d, + 0x96, 0xd5, 0x82, 0xd4, 0xe9, 0xba, 0xec, 0xe8, 0x55, 0x38, 0xaa, 0xb6, + 0x82, 0x8a, 0xcf, 0x63, 0x6a, 0xb6, 0x15, 0x75, 0x76, 0x77, 0x7e, 0x5b, + 0xae, 0xf2, 0x1d, 0xf8, 0x45, 0x38, 0x67, 0x63, 0x9f, 0x18, 0xb1, 0x85, + 0x65, 0x74, 0x5c, 0xa0, 0x12, 0x6f, 0xed, 0x55, 0xd6, 0x35, 0xbe, 0x8e, + 0x62, 0x57, 0x9f, 0xae, 0x87, 0xee, 0x75, 0x24, 0x6b, 0x21, 0x3b, 0x18, + 0xd6, 0xcc, 0xbe, 0x01, 0x4b, 0x7c, 0x40, 0xfb, 0x35, 0xbc, 0x1e, 0x6d, + 0xfa, 0xc9, 0x2b, 0x35, 0xcd, 0x84, 0xb5, 0x9a, 0x26, 0x11, 0xb2, 0x13, + 0x69, 0x27, 0x4f, 0xf9, 0x08, 0x6d, 0x25, 0x10, 0xfb, 0x88, 0x0c, 0xef, + 0xfb, 0x9e, 0x18, 0x6e, 0x14, 0x2b, 0xe1, 0x4c, 0x14, 0x0f, 0xe8, 0x16, + 0xee, 0x91, 0x1c, 0x79, 0x3a, 0x36, 0x8b, 0x45, 0x3c, 0xdc, 0x53, 0xf5, + 0x60, 0x26, 0xe5, 0xf5, 0x75, 0x32, 0xdd, 0xb0, 0x40, 0x4c, 0x8d, 0xe6, + 0xf3, 0x04, 0x85, 0x89, 0xe6, 0xb0, 0x32, 0xa1, 0x54, 0xc8, 0xe0, 0x48, + 0xfb, 0x57, 0xce, 0xc5, 0xd5, 0xe8, 0xde, 0xc7, 0xfb, 0x1e, 0x21, 0x2d, + 0x87, 0x24, 0x7a, 0xeb, 0x8c, 0x7c, 0x57, 0xb2, 0xcb, 0x37, 0x0e, 0x2e, + 0x06, 0x9a, 0x31, 0x8f, 0x5a, 0x5b, 0x9e, 0xc8, 0x4a, 0xf2, 0x77, 0x58, + 0x42, 0xc5, 0x29, 0x89, 0x11, 0x59, 0x1b, 0xf2, 0xd0, 0xb8, 0x31, 0xee, + 0x01, 0x44, 0x7a, 0x90, 0x76, 0x0f, 0x6d, 0x85, 0xb0, 0xa7, 0x9a, 0x00, + 0x4b, 0xdd, 0x14, 0x22, 0x95, 0x7b, 0x05, 0x19, 0x11, 0xe1, 0xe0, 0xfe, + 0x43, 0x67, 0xb4, 0xd7, 0xc8, 0x87, 0x47, 0x19, 0xfc, 0xd1, 0xe9, 0xc8, + 0xcf, 0xa8, 0x66, 0x0d, 0xd2, 0xd8, 0x64, 0xdc, 0x43, 0xec, 0x74, 0x86, + 0x4f, 0xa6, 0xd7, 0x42, 0x58, 0x35, 0xee, 0x2e, 0xea, 0xb1, 0xad, 0xd1, + 0xc1, 0xa3, 0x4d, 0x99, 0x3f, 0x15, 0x0d, 0x46, 0x78, 0x6a, 0xf9, 0xae, + 0x59, 0x1f, 0x54, 0x8e, 0x90, 0xe6, 0xc9, 0xb7, 0x18, 0x3c, 0x22, 0xa1, + 0x75, 0xa2, 0x11, 0xc0, 0xbe, 0x8d, 0xa4, 0x38, 0x63, 0x6e, 0x62, 0x60, + 0x6b, 0x0b, 0x61, 0x95, 0xe9, 0x30, 0xe5, 0x08, 0x37, 0x21, 0xc7, 0x31, + 0x63, 0x35, 0xd9, 0x7b, 0x87, 0xb8, 0x7d, 0x2c, 0xed, 0x4f, 0x8e, 0xac, + 0xa5, 0xdb, 0xa9, 0x83, 0x5f, 0xc7, 0x8b, 0x7b, 0xf7, 0x08, 0x9e, 0x82, + 0x4b, 0x37, 0x6a, 0x52, 0x6d, 0xc3, 0xbf, 0x4b, 0x81, 0x2d, 0xf9, 0x46, + 0xc4, 0xe6, 0x71, 0x73, 0xb9, 0x3e, 0x7b, 0x7e, 0xc7, 0x13, 0x20, 0xd6, + 0x78, 0xf8, 0xb9, 0x56, 0xa4, 0x06, 0x68, 0x2c, 0x50, 0xf1, 0x07, 0x40, + 0x72, 0x4e, 0xb2, 0x4d, 0xa2, 0x08, 0x31, 0xe2, 0x36, 0xe5, 0xb3, 0x63, + 0x28, 0xd4, 0x99, 0x0d, 0x06, 0x1e, 0x13, 0xba, 0x3f, 0xab, 0xff, 0xab, + 0x69, 0x1b, 0xbc, 0x68, 0xbb, 0xe1, 0xfb, 0x76, 0xf8, 0x97, 0x8a, 0x85, + 0xbe, 0x31, 0x45, 0x4f, 0xb2, 0x6e, 0x18, 0x6e, 0x4a, 0x98, 0x42, 0x7b, + 0x0a, 0x85, 0xc1, 0xba, 0x87, 0xd5, 0xa3, 0xb8, 0xd2, 0x3f, 0xd4, 0x3b, + 0x50, 0x47, 0x54, 0xe8, 0xfd, 0x8b, 0xf6, 0x97, 0x77, 0x0d, 0x6c, 0xe6, + 0x45, 0x71, 0x00, 0x90, 0x03, 0xce, 0xe2, 0x5f, 0xda, 0x27, 0xfb, 0xae, + 0xf9, 0xe8, 0x69, 0x15, 0x10, 0x28, 0x9b, 0x03, 0x4e, 0xf7, 0x67, 0xec, + 0xcc, 0x9d, 0xd9, 0x4b, 0x8d, 0x6f, 0x5b, 0x37, 0xb9, 0x07, 0xae, 0xce, + 0x5a, 0x79, 0xa4, 0xa7, 0xf5, 0x1a, 0x45, 0x83, 0xa2, 0x41, 0xd5, 0x50, + 0xb2, 0xd3, 0xc6, 0x26, 0xd7, 0x92, 0xea, 0xd1, 0x72, 0xb9, 0xc5, 0xd5, + 0x12, 0x27, 0xda, 0x1f, 0x51, 0xc1, 0xe4, 0x09, 0x2a, 0xeb, 0x58, 0x6a, + 0xb2, 0x6d, 0x67, 0xfa, 0x1d, 0x5c, 0x6a, 0x3a, 0xa8, 0x59, 0xae, 0x54, + 0x68, 0xca, 0x60, 0xbe, 0xf8, 0x04, 0x3f, 0xbe, 0x49, 0xc9, 0x8d, 0x10, + 0x20, 0x76, 0xfa, 0x6c, 0x6d, 0x02, 0x9a, 0x5d, 0xd5, 0xef, 0xa2, 0xd2, + 0xfd, 0xfd, 0x23, 0x0c, 0xa9, 0x02, 0x16, 0xdd, 0x1c, 0x08, 0x21, 0x77, + 0x8a, 0xbb, 0xe6, 0xa2, 0x6b, 0xbc, 0x3a, 0x42, 0x5e, 0xf9, 0xf6, 0xc0, + 0x18, 0x5b, 0x74, 0xd3, 0x83, 0x69, 0x04, 0xd5, 0x5f, 0xed, 0x3a, 0xab, + 0x7a, 0xb4, 0xbb, 0x9f, 0x1f, 0xde, 0x78, 0x83, 0xd8, 0x5a, 0xb2, 0xa1, + 0x3f, 0xb2, 0xa2, 0xa3, 0xe2, 0x1c, 0xbf, 0xc0, 0x99, 0x61, 0x98, 0xb7, + 0x0f, 0xd9, 0x04, 0xdc, 0x0b, 0xf9, 0x45, 0xde, 0x51, 0xbb, 0x08, 0xcb, + 0xee, 0x9c, 0xad, 0x07, 0xf3, 0x30, 0xb2, 0x8b, 0xff, 0x65, 0x75, 0xe5, + 0x5b, 0xf6, 0x3b, 0x43, 0xeb, 0x20, 0xb1, 0x2d, 0xe4, 0xe8, 0xf9, 0xd0, + 0x70, 0x7d, 0x84, 0x22, 0xa3, 0x8c, 0x95, 0x5e, 0x6e, 0x30, 0x69, 0x3e, + 0xa0, 0x73, 0xca, 0xe9, 0x7f, 0x7f, 0x6a, 0x55, 0xdd, 0x26, 0xb4, 0xb7, + 0xa7, 0x4c, 0x73, 0x9a, 0x45, 0x3b, 0xab, 0x19, 0xee, 0x1e, 0x59, 0x31, + 0x20, 0x50, 0x27, 0x5f, 0xed, 0x02, 0x9b, 0xd9, 0x82, 0x8f, 0x28, 0x9a, + 0x19, 0x95, 0x4b, 0x28, 0x0a, 0xa6, 0x0e, 0x2c, 0xa8, 0x84, 0x82, 0x26, + 0xbb, 0xc2, 0x89, 0xce, 0xf8, 0x5b, 0xac, 0x82, 0xf7, 0xa0, 0x30, 0x5c, + 0xd4, 0xde, 0x28, 0x74, 0xa9, 0xd9, 0xb3, 0xc6, 0x06, 0xe7, 0xdf, 0x8b, + 0x75, 0x87, 0x6b, 0xc3, 0xbb, 0xad, 0x37, 0x02, 0x60, 0xd4, 0x12, 0x13, + 0x0f, 0x77, 0xc1, 0xb1, 0x09, 0xc2, 0x22, 0x68, 0x45, 0x71, 0xcb, 0x70, + 0xcc, 0xe9, 0x1d, 0x31, 0x87, 0x03, 0x7a, 0xa2, 0x14, 0x33, 0x17, 0x7c, + 0x6c, 0x9b, 0xd6, 0x85, 0x82, 0x2d, 0x53, 0x0d, 0xee, 0x17, 0x33, 0x63, + 0xee, 0x71, 0x45, 0x7c, 0xed, 0x62, 0xba, 0x9d, 0xc6, 0xd8, 0xf9, 0x04, + 0x50, 0x1f, 0x12, 0xcd, 0xe9, 0xea, 0x76, 0x4d, 0xd6, 0x9e, 0x93, 0x1e, + 0xab, 0x15, 0x3f, 0xe5, 0x2a, 0x2c, 0xff, 0x0f, 0xb3, 0xcb, 0xdb, 0x0f, + 0xe0, 0x48, 0xba, 0x51, 0x8d, 0x80, 0x80, 0x54, 0x8d, 0x52, 0xd3, 0xc4, + 0xd5, 0x1a, 0xc8, 0x1b, 0x74, 0xc2, 0x4f, 0xb5, 0xf8, 0xaa, 0xc2, 0xd0, + 0x30, 0x49, 0x0d, 0x10, 0xb6, 0xc3, 0x41, 0x45, 0xcd, 0x52, 0x33, 0x9e, + 0x46, 0xc5, 0x64, 0x22, 0x22, 0x1f, 0x6e, 0xd2, 0x9f, 0x20, 0xf5, 0x4b, + 0xa5, 0x99, 0x89, 0x42, 0x60, 0xb3, 0xbf, 0x29, 0xe6, 0x2a, 0x3f, 0x1c, + 0xc1, 0x38, 0x76, 0x66, 0xe3, 0x52, 0x09, 0x90, 0x08, 0xd0, 0x7a, 0x73, + 0x7e, 0x57, 0x3a, 0xc3, 0x9a, 0x83, 0xa5, 0xe0, 0x4d, 0xb6, 0x5d, 0xb1, + 0x48, 0x34, 0xf7, 0x27, 0x9a, 0xf7, 0x4c, 0x17, 0xd1, 0xda, 0xed, 0x38, + 0x91, 0xf2, 0x53, 0xab, 0x1e, 0xef, 0x42, 0x3b, 0xb8, 0x7a, 0x10, 0x10, + 0x8a, 0xa6, 0x87, 0xd0, 0x26, 0x50, 0x7d, 0x25, 0xf4, 0x7b, 0x33, 0xdc, + 0x03, 0xd4, 0x16, 0x15, 0xfe, 0xb1, 0x26, 0xc9, 0x6e, 0x4f, 0x62, 0xa3, + 0x98, 0xde, 0xcd, 0xfd, 0xa8, 0xb2, 0x36, 0xa2, 0xcb, 0xed, 0x90, 0x59, + 0x58, 0x6b, 0xf6, 0xc3, 0x68, 0x17, 0x2b, 0x98, 0xb9, 0x30, 0xd1, 0x7e, + 0x66, 0x47, 0x95, 0x39, 0x6e, 0xd7, 0xa3, 0x29, 0x5e, 0x8a, 0x1a, 0x65, + 0xe9, 0x88, 0x8a, 0x20, 0x3b, 0x27, 0x80, 0x09, 0x40, 0x3b, 0x66, 0xa9, + 0x40, 0x03, 0xa3, 0x65, 0x67, 0x97, 0x22, 0xa0, 0x31, 0x4f, 0x2c, 0x44, + 0x07, 0x8a, 0x97, 0xf0, 0x8a, 0xd9, 0x63, 0xc2, 0xd5, 0x82, 0xca, 0xc3, + 0x84, 0xff, 0xec, 0xa7, 0x43, 0x9c, 0x51, 0x34, 0xf5, 0xca, 0x8c, 0xeb, + 0xb6, 0x47, 0xdf, 0x6d, 0x2f, 0xdf, 0x9e, 0xe8, 0xf7, 0x1b, 0x6f, 0x28, + 0xff, 0x11, 0x18, 0xb9, 0xb2, 0x96, 0x07, 0xab, 0x6a, 0x4b, 0x14, 0x99, + 0x47, 0xe5, 0x77, 0xd0, 0x76, 0xf9, 0x8e, 0x29, 0x24, 0x0d, 0xa3, 0x3a, + 0x02, 0xaf, 0xa9, 0x89, 0x0e, 0xed, 0xb8, 0xcb, 0xf4, 0xc5, 0xcd, 0x24, + 0x80, 0x32, 0x40, 0x6a, 0x72, 0x4b, 0x7f, 0xf4, 0xef, 0xbd, 0xc7, 0x22, + 0xc6, 0x4f, 0x34, 0xa1, 0x75, 0x95, 0xfd, 0x52, 0x71, 0xa5, 0x67, 0xee, + 0x85, 0x66, 0x6e, 0xb8, 0x25, 0x43, 0x6e, 0x09, 0xc3, 0x45, 0xa0, 0x3f, + 0x8d, 0x55, 0x0e, 0xdc, 0x75, 0xf9, 0x3a, 0xf9, 0x73, 0x6e, 0x76, 0xb1, + 0x66, 0x7c, 0xb6, 0x4a, 0x06, 0xb8, 0x6b, 0xe6, 0xd9, 0x2c, 0xd8, 0xd3, + 0x5e, 0x8b, 0xd7, 0x32, 0x37, 0x05, 0x89, 0xe4, 0x3a, 0xf3, 0x22, 0xa2, + 0x66, 0x15, 0xf7, 0x1b, 0xd3, 0x7d, 0x85, 0x27, 0x44, 0x2f, 0xbf, 0x0c, + 0xa8, 0x8a, 0x1b, 0x6a, 0xca, 0x59, 0xcd, 0x81, 0x02, 0x6c, 0x56, 0xda, + 0x28, 0xd8, 0xb4, 0xa3, 0xb3, 0x73, 0x41, 0x72, 0xa4, 0xa0, 0x0f, 0x88, + 0x5a, 0x31, 0x1b, 0xe9, 0x09, 0x04, 0x2e, 0x51, 0x38, 0x79, 0xdc, 0x2c, + 0x96, 0x71, 0xdf, 0x6f, 0xb1, 0x2d, 0xf8, 0x03, 0x24, 0xe1, 0x87, 0xd8, + 0xcd, 0xcc, 0x9a, 0x32, 0x10, 0x5b, 0xce, 0x98, 0x9d, 0x84, 0x1d, 0x9f, + 0xfd, 0xb6, 0xde, 0xed, 0x53, 0xe6, 0x95, 0xa5, 0xba, 0x4d, 0x1c, 0xbe, + 0x6a, 0x0b, 0x79, 0x04, 0xbb, 0x65, 0xc2, 0x1b, 0x27, 0xf8, 0xca, 0x8a, + 0xf2, 0x92, 0xda, 0xa0, 0x36, 0xc0, 0xcd, 0x47, 0x63, 0xa5, 0x9c, 0x1f, + 0xb0, 0x7d, 0x02, 0x7a, 0x4e, 0x3b, 0x12, 0x93, 0x82, 0xa2, 0xba, 0xde, + 0x0f, 0x05, 0xe2, 0x7e, 0x51, 0x0b, 0x93, 0xae, 0xa0, 0x38, 0xfa, 0x57, + 0x9f, 0xb7, 0xe5, 0x5b, 0xe7, 0x70, 0x35, 0x73, 0xfd, 0x69, 0xbb, 0x02, + 0x36, 0xdc, 0xe6, 0x88, 0xdf, 0xc4, 0x14, 0x53, 0xdb, 0xd5, 0x29, 0x75, + 0xee, 0x7a, 0x43, 0x02, 0xb8, 0x9e, 0xd2, 0xeb, 0x9c, 0x30, 0x17, 0x11, + 0x2c, 0x17, 0x85, 0x29, 0xf0, 0x14, 0x83, 0x40, 0x67, 0x3a, 0xaa, 0x81, + 0xab, 0x51, 0xd4, 0x93, 0xca, 0x71, 0xb6, 0x91, 0x71, 0xfa, 0xd2, 0x85, + 0x0b, 0x37, 0x03, 0x7d, 0x3a, 0xa8, 0xdc, 0xdd, 0x86, 0xf5, 0xaf, 0x68, + 0x83, 0x62, 0x99, 0xd3, 0x08, 0x66, 0x31, 0xc2, 0xb3, 0x9b, 0x5b, 0x16, + 0x79, 0x02, 0x73, 0x08, 0xc2, 0x5e, 0xb2, 0x51, 0x62, 0xde, 0xf2, 0x80, + 0x3f, 0xf9, 0x38, 0x89, 0xd8, 0x54, 0x8f, 0x12, 0x57, 0x6f, 0x63, 0x82, + 0xe8, 0x85, 0x50, 0x76, 0x47, 0xe8, 0xb4, 0x72, 0x30, 0x82, 0x01, 0x66, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, + 0x82, 0x01, 0x57, 0x04, 0x82, 0x01, 0x53, 0x30, 0x82, 0x01, 0x4f, 0x30, + 0x82, 0x01, 0x4b, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x81, 0xef, 0x30, 0x81, 0xec, 0x30, 0x57, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d, 0x30, + 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0x02, 0x22, 0x32, 0x6d, 0xb7, 0xe6, + 0x22, 0x27, 0x02, 0x02, 0x08, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05, 0x00, 0x30, 0x1d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2a, 0x04, 0x10, 0x67, + 0x70, 0x30, 0x70, 0x64, 0x61, 0xc0, 0x8d, 0xe0, 0x4b, 0x29, 0x5c, 0x05, + 0x42, 0x51, 0x55, 0x04, 0x81, 0x90, 0xd9, 0xf6, 0x74, 0x29, 0xd6, 0xb5, + 0xf8, 0x75, 0x65, 0x08, 0x7b, 0x96, 0x19, 0x3d, 0xf3, 0xaf, 0xc9, 0xeb, + 0x5f, 0x5b, 0xdf, 0x7d, 0xc6, 0xe9, 0x8d, 0xdb, 0x0a, 0xe8, 0xda, 0x51, + 0x2c, 0x52, 0xca, 0x8e, 0x2b, 0xb3, 0x81, 0x92, 0x61, 0xaa, 0x11, 0x68, + 0x4d, 0x9b, 0xce, 0xff, 0xdc, 0x7f, 0x0f, 0x43, 0x2b, 0xd7, 0x11, 0x02, + 0xad, 0xd3, 0x2a, 0x4b, 0x56, 0x5e, 0x7e, 0xde, 0x70, 0xa2, 0x42, 0x5d, + 0xa5, 0x07, 0x3f, 0x6a, 0xb9, 0x6a, 0x8f, 0x22, 0xc2, 0x10, 0x16, 0xa5, + 0x97, 0x5b, 0x1c, 0x4d, 0xf3, 0x42, 0xfa, 0x14, 0xd1, 0x5f, 0xd1, 0x22, + 0xcd, 0xfe, 0x2c, 0x2c, 0xf6, 0xc2, 0x23, 0xeb, 0x98, 0x3c, 0xc5, 0xd3, + 0xec, 0xcc, 0x61, 0x32, 0xc0, 0x56, 0xb7, 0xe8, 0x01, 0x7a, 0xd5, 0xdc, + 0x8d, 0x6d, 0x3e, 0xe4, 0x48, 0x45, 0xd3, 0xf0, 0xbe, 0xe6, 0x48, 0x4f, + 0x4b, 0xba, 0x9e, 0xe3, 0xb9, 0x7d, 0xb1, 0xb1, 0x2e, 0x71, 0x91, 0x53, + 0xc9, 0x47, 0x73, 0x58, 0x39, 0xa7, 0x31, 0x4a, 0x30, 0x23, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, 0x16, 0x1e, + 0x14, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x66, 0x00, 0x53, 0x00, + 0x53, 0x00, 0x4c, 0x00, 0x20, 0x00, 0xe9, 0x20, 0xac, 0x30, 0x23, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, + 0x04, 0x14, 0xfe, 0x60, 0x42, 0x8a, 0xbb, 0x29, 0xf4, 0xe4, 0x4d, 0x0f, + 0x8e, 0xba, 0x04, 0xb0, 0xca, 0xf3, 0x48, 0xe0, 0xb3, 0x07, 0x30, 0x41, + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, 0x1b, 0x58, 0x6c, 0xa5, 0x9c, + 0xf4, 0xb8, 0xbd, 0x48, 0x80, 0xb2, 0xd0, 0x6b, 0x5b, 0xfc, 0x54, 0x16, + 0x61, 0x57, 0x10, 0xc0, 0x3d, 0xf3, 0x21, 0x03, 0x5b, 0x2a, 0x3a, 0xf9, + 0x7f, 0x00, 0xc6, 0x04, 0x08, 0xa5, 0x25, 0xff, 0x5c, 0x11, 0xaa, 0xb6, + 0xa6, 0x02, 0x02, 0x08, 0x00, + }; + /* As p12Der with friendlyName "wolfSSL". */ + static const unsigned char p12AsciiDer[] = { + 0x30, 0x82, 0x08, 0x5b, 0x02, 0x01, 0x03, 0x30, 0x82, 0x08, 0x11, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, + 0x08, 0x02, 0x04, 0x82, 0x07, 0xfe, 0x30, 0x82, 0x07, 0xfa, 0x30, 0x82, + 0x06, 0x92, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x06, 0xa0, 0x82, 0x06, 0x83, 0x30, 0x82, 0x06, 0x7f, 0x02, 0x01, 0x00, + 0x30, 0x82, 0x06, 0x78, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x01, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0x1f, + 0xcf, 0xc4, 0xe9, 0xd6, 0xc7, 0x81, 0x1a, 0x02, 0x02, 0x08, 0x00, 0x30, + 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05, + 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x01, 0x2a, 0x04, 0x10, 0xd3, 0xd7, 0x8e, 0xb1, 0x03, 0xed, 0xcc, 0x84, + 0xd7, 0x16, 0xb8, 0xd9, 0xfd, 0xcd, 0x91, 0xa2, 0x80, 0x82, 0x06, 0x10, + 0x4c, 0x8b, 0x28, 0xa6, 0xd3, 0x62, 0xd4, 0xd1, 0xb1, 0xcf, 0x8d, 0x63, + 0x98, 0x5f, 0x7e, 0xec, 0xce, 0x77, 0x8f, 0x77, 0x4e, 0xab, 0x14, 0x54, + 0x0b, 0x9a, 0x23, 0x2a, 0xb9, 0x77, 0x3b, 0xd5, 0x20, 0x29, 0x3e, 0xe8, + 0xa5, 0x8b, 0xc1, 0xe1, 0x3c, 0x8c, 0xd7, 0xd0, 0xd2, 0xf7, 0x19, 0x11, + 0xd7, 0x03, 0xfd, 0xba, 0x9a, 0x10, 0xa8, 0x6a, 0x16, 0x60, 0xd1, 0x27, + 0x46, 0xc2, 0x9a, 0xa1, 0xda, 0xc3, 0x32, 0xba, 0x8e, 0x70, 0x89, 0x0f, + 0x38, 0x28, 0x92, 0x8e, 0x18, 0x1a, 0x0e, 0xa1, 0x3c, 0x5e, 0x50, 0x6a, + 0x1b, 0x3a, 0x50, 0x74, 0x6a, 0xaf, 0xf6, 0x40, 0x83, 0xd2, 0xe9, 0xd0, + 0xc9, 0x0e, 0x35, 0xd1, 0x07, 0x9f, 0xe0, 0xd1, 0x91, 0x62, 0xce, 0x99, + 0x9e, 0xd4, 0x17, 0x6f, 0x8d, 0x10, 0x19, 0x15, 0x86, 0x01, 0x28, 0x13, + 0xe9, 0x13, 0x0c, 0x3f, 0xa9, 0x1a, 0x62, 0x15, 0x08, 0x93, 0x6a, 0x4f, + 0xc4, 0x64, 0x91, 0xd8, 0xb8, 0xe9, 0xe0, 0x53, 0xaf, 0x36, 0x70, 0xcb, + 0xc8, 0xe3, 0x1e, 0x17, 0xfb, 0xa4, 0x39, 0xba, 0xd4, 0x57, 0xa9, 0xea, + 0x64, 0x98, 0x4b, 0x99, 0xb9, 0x16, 0x29, 0x5b, 0xc4, 0xff, 0x7d, 0xc1, + 0x26, 0xd0, 0x49, 0xdc, 0xcc, 0x6d, 0xa5, 0xf9, 0xb6, 0xa3, 0x8a, 0x29, + 0x49, 0x98, 0x58, 0x4e, 0xe8, 0x20, 0x46, 0xd8, 0xc8, 0x7e, 0xe1, 0x89, + 0x3b, 0xa3, 0xb9, 0x91, 0xfc, 0x30, 0x2e, 0x8c, 0xc4, 0x3e, 0x1a, 0x9b, + 0xac, 0x5e, 0x8c, 0x48, 0x59, 0xc7, 0x0d, 0x63, 0x89, 0x81, 0x7f, 0xf0, + 0x9e, 0xc0, 0xb3, 0xb5, 0x6b, 0x6d, 0x35, 0xf6, 0xe7, 0x9b, 0xef, 0x16, + 0x71, 0xd6, 0x8c, 0x68, 0xdc, 0xf5, 0x52, 0xad, 0x8e, 0x14, 0x00, 0x80, + 0x73, 0x67, 0x1d, 0x5a, 0x3d, 0xd3, 0xfa, 0x1f, 0x5e, 0x02, 0x5d, 0x79, + 0x77, 0xfe, 0x74, 0x21, 0x24, 0x7f, 0x27, 0xe7, 0xaf, 0x18, 0xa3, 0x62, + 0xb3, 0x75, 0xf4, 0x8b, 0xa0, 0x9f, 0x55, 0x80, 0xe1, 0x2e, 0x59, 0xd7, + 0xb9, 0x14, 0x2b, 0x9d, 0xfd, 0xfe, 0xf5, 0x04, 0x9b, 0x09, 0xc9, 0x72, + 0xad, 0x53, 0xae, 0x47, 0x4a, 0x20, 0xeb, 0xcd, 0x71, 0xf4, 0x5e, 0xff, + 0x83, 0xab, 0x77, 0x42, 0xbb, 0x03, 0x0d, 0xb9, 0x8c, 0x37, 0x8e, 0xa1, + 0xa1, 0xd4, 0x77, 0xf5, 0xf1, 0x62, 0xa4, 0xae, 0x1a, 0x2c, 0xd9, 0x6d, + 0x89, 0x49, 0xf7, 0x5f, 0x76, 0xda, 0xb4, 0x44, 0xe6, 0x14, 0xe9, 0xfa, + 0x8e, 0xb8, 0x96, 0xe2, 0x8d, 0xb0, 0x2d, 0x4c, 0x63, 0xd8, 0xd6, 0x92, + 0x50, 0xf6, 0x1c, 0x4a, 0xba, 0x4d, 0x40, 0xdf, 0xc1, 0xef, 0xa0, 0xa3, + 0xa4, 0x7f, 0xb8, 0x9f, 0x01, 0x54, 0xf1, 0x45, 0xf9, 0xf1, 0xcc, 0x90, + 0xfa, 0xd2, 0x91, 0x14, 0x43, 0x1e, 0xd6, 0x73, 0x01, 0x76, 0xc1, 0x6f, + 0xd1, 0x50, 0x6f, 0xba, 0xd4, 0xd7, 0x6a, 0xff, 0xfb, 0x67, 0xe8, 0x57, + 0x45, 0x4c, 0xf2, 0x2c, 0x60, 0x43, 0xb4, 0x49, 0xec, 0x39, 0x63, 0xe1, + 0xcf, 0x98, 0x01, 0x78, 0x30, 0xf3, 0x4d, 0x9d, 0xd8, 0x90, 0x68, 0x4c, + 0x7c, 0x2e, 0xf5, 0x38, 0x19, 0x87, 0x6e, 0x9c, 0xbd, 0xb9, 0x51, 0x01, + 0x90, 0x7f, 0x26, 0x7d, 0x4f, 0x50, 0x40, 0x2c, 0xf2, 0xc3, 0x7c, 0x28, + 0x55, 0xd0, 0x79, 0x11, 0x5c, 0x0f, 0xdc, 0xe5, 0xba, 0x35, 0xd8, 0xb3, + 0xf8, 0xdf, 0x66, 0xa8, 0x49, 0xac, 0xeb, 0xf6, 0xa8, 0x5a, 0x93, 0xc6, + 0x03, 0xa1, 0x93, 0x20, 0xb4, 0xcb, 0xf3, 0xbb, 0x99, 0xd0, 0x4a, 0x7c, + 0x58, 0x86, 0x07, 0xe5, 0x63, 0xbc, 0x78, 0xb8, 0xeb, 0xb2, 0x3f, 0xb1, + 0xac, 0xcb, 0xfa, 0x09, 0x81, 0x9f, 0xc3, 0xcb, 0x6f, 0xae, 0x69, 0x8e, + 0xf2, 0x46, 0xa6, 0x2e, 0x54, 0xa7, 0xf1, 0xf8, 0x03, 0x29, 0x97, 0xf2, + 0x51, 0x16, 0x02, 0x91, 0xc5, 0xd5, 0xef, 0x45, 0x79, 0x2a, 0xb4, 0x62, + 0x30, 0xdc, 0x16, 0xea, 0xcd, 0xb8, 0xda, 0xe6, 0x69, 0x53, 0xf3, 0x56, + 0x7a, 0x7c, 0x7f, 0x1a, 0x3e, 0x04, 0x00, 0x46, 0x54, 0xe5, 0x9d, 0xc7, + 0x68, 0x80, 0xb3, 0x7b, 0x97, 0x40, 0xb1, 0x5c, 0x42, 0x90, 0x00, 0xa4, + 0x89, 0x59, 0x1e, 0x19, 0xb1, 0xfd, 0x71, 0xc7, 0x15, 0x36, 0x89, 0xbd, + 0x82, 0xc3, 0xcc, 0x2b, 0xdf, 0x0f, 0x96, 0x73, 0xde, 0x17, 0x01, 0x61, + 0xbc, 0x52, 0x9d, 0x0f, 0x23, 0xee, 0x2a, 0xc4, 0x22, 0x69, 0x6c, 0x80, + 0x2e, 0x54, 0x8c, 0x09, 0xdb, 0x24, 0xa9, 0x29, 0x26, 0x36, 0x2a, 0xf9, + 0xdc, 0x79, 0xcb, 0x51, 0x56, 0x60, 0xaf, 0xc2, 0x77, 0x3c, 0x4e, 0x77, + 0x5b, 0x7d, 0x7c, 0xe1, 0x01, 0xd8, 0x72, 0x2b, 0x9b, 0xe0, 0xa2, 0x5a, + 0x57, 0x6e, 0xf4, 0x00, 0x5c, 0x08, 0x58, 0x35, 0x91, 0x58, 0x4c, 0xbe, + 0x31, 0x78, 0x78, 0xe4, 0xf8, 0x47, 0x73, 0xc6, 0xbe, 0x9c, 0x62, 0x62, + 0xb2, 0x03, 0xc3, 0xa5, 0xbd, 0xf5, 0x97, 0x11, 0x61, 0xb0, 0xf1, 0x2b, + 0x76, 0x38, 0x17, 0x5a, 0x2a, 0xc8, 0xe7, 0x02, 0x7c, 0x34, 0xea, 0x93, + 0x01, 0x12, 0x70, 0xff, 0xc7, 0xa5, 0x7b, 0x66, 0x75, 0x55, 0x43, 0x72, + 0xd6, 0x68, 0x84, 0xfe, 0xba, 0x7e, 0xf0, 0x3f, 0x52, 0x74, 0x59, 0x17, + 0xdd, 0x9a, 0xd3, 0x50, 0xd2, 0x38, 0x7a, 0x22, 0xb8, 0xf8, 0x3b, 0x3f, + 0x3d, 0x01, 0xae, 0x6a, 0x16, 0x1d, 0xb6, 0xa9, 0x64, 0xeb, 0x52, 0x91, + 0x9c, 0x32, 0x54, 0x3e, 0xa1, 0xc6, 0x74, 0x6a, 0x77, 0x7e, 0x43, 0x84, + 0xed, 0x09, 0xcd, 0xa0, 0x51, 0x0d, 0x5c, 0x4c, 0xbc, 0x2b, 0x0a, 0x5a, + 0xa4, 0x6e, 0x96, 0xc6, 0xa0, 0xfd, 0x3b, 0x36, 0x71, 0x43, 0xda, 0x37, + 0xf2, 0xa2, 0x65, 0x5a, 0x74, 0x10, 0x6f, 0xda, 0xb4, 0x09, 0x64, 0x2d, + 0x0f, 0x26, 0x81, 0xc3, 0xb4, 0x67, 0x8d, 0x0d, 0xfe, 0xf2, 0x87, 0xcf, + 0x04, 0x53, 0xfd, 0x1a, 0xa7, 0x92, 0x85, 0x45, 0x60, 0x07, 0x55, 0x22, + 0x7c, 0x01, 0xe4, 0x77, 0xde, 0x43, 0x4b, 0xb5, 0x6e, 0xb5, 0x27, 0xff, + 0x06, 0x97, 0x5c, 0x66, 0x33, 0x54, 0x17, 0xe9, 0xee, 0xdc, 0x0a, 0x3d, + 0xf3, 0xe7, 0x31, 0x23, 0xb2, 0xa7, 0x29, 0x9b, 0x22, 0xc7, 0xb7, 0x6c, + 0xbd, 0x60, 0xbe, 0xc7, 0x6f, 0x4d, 0xa7, 0x80, 0x1c, 0x80, 0xfe, 0xbd, + 0xfe, 0xa1, 0xd5, 0x89, 0xcd, 0x3e, 0xe0, 0xd1, 0x18, 0x03, 0x2d, 0xb2, + 0x60, 0xdd, 0xd0, 0x0d, 0xc3, 0x9e, 0xb0, 0x3b, 0x06, 0x83, 0x8b, 0x24, + 0x05, 0xbc, 0x8f, 0x4f, 0x6d, 0x09, 0x10, 0x04, 0x6b, 0x11, 0x65, 0xfc, + 0x4b, 0x87, 0x91, 0x63, 0x29, 0x0d, 0xcb, 0xae, 0x34, 0x35, 0xf1, 0xee, + 0xaa, 0xc4, 0x5e, 0x81, 0xbd, 0xe7, 0xcc, 0x7c, 0xd9, 0x2e, 0x6c, 0x25, + 0x55, 0x0c, 0x92, 0x2a, 0xc3, 0x62, 0xaa, 0xc9, 0x91, 0x1c, 0x4c, 0xe4, + 0x56, 0xc3, 0xe8, 0x19, 0xe7, 0xb2, 0x0f, 0x78, 0xd4, 0x0b, 0x20, 0x55, + 0xd2, 0x40, 0xb3, 0x76, 0x3f, 0x4a, 0xc9, 0xde, 0x4b, 0xde, 0x36, 0x5b, + 0x81, 0x23, 0x3b, 0x80, 0x83, 0x44, 0x17, 0x03, 0x4c, 0x09, 0xee, 0x76, + 0xd0, 0x07, 0x21, 0x8b, 0xf7, 0x49, 0x16, 0xc7, 0x6e, 0x76, 0xbc, 0xa4, + 0x11, 0xb4, 0x9f, 0x15, 0x43, 0xae, 0xe6, 0xf2, 0xf4, 0x48, 0x7e, 0x0e, + 0x73, 0x8f, 0xfa, 0x2b, 0x5d, 0xa7, 0x02, 0x12, 0xf4, 0x29, 0x25, 0xec, + 0xe5, 0x43, 0x60, 0x4a, 0x8a, 0x89, 0x38, 0xe2, 0xd7, 0x7f, 0x3e, 0x5f, + 0x14, 0x2d, 0x70, 0xd8, 0x34, 0xfd, 0xc5, 0x89, 0x59, 0xd0, 0x27, 0xb0, + 0x0b, 0xa7, 0x23, 0x71, 0xa8, 0x8a, 0xd1, 0xcc, 0x51, 0xe4, 0x1d, 0xd3, + 0x93, 0x77, 0x42, 0x7d, 0x36, 0xdb, 0x53, 0xda, 0x83, 0x42, 0xfa, 0x26, + 0xb5, 0xa6, 0xd8, 0xcd, 0x64, 0xb9, 0x78, 0xdd, 0x14, 0x9d, 0x3d, 0xb4, + 0xd3, 0x88, 0x1f, 0x67, 0x7c, 0xd4, 0xe7, 0xdb, 0xc1, 0xe6, 0x25, 0x24, + 0x93, 0xea, 0xb1, 0x26, 0x0d, 0x33, 0xf2, 0xa0, 0x6e, 0x23, 0x4b, 0xf8, + 0x9a, 0x2d, 0x22, 0x47, 0x09, 0x16, 0xb0, 0x46, 0xb2, 0xdb, 0xe0, 0x43, + 0x49, 0x0f, 0xc6, 0xb4, 0x9c, 0x11, 0x25, 0x9d, 0x53, 0x5e, 0x9a, 0xb0, + 0xd8, 0x5e, 0x94, 0x10, 0x36, 0x3a, 0xb1, 0x10, 0x59, 0x91, 0x6d, 0x70, + 0xd2, 0xa4, 0x9d, 0x36, 0x29, 0x37, 0xf7, 0xc2, 0x1a, 0x66, 0xb5, 0x6f, + 0x96, 0x57, 0x81, 0x50, 0x27, 0x7a, 0x36, 0x4a, 0x61, 0xa1, 0x33, 0x7a, + 0x03, 0x23, 0x0a, 0xb0, 0x1a, 0x1f, 0xd0, 0xaa, 0x1d, 0xc3, 0x82, 0xa3, + 0xe0, 0x08, 0x37, 0x0a, 0xb4, 0x57, 0xc5, 0xc4, 0xd5, 0xce, 0xe3, 0xd6, + 0xd1, 0x90, 0x8b, 0xfb, 0xdc, 0xf0, 0xfe, 0xeb, 0x67, 0x2f, 0x17, 0x89, + 0xe2, 0x25, 0x92, 0x43, 0xd1, 0x22, 0x8a, 0x2d, 0x04, 0xde, 0x3c, 0x04, + 0x48, 0xa1, 0xd3, 0xa6, 0x6f, 0x67, 0x49, 0xb7, 0x20, 0x14, 0xe0, 0xb9, + 0x27, 0xcb, 0xbb, 0xb2, 0xb9, 0xfb, 0xb7, 0x46, 0x6c, 0x41, 0xce, 0x5b, + 0xbf, 0xa2, 0x51, 0x9a, 0x65, 0x1f, 0x0a, 0x6f, 0x08, 0x60, 0x19, 0xdd, + 0x6f, 0x2a, 0xeb, 0xec, 0x5a, 0x23, 0x40, 0x60, 0xce, 0x8b, 0x5c, 0x8c, + 0x7d, 0xd2, 0xca, 0x58, 0x90, 0x5c, 0x6e, 0x80, 0x44, 0x06, 0x55, 0x66, + 0xf6, 0x64, 0x8d, 0x38, 0xff, 0x1a, 0x97, 0xe6, 0x96, 0xd6, 0x57, 0xc2, + 0xbb, 0xeb, 0x91, 0x5c, 0xf0, 0xd1, 0x33, 0x90, 0x08, 0x65, 0x69, 0xdf, + 0xb6, 0x73, 0x09, 0x68, 0x11, 0x11, 0xb4, 0x83, 0xbb, 0xdf, 0x1d, 0x36, + 0x94, 0x30, 0x87, 0xa0, 0x31, 0xca, 0xc0, 0x0f, 0x57, 0x83, 0xaa, 0xd4, + 0x88, 0xf6, 0xcc, 0x91, 0xd1, 0x7f, 0xcc, 0xd7, 0xf4, 0x30, 0x93, 0x16, + 0x70, 0x16, 0x28, 0xb5, 0xe1, 0x23, 0xdb, 0xb7, 0xed, 0x61, 0xc3, 0x2f, + 0xdf, 0xeb, 0x9b, 0x2a, 0x53, 0x81, 0x1b, 0xac, 0xc3, 0x51, 0x6a, 0x69, + 0x18, 0xf4, 0x8f, 0x2c, 0x5e, 0xc7, 0x7a, 0x63, 0xd0, 0x86, 0xe9, 0x8f, + 0x33, 0xbd, 0x1c, 0x33, 0xc2, 0x19, 0x2e, 0x14, 0xd2, 0xb8, 0xbb, 0xf8, + 0xd0, 0xbe, 0xe6, 0x75, 0x0d, 0xea, 0x30, 0x1e, 0xc1, 0x69, 0x6d, 0x8e, + 0x48, 0xf6, 0x10, 0xbc, 0xba, 0x35, 0x6a, 0x2c, 0x4c, 0x87, 0x3d, 0xdb, + 0x5e, 0x2e, 0x11, 0xcd, 0x62, 0x93, 0xe2, 0x0e, 0x20, 0xe8, 0x3c, 0x3e, + 0x9c, 0xea, 0x46, 0xd7, 0x4d, 0xc6, 0x9f, 0xf0, 0x1e, 0xaf, 0xd3, 0xa4, + 0x5f, 0x8f, 0x4b, 0x8d, 0x2a, 0xfa, 0x35, 0xc9, 0xe5, 0x2f, 0x74, 0x91, + 0x78, 0x0a, 0xf6, 0x42, 0x33, 0x49, 0xb2, 0xbf, 0x74, 0xa3, 0x50, 0x63, + 0x9c, 0x19, 0xcd, 0x59, 0xbe, 0x5a, 0x6f, 0xdd, 0xea, 0x1e, 0xcc, 0x69, + 0xaf, 0xfe, 0x02, 0x47, 0xd8, 0xd4, 0xb1, 0x16, 0xab, 0x2b, 0x5b, 0x0e, + 0x72, 0x81, 0x92, 0x96, 0x37, 0x20, 0x4a, 0x43, 0x5b, 0xcb, 0x32, 0x16, + 0x06, 0x99, 0xea, 0x6d, 0x26, 0x85, 0x88, 0x25, 0xa7, 0x0d, 0x67, 0xcd, + 0x57, 0x47, 0x2c, 0x1a, 0x19, 0x22, 0xc9, 0x07, 0xfa, 0x15, 0x8a, 0x11, + 0x89, 0x46, 0xdd, 0xc0, 0x42, 0x88, 0x44, 0xcd, 0xba, 0xd0, 0x73, 0x23, + 0x16, 0x4b, 0x8f, 0xdb, 0xd8, 0xbf, 0x35, 0xe3, 0x00, 0xc3, 0xdc, 0xda, + 0x98, 0x66, 0xe9, 0xb9, 0x08, 0xb4, 0xaa, 0xa2, 0x34, 0xaa, 0x5c, 0xba, + 0xc7, 0x57, 0x44, 0x3f, 0xd4, 0x81, 0xc1, 0x49, 0x84, 0xea, 0xa9, 0x06, + 0x65, 0x88, 0x25, 0xa8, 0xfb, 0x3c, 0xc5, 0x29, 0x31, 0x33, 0x24, 0x45, + 0x6e, 0x65, 0x4c, 0x87, 0x30, 0x82, 0x01, 0x60, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, 0x01, 0x51, 0x04, + 0x82, 0x01, 0x4d, 0x30, 0x82, 0x01, 0x49, 0x30, 0x82, 0x01, 0x45, 0x06, + 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, + 0xa0, 0x81, 0xef, 0x30, 0x81, 0xec, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, + 0x04, 0x08, 0xa1, 0x54, 0x7b, 0xb4, 0x4f, 0x30, 0x2f, 0x11, 0x02, 0x02, + 0x08, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x02, 0x09, 0x05, 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x01, 0x2a, 0x04, 0x10, 0x14, 0x04, 0xc0, 0x75, 0xef, + 0x62, 0x9a, 0x86, 0x86, 0xb8, 0x73, 0x6f, 0x9f, 0x96, 0x89, 0x27, 0x04, + 0x81, 0x90, 0xdf, 0xbf, 0xab, 0xf2, 0x4c, 0x49, 0xcc, 0x98, 0x81, 0xa7, + 0xba, 0x2f, 0x18, 0x42, 0x01, 0x3b, 0xf2, 0xf6, 0x0b, 0xb4, 0xf0, 0x3d, + 0xda, 0x13, 0xe6, 0x92, 0x33, 0xa7, 0x63, 0x50, 0xcc, 0x66, 0xa5, 0xa6, + 0x7d, 0xe6, 0x70, 0x62, 0x4c, 0xf5, 0x09, 0x4f, 0x38, 0x0e, 0x0a, 0xab, + 0x34, 0x26, 0xbf, 0xf1, 0xb3, 0x6f, 0x87, 0x9c, 0x0b, 0x1b, 0xcc, 0x70, + 0x1b, 0xc0, 0x3e, 0x47, 0x38, 0x4e, 0x79, 0x34, 0x3f, 0x76, 0xa4, 0x88, + 0x80, 0x95, 0x2d, 0xd1, 0x3b, 0xfe, 0xca, 0x58, 0x15, 0x35, 0x23, 0x6d, + 0x6e, 0xf8, 0x54, 0x4a, 0x4d, 0x25, 0xbc, 0x62, 0x78, 0x31, 0x82, 0x12, + 0x06, 0x1a, 0x33, 0xd9, 0xf1, 0x48, 0x66, 0xa3, 0xb9, 0x2c, 0x36, 0x0e, + 0x42, 0x0c, 0x07, 0x35, 0x8b, 0xd6, 0x90, 0x3b, 0x24, 0x34, 0x7e, 0x64, + 0x23, 0x8a, 0xb8, 0xbb, 0x91, 0xac, 0x75, 0xb6, 0x11, 0x58, 0x89, 0xb7, + 0x4a, 0x5b, 0x30, 0xb3, 0x24, 0x25, 0x7a, 0x16, 0x96, 0x3c, 0x97, 0x14, + 0x81, 0x9a, 0x31, 0x44, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, 0x10, 0x1e, 0x0e, 0x00, 0x77, 0x00, + 0x6f, 0x00, 0x6c, 0x00, 0x66, 0x00, 0x53, 0x00, 0x53, 0x00, 0x4c, 0x30, + 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, + 0x31, 0x16, 0x04, 0x14, 0xfe, 0x60, 0x42, 0x8a, 0xbb, 0x29, 0xf4, 0xe4, + 0x4d, 0x0f, 0x8e, 0xba, 0x04, 0xb0, 0xca, 0xf3, 0x48, 0xe0, 0xb3, 0x07, + 0x30, 0x41, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, 0x45, 0xf3, 0x81, + 0xac, 0x56, 0xb3, 0xd8, 0xbb, 0x3d, 0x5e, 0x5b, 0xdc, 0xe6, 0xf5, 0x61, + 0x8a, 0xe0, 0x3b, 0xbc, 0xd7, 0xb1, 0x4b, 0xff, 0xe9, 0x39, 0xd0, 0xa6, + 0x0d, 0x9e, 0x23, 0xda, 0x10, 0x04, 0x08, 0xac, 0xec, 0x08, 0xf8, 0x75, + 0x35, 0xd4, 0xf7, 0x02, 0x02, 0x08, 0x00, + }; + static const unsigned char keyid[] = { + 0xfe, 0x60, 0x42, 0x8a, 0xbb, 0x29, 0xf4, 0xe4, 0x4d, 0x0f, + 0x8e, 0xba, 0x04, 0xb0, 0xca, 0xf3, 0x48, 0xe0, 0xb3, 0x07 + }; + WOLFSSL_BIO* bio = NULL; + WC_PKCS12* pkcs12 = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + WOLFSSL_X509* cert = NULL; + WOLFSSL_X509* dup = NULL; + WOLF_STACK_OF(WOLFSSL_X509)* caCerts = NULL; + unsigned char* p = NULL; + int len = 0; + + ExpectNull(X509_alias_get0(NULL, &len)); + ExpectNull(X509_keyid_get0(NULL, &len)); + + ExpectNotNull(bio = BIO_new_mem_buf(p12AsciiDer, + (int)sizeof(p12AsciiDer))); + ExpectNotNull(pkcs12 = d2i_PKCS12_bio(bio, NULL)); + /* The key cert has its own attributes, not those of the CA bag. */ + ExpectIntEQ(PKCS12_parse(pkcs12, "wolfSSL test", &pkey, &cert, + &caCerts), 1); + ExpectIntEQ(sk_X509_num(caCerts), 1); + ExpectNotNull(p = X509_alias_get0(cert, &len)); + ExpectIntEQ(len, 7); + ExpectBufEQ(p, "wolfSSL", 7); + ExpectIntEQ(p[len], 0); + ExpectNotNull(X509_alias_get0(cert, NULL)); + ExpectNotNull(p = X509_keyid_get0(cert, &len)); + ExpectIntEQ(len, (int)sizeof(keyid)); + ExpectBufEQ(p, keyid, sizeof(keyid)); + ExpectIntEQ(p[len], 0); + + /* As in OpenSSL, a copy does not have them. */ + ExpectNotNull(dup = X509_dup(cert)); + ExpectNull(X509_alias_get0(dup, &len)); + ExpectNull(X509_keyid_get0(dup, &len)); + + X509_free(dup); + sk_X509_pop_free(caCerts, X509_free); + X509_free(cert); + EVP_PKEY_free(pkey); + PKCS12_free(pkcs12); + BIO_free(bio); + + /* A friendlyName that is not ASCII is left out. */ + caCerts = NULL; + cert = NULL; + pkey = NULL; + pkcs12 = NULL; + bio = NULL; + ExpectNotNull(bio = BIO_new_mem_buf(p12Der, (int)sizeof(p12Der))); + ExpectNotNull(pkcs12 = d2i_PKCS12_bio(bio, NULL)); + ExpectIntEQ(PKCS12_parse(pkcs12, "wolfSSL test", &pkey, &cert, + &caCerts), 1); + ExpectNull(X509_alias_get0(cert, &len)); + ExpectNotNull(p = X509_keyid_get0(cert, &len)); + ExpectIntEQ(len, (int)sizeof(keyid)); + ExpectBufEQ(p, keyid, sizeof(keyid)); + sk_X509_pop_free(caCerts, X509_free); + X509_free(cert); + EVP_PKEY_free(pkey); + PKCS12_free(pkcs12); + BIO_free(bio); +#if !defined(NO_DES3) && !defined(NO_RSA) && !defined(NO_SHA) && \ + !defined(NO_FILESYSTEM) + { + /* localKeyId only: the SHA-1 of the cert, as OpenSSL makes it. */ + unsigned char srvKeyid[WC_SHA_DIGEST_SIZE]; + unsigned int srvKeyidSz = 0; + WOLF_STACK_OF(WOLFSSL_X509)* ca = NULL; + + cert = NULL; + pkey = NULL; + pkcs12 = NULL; + bio = NULL; + ExpectNotNull(bio = BIO_new_file("./certs/test-servercert.p12", + "rb")); + ExpectNotNull(pkcs12 = d2i_PKCS12_bio(bio, NULL)); + ExpectIntEQ(PKCS12_parse(pkcs12, "wolfSSL test", &pkey, &cert, &ca), + 1); + ExpectNull(X509_alias_get0(cert, &len)); + ExpectIntEQ(X509_digest(cert, EVP_sha1(), srvKeyid, &srvKeyidSz), 1); + ExpectNotNull(p = X509_keyid_get0(cert, &len)); + ExpectIntEQ(len, (int)srvKeyidSz); + ExpectBufEQ(p, srvKeyid, srvKeyidSz); + sk_X509_pop_free(ca, X509_free); + X509_free(cert); + EVP_PKEY_free(pkey); + PKCS12_free(pkcs12); + BIO_free(bio); + } +#endif +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ossl_p7p12.h b/tests/api/test_ossl_p7p12.h index 1f1e283f528..a35a26eefdd 100644 --- a/tests/api/test_ossl_p7p12.h +++ b/tests/api/test_ossl_p7p12.h @@ -38,6 +38,7 @@ int test_wolfSSL_SMIME_write_PKCS7(void); int test_wolfSSL_PKCS7_verify_nointern(void); int test_wolfSSL_PKCS12(void); int test_wolfSSL_PKCS12_create_mac(void); +int test_wolfSSL_PKCS12_alias_keyid(void); #define TEST_OSSL_PKCS7_DECLS \ TEST_DECL_GROUP("ossl_p7", test_wolfssl_PKCS7), \ @@ -56,9 +57,10 @@ int test_wolfSSL_PKCS12_create_mac(void); TEST_DECL_GROUP("ossl_smime", test_wolfSSL_SMIME_write_PKCS7), \ TEST_DECL_GROUP("ossl_smime", test_wolfSSL_PKCS7_verify_nointern) -#define TEST_OSSL_PKCS12_DECLS \ - TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12), \ - TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12_create_mac) +#define TEST_OSSL_PKCS12_DECLS \ + TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12), \ + TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12_create_mac), \ + TEST_DECL_GROUP("ossl_p12", test_wolfSSL_PKCS12_alias_keyid) #endif /* WOLFCRYPT_TEST_OSSL_P7P12_H */ diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index d2ac234bc70..ac3a5497cd4 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -60,6 +60,11 @@ static const byte WC_PKCS12_KeyBag_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0c, 0x0a, 0x01, 0x01}; static const byte WC_PKCS12_ShroudedKeyBag_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0c, 0x0a, 0x01, 0x02}; +/* PKCS#9 friendlyName and localKeyId bag attributes */ +static const byte WC_PKCS12_FriendlyName_OID[] = + {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x14}; +static const byte WC_PKCS12_LocalKeyId_OID[] = + {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x15}; typedef struct ContentInfo { @@ -1342,6 +1347,126 @@ static int PKCS12_CoalesceOctetStrings(WC_PKCS12* pkcs12, byte* data, } #endif +/* friendlyName and localKeyId bag attributes of a cert */ +typedef struct Pkcs12CertAttrs { + const byte* cert; + byte* friendlyName; + word32 friendlyNameSz; + byte* localKeyId; + word32 localKeyIdSz; + struct Pkcs12CertAttrs* next; +} Pkcs12CertAttrs; + +static void PKCS12_FreeCertAttrs(Pkcs12CertAttrs* attrs, void* heap) +{ + while (attrs != NULL) { + Pkcs12CertAttrs* next = attrs->next; + XFREE(attrs->friendlyName, heap, DYNAMIC_TYPE_PKCS); + XFREE(attrs->localKeyId, heap, DYNAMIC_TYPE_PKCS); + XFREE(attrs, heap, DYNAMIC_TYPE_PKCS); + attrs = next; + } + (void)heap; +} + +/* Copy the value of an attribute when it has the expected tag. */ +static int PKCS12_CopyAttrValue(const byte* data, word32 idx, word32 maxIdx, + byte expTag, byte** out, word32* outSz, void* heap) +{ + int ret = 0; + int len = 0; + byte tag = 0; + + if ((*out != NULL) || (GetASNTag(data, &idx, &tag, maxIdx) < 0) || + (tag != expTag) || (GetLength(data, &idx, &len, maxIdx) < 0)) { + /* Not used: a second attribute or another type. */ + return 0; + } + *out = (byte*)XMALLOC((size_t)len + 1, heap, DYNAMIC_TYPE_PKCS); + if (*out == NULL) { + ret = MEMORY_E; + } + else { + XMEMCPY(*out, data + idx, (size_t)len); + *outSz = (word32)len; + } + (void)heap; + return ret; +} + +/* Get the friendlyName and localKeyId bag attributes of a cert. + * PKCS#12 (RFC 7292), 4.2: SafeBag bagAttributes SET OF PKCS12Attribute. + * Bad attributes are skipped as other attributes are. + * + * data : decoded content + * idx : index of the attributes after the bag value + * bagEnd : index of the end of the bag + * cert : cert of the bag + * list : list of the attributes of the certs, the new entry is added + */ +static int PKCS12_GetCertAttrs(const byte* data, word32 idx, word32 bagEnd, + const byte* cert, Pkcs12CertAttrs** list, void* heap) +{ + int ret = 0; + int len = 0; + int oidSz = 0; + word32 setEnd; + word32 attrEnd; + word32 oidIdx; + Pkcs12CertAttrs* attrs; + + if ((idx >= bagEnd) || (GetSet(data, &idx, &len, bagEnd) < 0)) { + return 0; + } + setEnd = idx + (word32)len; + + attrs = (Pkcs12CertAttrs*)XMALLOC(sizeof(Pkcs12CertAttrs), heap, + DYNAMIC_TYPE_PKCS); + if (attrs == NULL) { + return MEMORY_E; + } + XMEMSET(attrs, 0, sizeof(Pkcs12CertAttrs)); + attrs->cert = cert; + + while ((ret == 0) && (idx < setEnd)) { + if (GetSequence(data, &idx, &len, setEnd) < 0) { + break; + } + attrEnd = idx + (word32)len; + if (GetASNObjectId(data, &idx, &oidSz, attrEnd) < 0) { + break; + } + oidIdx = idx; + idx += (word32)oidSz; + /* The first of the attribute values is used. */ + if (GetSet(data, &idx, &len, attrEnd) >= 0) { + if ((oidSz == (int)sizeof(WC_PKCS12_FriendlyName_OID)) && + (XMEMCMP(data + oidIdx, WC_PKCS12_FriendlyName_OID, + sizeof(WC_PKCS12_FriendlyName_OID)) == 0)) { + ret = PKCS12_CopyAttrValue(data, idx, attrEnd, ASN_BMPSTRING, + &attrs->friendlyName, &attrs->friendlyNameSz, heap); + } + else if ((oidSz == (int)sizeof(WC_PKCS12_LocalKeyId_OID)) && + (XMEMCMP(data + oidIdx, WC_PKCS12_LocalKeyId_OID, + sizeof(WC_PKCS12_LocalKeyId_OID)) == 0)) { + ret = PKCS12_CopyAttrValue(data, idx, attrEnd, + ASN_OCTET_STRING, &attrs->localKeyId, + &attrs->localKeyIdSz, heap); + } + } + idx = attrEnd; + } + + if (ret != 0) { + PKCS12_FreeCertAttrs(attrs, heap); + } + else { + attrs->next = *list; + *list = attrs; + } + return ret; +} + /* return 0 on success and negative on failure. * By side effect returns private key, cert, and optionally ca. * Parses and decodes the parts of PKCS12 @@ -1379,10 +1504,28 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, byte** pkey, word32* pkeySz, byte** cert, word32* certSz, WC_DerCertList** ca, int keepKeyHeader) +{ + return wc_PKCS12_parse_ex2(pkcs12, psw, pkey, pkeySz, cert, certSz, ca, + keepKeyHeader, NULL, NULL, NULL, NULL); +} + +/* As wc_PKCS12_parse_ex, and also returns the bag attributes of cert. + * + * friendlyName : optional BMPString of the friendlyName returned, NULL + * when cert has none + * localKeyId : optional localKeyId returned, NULL when cert has none + */ +int wc_PKCS12_parse_ex2(WC_PKCS12* pkcs12, const char* psw, + byte** pkey, word32* pkeySz, byte** cert, word32* certSz, + WC_DerCertList** ca, int keepKeyHeader, + byte** friendlyName, word32* friendlyNameSz, + byte** localKeyId, word32* localKeyIdSz) { ContentInfo* ci = NULL; WC_DerCertList* certList = NULL; WC_DerCertList* tailList = NULL; + Pkcs12CertAttrs* attrs = NULL; + int wantAttrs = (friendlyName != NULL) || (localKeyId != NULL); byte* buf = NULL; word32 bufSz = 0; word32 i, oid; @@ -1405,6 +1548,14 @@ int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, *pkey = NULL; if (ca != NULL) *ca = NULL; + if (friendlyName != NULL) { + *friendlyName = NULL; + *friendlyNameSz = 0; + } + if (localKeyId != NULL) { + *localKeyId = NULL; + *localKeyIdSz = 0; + } /* if there is sign data then verify the MAC */ if (pkcs12->signData != NULL ) { @@ -1785,6 +1936,14 @@ int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, /* on to next */ idx += (word32)size; + + if (wantAttrs) { + ret = PKCS12_GetCertAttrs(data, idx, (word32)bagSz, + node->buffer, &attrs, pkcs12->heap); + if (ret != 0) { + goto exit_pk12par; + } + } } break; @@ -1829,6 +1988,26 @@ int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, if (ret < 0) goto exit_pk12par; } + if (*cert != NULL) { + Pkcs12CertAttrs* cur; + + for (cur = attrs; cur != NULL; cur = cur->next) { + if (cur->cert != *cert) { + continue; + } + if (friendlyName != NULL) { + *friendlyName = cur->friendlyName; + *friendlyNameSz = cur->friendlyNameSz; + cur->friendlyName = NULL; + } + if (localKeyId != NULL) { + *localKeyId = cur->localKeyId; + *localKeyIdSz = cur->localKeyIdSz; + cur->localKeyId = NULL; + } + break; + } + } /* if ca arg provided return certList, otherwise free it */ if (ca != NULL) { @@ -1859,6 +2038,7 @@ int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, wc_FreeCertList(certList, pkcs12->heap); } + PKCS12_FreeCertAttrs(attrs, pkcs12->heap); return ret; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 347a42cbba2..29307857ef0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6367,6 +6367,13 @@ struct WOLFSSL_X509 { #endif /* WOLFSSL_QT || OPENSSL_ALL */ #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) WOLFSSL_ASN1_INTEGER* serialNumber; /* Stores SN from wolfSSL_X509_get_serialNumber */ +#endif +#ifdef OPENSSL_EXTRA + /* friendlyName, as UTF-8, and localKeyId from a PKCS#12 bag */ + byte* alias; + int aliasSz; + byte* keyid; + int keyidSz; #endif WOLFSSL_ASN1_TIME notBefore; WOLFSSL_ASN1_TIME notAfter; diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 005534f0a74..7b41a6d95f4 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -656,6 +656,8 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define X509_add1_ext_i2d wolfSSL_X509_add1_ext_i2d #define X509_delete_ext wolfSSL_X509_delete_ext #define X509_get0_subject_key_id wolfSSL_X509_get0_subject_key_id +#define X509_alias_get0 wolfSSL_X509_alias_get0 +#define X509_keyid_get0 wolfSSL_X509_keyid_get0 #define X509_EXTENSION_get_object wolfSSL_X509_EXTENSION_get_object #define X509_EXTENSION_get_data wolfSSL_X509_EXTENSION_get_data diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index c944e42a764..509e12cd49c 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2509,6 +2509,12 @@ WOLFSSL_API unsigned char* wolfSSL_X509_get_subjectKeyID( WOLFSSL_X509* x509, unsigned char* dst, int* dstLen); WOLFSSL_API const WOLFSSL_ASN1_STRING *wolfSSL_X509_get0_subject_key_id( WOLFSSL_X509 *x509); +#ifdef OPENSSL_EXTRA +WOLFSSL_API unsigned char* wolfSSL_X509_alias_get0(WOLFSSL_X509* x509, + int* len); +WOLFSSL_API unsigned char* wolfSSL_X509_keyid_get0(WOLFSSL_X509* x509, + int* len); +#endif WOLFSSL_API int wolfSSL_X509_verify(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey); #ifdef WOLFSSL_CERT_REQ diff --git a/wolfssl/wolfcrypt/pkcs12.h b/wolfssl/wolfcrypt/pkcs12.h index 4ebd1713770..73c17d282a6 100644 --- a/wolfssl/wolfcrypt/pkcs12.h +++ b/wolfssl/wolfcrypt/pkcs12.h @@ -58,6 +58,11 @@ WOLFSSL_API int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, WOLFSSL_API int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw, byte** pkey, word32* pkeySz, byte** cert, word32* certSz, WC_DerCertList** ca, int keepKeyHeader); +WOLFSSL_LOCAL int wc_PKCS12_parse_ex2(WC_PKCS12* pkcs12, const char* psw, + byte** pkey, word32* pkeySz, byte** cert, word32* certSz, + WC_DerCertList** ca, int keepKeyHeader, + byte** friendlyName, word32* friendlyNameSz, + byte** localKeyId, word32* localKeyIdSz); WOLFSSL_LOCAL int wc_PKCS12_verify_ex(WC_PKCS12* pkcs12, const byte* psw, word32 pswSz); WOLFSSL_API WC_PKCS12* wc_PKCS12_create(char* pass, word32 passSz, From 7ba4719644928a0f605e6db2a686b3ffbedc7b73 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:22:04 +0000 Subject: [PATCH 16/31] Add CRYPTO_get_mem_functions Returns what CRYPTO_set_mem_functions set, else functions using the C library. These can be wrapped and the wrappers passed to CRYPTO_set_mem_functions without recursion. Getting them changes nothing. Where CRYPTO_set_mem_functions is not supported they allocate as OPENSSL_malloc does. --- doc/dox_comments/header_files/ssl.h | 42 ++++++++ src/ssl.c | 90 +++++++++++++++++ tests/api.c | 151 ++++++++++++++++++++++++++++ wolfssl/openssl/crypto.h | 5 + wolfssl/ssl.h | 4 + 5 files changed, 292 insertions(+) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 777e649e719..9be9564c897 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -17145,6 +17145,48 @@ int wolfSSL_CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, WOLFSSL_CRYPTO_EX_dup* dup_func, WOLFSSL_CRYPTO_EX_free* free_func); +/*! + \ingroup SSL + \brief Get the memory functions set with CRYPTO_set_mem_functions, as + OpenSSL 3.0's CRYPTO_get_mem_functions. Each output may be NULL. + + When none are set, the returned functions use the C library. They do + not call back into wolfSSL, so wrappers of them may be passed to + CRYPTO_set_mem_functions. + + CRYPTO_set_mem_functions needs USE_WOLFSSL_MEMORY and no + WOLFSSL_STATIC_MEMORY. Otherwise it fails and the returned functions + allocate as OPENSSL_malloc does. + + \param [out] m Malloc function. May be NULL. + \param [out] r Realloc function. May be NULL. + \param [out] f Free function. May be NULL. + + _Example_ + \code + static CRYPTO_malloc_fn prev_malloc; + static CRYPTO_realloc_fn prev_realloc; + static CRYPTO_free_fn prev_free; + + static void* my_malloc(size_t sz, const char* file, int line) + { + return prev_malloc(sz, file, line); + } + // my_realloc and my_free alike + + CRYPTO_get_mem_functions(&prev_malloc, &prev_realloc, &prev_free); + CRYPTO_set_mem_functions(my_malloc, my_realloc, my_free); + // ... + CRYPTO_set_mem_functions(prev_malloc, prev_realloc, prev_free); + \endcode + + \sa wolfSSL_CRYPTO_set_mem_functions + \sa wolfSSL_SetAllocators +*/ +void wolfSSL_CRYPTO_get_mem_functions(wolfSSL_OSSL_Malloc_cb* m, + wolfSSL_OSSL_Realloc_cb* r, + wolfSSL_OSSL_Free_cb* f); + /*! \ingroup Setup \brief In case this function is called in a client side, set certificate types diff --git a/src/ssl.c b/src/ssl.c index 3773badb1d9..1b5071405ee 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8915,6 +8915,45 @@ static void* OSSL_Realloc(void *ptr, size_t size) #endif /* USE_WOLFSSL_MEMORY && !WOLFSSL_DEBUG_MEMORY && * !WOLFSSL_STATIC_MEMORY */ +/* The allocators CRYPTO_get_mem_functions returns when none are set. They + * don't call into wolfSSL's allocator, so a wrapper of them passed to + * CRYPTO_set_mem_functions does not recurse. */ +#if !defined(USE_WOLFSSL_MEMORY) || defined(WOLFSSL_STATIC_MEMORY) + #define OSSL_DEF_MALLOC(s) XMALLOC(s, NULL, DYNAMIC_TYPE_OPENSSL) + #define OSSL_DEF_REALLOC(p, s) XREALLOC(p, s, NULL, DYNAMIC_TYPE_OPENSSL) + #define OSSL_DEF_FREE(p) XFREE(p, NULL, DYNAMIC_TYPE_OPENSSL) +#elif !defined(WOLFSSL_NO_MALLOC) + #define OSSL_DEF_MALLOC(s) malloc(s) /* native heap */ + #define OSSL_DEF_REALLOC(p, s) realloc(p, s) /* native heap */ + #define OSSL_DEF_FREE(p) free(p) /* native heap */ +#else + #define OSSL_DEF_MALLOC(s) ((void)(s), (void*)NULL) + #define OSSL_DEF_REALLOC(p, s) ((void)(p), (void)(s), (void*)NULL) + #define OSSL_DEF_FREE(p) (void)(p) +#endif + +static void* OSSL_DefMalloc(size_t size, const char* file, int line) +{ + (void)file; + (void)line; + return OSSL_DEF_MALLOC(size); +} + +static void* OSSL_DefRealloc(void* ptr, size_t size, const char* file, + int line) +{ + (void)file; + (void)line; + return OSSL_DEF_REALLOC(ptr, size); +} + +static void OSSL_DefFree(void* ptr, const char* file, int line) +{ + (void)file; + (void)line; + OSSL_DEF_FREE(ptr); +} + int wolfSSL_CRYPTO_set_mem_functions( wolfSSL_OSSL_Malloc_cb m, wolfSSL_OSSL_Realloc_cb r, @@ -8950,6 +8989,57 @@ int wolfSSL_CRYPTO_set_mem_functions( #endif } +/* Get the allocators set with CRYPTO_set_mem_functions, as in OpenSSL 3.0. + * + * When none are set, the returned functions use the C library. They can be + * wrapped and the wrappers passed to CRYPTO_set_mem_functions. + * Where that is not supported they allocate as OPENSSL_malloc does. + * + * @param [out] m Malloc function. May be NULL. + * @param [out] r Realloc function. May be NULL. + * @param [out] f Free function. May be NULL. + */ +void wolfSSL_CRYPTO_get_mem_functions( + wolfSSL_OSSL_Malloc_cb* m, + wolfSSL_OSSL_Realloc_cb* r, + wolfSSL_OSSL_Free_cb* f) +{ + wolfSSL_OSSL_Malloc_cb mf = OSSL_DefMalloc; + wolfSSL_OSSL_Realloc_cb rf = OSSL_DefRealloc; + wolfSSL_OSSL_Free_cb ff = OSSL_DefFree; + +#if defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY) && \ + !defined(WOLFSSL_STATIC_MEMORY) + if (ossl_malloc != NULL) { + mf = ossl_malloc; + rf = ossl_realloc; + ff = ossl_free; + } +#elif defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) + { + wolfSSL_Malloc_cb wmf = NULL; + wolfSSL_Free_cb wff = NULL; + wolfSSL_Realloc_cb wrf = NULL; + + /* CRYPTO_set_mem_functions sets them as wolfSSL's allocators. */ + (void)wolfSSL_GetAllocators(&wmf, &wff, &wrf); + if (wmf != NULL) + mf = (wolfSSL_OSSL_Malloc_cb)wmf; + if (wrf != NULL) + rf = (wolfSSL_OSSL_Realloc_cb)wrf; + if (wff != NULL) + ff = (wolfSSL_OSSL_Free_cb)wff; + } +#endif + + if (m != NULL) + *m = mf; + if (r != NULL) + *r = rf; + if (f != NULL) + *f = ff; +} + int wolfSSL_FIPS_mode(void) { #ifdef HAVE_FIPS diff --git a/tests/api.c b/tests/api.c index 9dbfb03a6f7..d65a6be154b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -28181,6 +28181,156 @@ static int test_wolfSSL_CRYPTO_memcmp(void) return EXPECT_RESULT(); } +#ifdef OPENSSL_EXTRA +static CRYPTO_malloc_fn test_mem_prev_malloc = NULL; +static CRYPTO_realloc_fn test_mem_prev_realloc = NULL; +static CRYPTO_free_fn test_mem_prev_free = NULL; +static int test_mem_allocs = 0; +static int test_mem_frees = 0; + +static void* test_mem_malloc(size_t sz, const char* file, int line) +{ + test_mem_allocs++; + return test_mem_prev_malloc(sz, file, line); +} + +static void* test_mem_realloc(void* p, size_t sz, const char* file, int line) +{ + if (p == NULL) + test_mem_allocs++; + return test_mem_prev_realloc(p, sz, file, line); +} + +static void test_mem_free(void* p, const char* file, int line) +{ + if (p != NULL) + test_mem_frees++; + test_mem_prev_free(p, file, line); +} + +#if defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) && \ + !defined(WOLFSSL_DEBUG_MEMORY) +/* A native wrapper of wolfSSL's allocators that stops a loop. */ +static wolfSSL_Malloc_cb test_mem_native_malloc = NULL; +static wolfSSL_Free_cb test_mem_native_free = NULL; +static wolfSSL_Realloc_cb test_mem_native_realloc = NULL; +static int test_mem_native_depth = 0; +static int test_mem_native_loop = 0; + +static void* test_mem_native_wrap_malloc(size_t sz) +{ + void* p = NULL; + + if (++test_mem_native_depth > 1) + test_mem_native_loop = 1; + else + p = test_mem_native_malloc(sz); + test_mem_native_depth--; + return p; +} + +static void* test_mem_native_wrap_realloc(void* p, size_t sz) +{ + void* q = NULL; + + if (++test_mem_native_depth > 1) + test_mem_native_loop = 1; + else + q = test_mem_native_realloc(p, sz); + test_mem_native_depth--; + return q; +} + +static void test_mem_native_wrap_free(void* p) +{ + if (++test_mem_native_depth > 1) + test_mem_native_loop = 1; + else + test_mem_native_free(p); + test_mem_native_depth--; +} +#endif +#endif + +static int test_wolfSSL_CRYPTO_get_mem_functions(void) +{ + EXPECT_DECLS; +#ifdef OPENSSL_EXTRA + CRYPTO_malloc_fn m = NULL; + CRYPTO_realloc_fn r = NULL; + CRYPTO_free_fn f = NULL; + void* p = NULL; + void* q = NULL; + int ret = 0; +#if defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) + X509* x509 = NULL; +#endif + + CRYPTO_get_mem_functions(NULL, NULL, NULL); + CRYPTO_get_mem_functions(&test_mem_prev_malloc, &test_mem_prev_realloc, + &test_mem_prev_free); + ExpectTrue(test_mem_prev_malloc != NULL); + ExpectTrue(test_mem_prev_realloc != NULL); + ExpectTrue(test_mem_prev_free != NULL); + + /* The returned functions can be called directly. */ + if (EXPECT_SUCCESS()) { + ExpectNotNull(p = test_mem_prev_malloc(16, __FILE__, __LINE__)); + ExpectNotNull(q = test_mem_prev_realloc(p, 32, __FILE__, __LINE__)); + if (q != NULL) + p = q; + test_mem_prev_free(p, __FILE__, __LINE__); + p = NULL; + } + + /* Wrap them, allocate through wolfSSL and restore them. */ + if (EXPECT_SUCCESS()) { + ret = CRYPTO_set_mem_functions(test_mem_malloc, test_mem_realloc, + test_mem_free); + } +#if defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) + ExpectIntEQ(ret, 1); + CRYPTO_get_mem_functions(&m, &r, &f); + ExpectTrue(m == test_mem_malloc); + ExpectTrue(r == test_mem_realloc); + ExpectTrue(f == test_mem_free); + ExpectNotNull(x509 = X509_new()); + X509_free(x509); + ExpectNotNull(p = OPENSSL_malloc(8)); + OPENSSL_free(p); +#ifndef WOLFSSL_DEBUG_MEMORY + /* Getting them while a native wrapper is installed changes nothing. */ + if (EXPECT_SUCCESS()) { + (void)wolfSSL_GetAllocators(&test_mem_native_malloc, + &test_mem_native_free, &test_mem_native_realloc); + ExpectIntEQ(wolfSSL_SetAllocators(test_mem_native_wrap_malloc, + test_mem_native_wrap_free, test_mem_native_wrap_realloc), 0); + CRYPTO_get_mem_functions(&m, &r, &f); + ExpectIntEQ(wolfSSL_SetAllocators(test_mem_native_malloc, + test_mem_native_free, test_mem_native_realloc), 0); + } + ExpectNotNull(p = OPENSSL_malloc(8)); + OPENSSL_free(p); + ExpectIntEQ(test_mem_native_loop, 0); +#endif + if (ret == 1) { + ret = CRYPTO_set_mem_functions(test_mem_prev_malloc, + test_mem_prev_realloc, test_mem_prev_free); + } + ExpectIntEQ(ret, 1); + ExpectIntGT(test_mem_allocs, 1); + ExpectIntEQ(test_mem_allocs, test_mem_frees); +#else + ExpectIntEQ(ret, 0); +#endif + CRYPTO_get_mem_functions(&m, &r, &f); + ExpectTrue(m == test_mem_prev_malloc); + ExpectTrue(r == test_mem_prev_realloc); + ExpectTrue(f == test_mem_prev_free); +#endif + return EXPECT_RESULT(); +} + /*----------------------------------------------------------------------------* | wolfCrypt ASN *----------------------------------------------------------------------------*/ @@ -47516,6 +47666,7 @@ TEST_CASE testCases[] = { #endif TEST_DECL(test_wolfSSL_CRYPTO_memcmp), + TEST_DECL(test_wolfSSL_CRYPTO_get_mem_functions), TEST_DECL(test_wolfSSL_CRYPTO_get_ex_new_index), TEST_DECL(test_CRYPTO_set_dynlock_xxx), TEST_DECL(test_CRYPTO_THREADID_xxx), diff --git a/wolfssl/openssl/crypto.h b/wolfssl/openssl/crypto.h index cef908f1539..d8391d43aa6 100644 --- a/wolfssl/openssl/crypto.h +++ b/wolfssl/openssl/crypto.h @@ -113,6 +113,11 @@ WOLFSSL_API int wolfSSL_OPENSSL_init_crypto(word64 opts, const WOLFSSL_INIT_SETT /* this function was used to set the default malloc, free, and realloc */ #define CRYPTO_malloc_init() 0 /* CRYPTO_malloc_init is not needed */ +#define CRYPTO_malloc_fn wolfSSL_OSSL_Malloc_cb +#define CRYPTO_realloc_fn wolfSSL_OSSL_Realloc_cb +#define CRYPTO_free_fn wolfSSL_OSSL_Free_cb +#define CRYPTO_get_mem_functions wolfSSL_CRYPTO_get_mem_functions + #define OPENSSL_free wolfSSL_OPENSSL_free #define OPENSSL_malloc wolfSSL_OPENSSL_malloc #define OPENSSL_hexchar2int wolfSSL_OPENSSL_hexchar2int diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 509e12cd49c..31ba14ed011 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -5988,6 +5988,10 @@ WOLFSSL_API int wolfSSL_CRYPTO_set_mem_functions( wolfSSL_OSSL_Malloc_cb m, wolfSSL_OSSL_Realloc_cb r, wolfSSL_OSSL_Free_cb f); +WOLFSSL_API void wolfSSL_CRYPTO_get_mem_functions( + wolfSSL_OSSL_Malloc_cb* m, + wolfSSL_OSSL_Realloc_cb* r, + wolfSSL_OSSL_Free_cb* f); WOLFSSL_API int wolfSSL_CRYPTO_set_mem_ex_functions(void *(*m) (size_t, const char *, int), void *(*r) (void *, size_t, const char *, int), void (*f) (void *)); From 6841dccad3858054abe6e478a46470e26e226855 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 11:23:29 +0000 Subject: [PATCH 17/31] Make X509_chain_up_ref reference the certs instead of copying them OpenSSL's X509_chain_up_ref returns a new stack of the same certs, each with its reference count raised; wolfSSL's copied every cert. Code ported from OpenSSL expects the same objects. Changes to a returned cert now apply to the original, as in OpenSSL. X509_STORE_CTX_get1_chain uses it, so it returns the chain's certs, as in OpenSSL. --- src/x509.c | 24 +++++++++++++-- src/x509_str.c | 2 +- tests/api/test_ossl_sk.c | 54 ++++++++++++++++++++++++++++++++++ tests/api/test_ossl_sk.h | 2 ++ tests/api/test_ossl_x509_str.c | 8 +++++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/x509.c b/src/x509.c index 47d8136849c..5db523e3f2c 100644 --- a/src/x509.c +++ b/src/x509.c @@ -13094,8 +13094,28 @@ int wolfSSL_X509_up_ref(WOLFSSL_X509* x509) WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_X509_chain_up_ref( WOLF_STACK_OF(WOLFSSL_X509)* chain) { - /* wolfSSL_sk_dup takes care of doing a deep copy */ - return wolfSSL_sk_dup(chain); + WOLFSSL_STACK* ret; + WOLFSSL_STACK* node; + WOLFSSL_STACK* fail = NULL; + + /* As OpenSSL: the same certs, each with a new reference. */ + ret = wolfSSL_shallow_sk_dup(chain); + for (node = ret; node != NULL; node = node->next) { + if ((node->data.x509 != NULL) && + (wolfSSL_X509_up_ref(node->data.x509) != WOLFSSL_SUCCESS)) { + fail = node; + break; + } + } + if (fail != NULL) { + for (node = ret; node != fail; node = node->next) { + wolfSSL_X509_free(node->data.x509); + } + wolfSSL_sk_free(ret); + ret = NULL; + } + + return ret; } #endif diff --git a/src/x509_str.c b/src/x509_str.c index e2e74d0d275..5c81055c366 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -1686,7 +1686,7 @@ WOLFSSL_STACK* wolfSSL_X509_STORE_CTX_get1_chain(WOLFSSL_X509_STORE_CTX* ctx) } /* create duplicate of ctx chain */ - return wolfSSL_sk_dup(ref); + return wolfSSL_X509_chain_up_ref(ref); } #ifndef NO_WOLFSSL_STUB diff --git a/tests/api/test_ossl_sk.c b/tests/api/test_ossl_sk.c index fae469582e2..e9feca3bbcf 100644 --- a/tests/api/test_ossl_sk.c +++ b/tests/api/test_ossl_sk.c @@ -407,6 +407,60 @@ int test_wolfSSL_shallow_sk_dup(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_chain_up_ref(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL; + WOLF_STACK_OF(WOLFSSL_X509)* copy = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509* srv = NULL; + WOLFSSL_X509* x509; + unsigned char* der = NULL; + int i; + + ExpectNull(X509_chain_up_ref(NULL)); + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(srv = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(chain = sk_X509_new_null()); + ExpectIntEQ(sk_X509_push(chain, srv), 1); + if (EXPECT_FAIL()) + X509_free(srv); + ExpectIntEQ(sk_X509_push(chain, ca), 2); + if (EXPECT_FAIL()) + X509_free(ca); + + /* Same certs in the same order. */ + ExpectNotNull(copy = X509_chain_up_ref(chain)); + ExpectIntEQ(sk_X509_num(copy), 2); + ExpectPtrEq(sk_X509_value(copy, 0), srv); + ExpectPtrEq(sk_X509_value(copy, 1), ca); + + /* The copy holds its own references. */ + sk_X509_pop_free(chain, X509_free); + chain = NULL; + for (i = 0; i < 2; i++) { + x509 = sk_X509_value(copy, i); + ExpectNotNull(X509_get_subject_name(x509)); + ExpectIntGT(i2d_X509(x509, &der), 0); + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + der = NULL; + } + sk_X509_pop_free(copy, X509_free); + + /* An empty chain gives an empty copy. */ + ExpectNotNull(chain = sk_X509_new_null()); + ExpectNotNull(copy = X509_chain_up_ref(chain)); + ExpectIntEQ(sk_X509_num(copy), 0); + sk_X509_pop_free(copy, X509_free); + sk_X509_pop_free(chain, X509_free); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_sk_num(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_sk.h b/tests/api/test_ossl_sk.h index ab54370b8c6..dff0a06b064 100644 --- a/tests/api/test_ossl_sk.h +++ b/tests/api/test_ossl_sk.h @@ -34,6 +34,7 @@ int test_wolfSSL_sk_delete_ptr(void); int test_wolfSSL_sk_X509_insert_delete_ptr(void); int test_wolfSSL_sk_sort(void); int test_wolfSSL_shallow_sk_dup(void); +int test_wolfSSL_X509_chain_up_ref(void); int test_wolfSSL_sk_num(void); int test_wolfSSL_sk_value(void); int test_wolfssl_sk_GENERIC(void); @@ -62,6 +63,7 @@ int test_wolfSSL_CIPHER_current(void); test_wolfSSL_sk_X509_insert_delete_ptr), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_sort), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_shallow_sk_dup), \ + TEST_DECL_GROUP("ossl_sk", test_wolfSSL_X509_chain_up_ref), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_num), \ TEST_DECL_GROUP("ossl_sk", test_wolfSSL_sk_value), \ TEST_DECL_GROUP("ossl_sk", test_wolfssl_sk_GENERIC), \ diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index b8e5b53f81f..98a55003e31 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -906,6 +906,7 @@ static int test_wolfSSL_X509_STORE_CTX_ex1(X509_STORE_test_data *testData) X509_STORE* store = NULL; X509_STORE_CTX* ctx = NULL; STACK_OF(X509)* chain = NULL; + STACK_OF(X509)* chain1 = NULL; int i = 0; /* Test case 1, add X509 certs to store and verify */ @@ -922,6 +923,13 @@ static int test_wolfSSL_X509_STORE_CTX_ex1(X509_STORE_test_data *testData) ExpectIntEQ(X509_cmp(sk_X509_value(chain, i), sk_X509_value(testData->expectedChain, i)), 0); } + /* get1_chain references the same certs. */ + ExpectNotNull(chain1 = X509_STORE_CTX_get1_chain(ctx)); + ExpectIntEQ(sk_X509_num(chain1), sk_X509_num(chain)); + for (i = 0; i < sk_X509_num(chain1); i++) { + ExpectPtrEq(sk_X509_value(chain1, i), sk_X509_value(chain, i)); + } + sk_X509_pop_free(chain1, X509_free); X509_STORE_CTX_free(ctx); X509_STORE_free(store); return EXPECT_RESULT(); From b8de6b70068bc3b25b329eafd10dde553788177e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:22:36 +0000 Subject: [PATCH 18/31] Add X509_STORE_CTX_set_app_data and get_app_data Map them to X509_STORE_CTX ex_data index 1. OpenSSL uses index 0, but wolfSSL keeps the SSL there in a TLS verify callback, so get_ex_data(ctx, 0) is not the app data. They need HAVE_EX_DATA. Init and cleanup clear the app data. Unlike in OpenSSL, it does not carry over to the next TLS verify callback of a handshake, as wolfSSL gives each cert its own context. --- tests/api/test_ossl_x509_str.c | 97 ++++++++++++++++++++++++++++++++++ wolfssl/openssl/ssl.h | 10 ++++ 2 files changed, 107 insertions(+) diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 98a55003e31..cf567fbdba3 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -737,6 +737,73 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) } #endif +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(NO_TLS) +static WOLFSSL* test_app_data_ssl; +static int test_app_data_calls; +static int test_app_data_bad; + +/* App data starts NULL and is apart from the SSL. */ +static int test_app_data_verify_cb(int ok, X509_STORE_CTX* ctx) +{ + static int data; + int idx = SSL_get_ex_data_X509_STORE_CTX_idx(); + + test_app_data_calls++; + if ((test_app_data_calls == 1) && + (X509_STORE_CTX_get_app_data(ctx) != NULL)) { + test_app_data_bad = 1; + } +#ifdef HAVE_EX_DATA + if ((X509_STORE_CTX_set_app_data(ctx, &data) != WOLFSSL_SUCCESS) || + (X509_STORE_CTX_get_app_data(ctx) != &data) || + (X509_STORE_CTX_get_ex_data(ctx, idx) != test_app_data_ssl)) { + test_app_data_bad = 1; + } +#else + (void)idx; + if ((X509_STORE_CTX_set_app_data(ctx, &data) != + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) || + (X509_STORE_CTX_get_app_data(ctx) != NULL)) { + test_app_data_bad = 1; + } +#endif + return ok; +} + +static int test_X509_STORE_CTX_app_data_tls(void) +{ + EXPECT_DECLS; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); +#else + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); +#endif + test_app_data_ssl = ssl_c; + test_app_data_calls = 0; + test_app_data_bad = 0; + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_app_data_verify_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntGT(test_app_data_calls, 0); + ExpectIntEQ(test_app_data_bad, 0); + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + return EXPECT_RESULT(); +} +#endif + int test_wolfSSL_X509_STORE_CTX(void) { EXPECT_DECLS; @@ -841,6 +908,36 @@ int test_wolfSSL_X509_STORE_CTX(void) ctx = NULL; } + /* test X509_STORE_CTX_get/set_app_data */ + { + int tmpData = 7; + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectNull(X509_STORE_CTX_get_app_data(ctx)); + #ifdef HAVE_EX_DATA + ExpectIntEQ(X509_STORE_CTX_set_app_data(ctx, &tmpData), + WOLFSSL_SUCCESS); + ExpectPtrEq(X509_STORE_CTX_get_app_data(ctx), &tmpData); + ExpectNull(X509_STORE_CTX_get_ex_data(ctx, 0)); + /* init and cleanup clear it */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, NULL, NULL, NULL), + WOLFSSL_SUCCESS); + ExpectNull(X509_STORE_CTX_get_app_data(ctx)); + ExpectIntEQ(X509_STORE_CTX_set_app_data(ctx, &tmpData), + WOLFSSL_SUCCESS); + X509_STORE_CTX_cleanup(ctx); + ExpectNull(X509_STORE_CTX_get_app_data(ctx)); + #else + ExpectIntEQ(X509_STORE_CTX_set_app_data(ctx, &tmpData), + WC_NO_ERR_TRACE(WOLFSSL_FAILURE)); + ExpectNull(X509_STORE_CTX_get_app_data(ctx)); + #endif + X509_STORE_CTX_free(ctx); + ctx = NULL; + } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) + ExpectIntEQ(test_X509_STORE_CTX_app_data_tls(), TEST_SUCCESS); +#endif + /* test X509_STORE_get/set_ex_data */ { int i = 0, tmpData = 99; diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 7b41a6d95f4..611d84f502c 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -814,6 +814,16 @@ typedef WOLFSSL_X509_NAME_ENTRY X509_NAME_ENTRY; #define X509_STORE_CTX_set_error_depth wolfSSL_X509_STORE_CTX_set_error_depth #define X509_STORE_CTX_get_ex_data wolfSSL_X509_STORE_CTX_get_ex_data #define X509_STORE_CTX_set_ex_data wolfSSL_X509_STORE_CTX_set_ex_data +#ifdef OPENSSL_EXTRA +/* wolfSSL keeps the SSL at index 0 in a TLS verify callback. */ +#define WOLFSSL_X509_STORE_CTX_APP_DATA_IDX 1 +#define X509_STORE_CTX_set_app_data(ctx, data) \ + wolfSSL_X509_STORE_CTX_set_ex_data((ctx), \ + WOLFSSL_X509_STORE_CTX_APP_DATA_IDX, (data)) +#define X509_STORE_CTX_get_app_data(ctx) \ + wolfSSL_X509_STORE_CTX_get_ex_data((ctx), \ + WOLFSSL_X509_STORE_CTX_APP_DATA_IDX) +#endif #define X509_STORE_CTX_set_depth wolfSSL_X509_STORE_CTX_set_depth #define X509_STORE_CTX_verify_cb WOLFSSL_X509_STORE_CTX_verify_cb #define X509_STORE_CTX_get0_current_issuer \ From cd1922e2a18260283c28fd18e769088c100aeeec Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:22:42 +0000 Subject: [PATCH 19/31] Add test certificates for the X509_verify_cert chain checks Certificates, keys and CRLs under certs/intermediate/trust_anchor/ for table-driven X509_verify_cert() tests: trust anchors and their copies, roots and intermediates with fixed validity periods, cross-certs, twins with the same name or key id, CAs without basicConstraints or keyCertSign, v1 roots, forged issuers and CRLs. gen_certs.sh makes certs valid for 30 years from when it runs; the tests use them at the current time only. gen_dated_certs.sh makes the certs with fixed dates. It gives each cert the serial it has in the set, where some serials repeat, and issues each with a CA database of its own. check_dates.sh lists the ones that are expired on purpose. check_cert_chains.sh checks the certs whose issuer is kept and in date. No code changes. --- certs/check_cert_chains.sh | 25 +- certs/check_dates.sh | 6 + certs/intermediate/include.am | 68 ++- .../trust_anchor/alt-end-cert.pem | 19 + .../trust_anchor/alt-end-int-cert.pem | 19 + .../trust_anchor/alt-int-cert.pem | 19 + .../trust_anchor/alt-int-leaf-cert.pem | 19 + .../trust_anchor/alt-notca-int-cert.pem | 19 + .../trust_anchor/alt-old-int-cert.pem | 19 + .../trust_anchor/alt-root-cert.pem | 19 + .../trust_anchor/badkid-int-cert.pem | 20 + .../trust_anchor/badkid-int-leaf-cert.pem | 20 + .../trust_anchor/cross-root-cert.pem | 20 + .../trust_anchor/cross-root-late-cert.pem | 20 + .../trust_anchor/dated-int-cert.pem | 19 + .../trust_anchor/dated-int-leaf-cert.pem | 19 + .../trust_anchor/dated-root-cert.pem | 20 + .../trust_anchor/dated-root-crl.pem | 12 + .../trust_anchor/dated-root-empty-crl.pem | 11 + .../trust_anchor/dated-root-late-cert.pem | 20 + .../trust_anchor/ec-name-root-cert.pem | 10 + .../trust_anchor/expired-int-cert.pem | 19 + .../trust_anchor/expired-root-cert.pem | 20 + .../trust_anchor/expired-root-int-cert.pem | 20 + .../expired-root-int-leaf-cert.pem | 20 + .../trust_anchor/expired-root-leaf-cert.pem | 20 + .../trust_anchor/expired-root-link-cert.pem | 20 + .../expired-root-link-leaf-cert.pem | 20 + .../trust_anchor/expired-root-nokid-cert.pem | 18 + .../expired-root-old-link-cert.pem | 20 + .../trust_anchor/forge-bad-sub-cert.pem | 20 + .../trust_anchor/forge-fake-int-cert.pem | 19 + .../trust_anchor/forge-fake-int-leaf-cert.pem | 20 + .../trust_anchor/forge-int-cert.pem | 20 + .../trust_anchor/forge-q-cert.pem | 19 + .../trust_anchor/forge-root-cert.pem | 20 + .../trust_anchor/forge-sub-cert.pem | 19 + .../trust_anchor/forge-sub-leaf-cert.pem | 20 + certs/intermediate/trust_anchor/gen_certs.sh | 122 +++++ .../trust_anchor/gen_dated_certs.sh | 432 ++++++++++++++++++ certs/intermediate/trust_anchor/int-crl.pem | 11 + certs/intermediate/trust_anchor/leaf-cert.pem | 20 + certs/intermediate/trust_anchor/leaf-key.pem | 28 ++ certs/intermediate/trust_anchor/link-cert.pem | 20 + certs/intermediate/trust_anchor/link-key.pem | 28 ++ .../trust_anchor/link-noaki-cert.pem | 19 + .../trust_anchor/noaki-root-cert.pem | 19 + .../trust_anchor/noaki-root-leaf-cert.pem | 20 + .../trust_anchor/noaki-root-link-cert.pem | 20 + .../trust_anchor/nobc-root-cert.pem | 18 + .../trust_anchor/nobc-root-leaf-cert.pem | 20 + .../trust_anchor/nokid-root-cert.pem | 18 + .../trust_anchor/notca-root-cert.pem | 19 + .../trust_anchor/notca-root-leaf-cert.pem | 20 + .../trust_anchor/notca-twin-cert.pem | 19 + .../trust_anchor/renewed-root-cert.pem | 20 + .../trust_anchor/renewed-root-leaf-cert.pem | 19 + certs/intermediate/trust_anchor/root-cert.pem | 20 + certs/intermediate/trust_anchor/root-key.pem | 28 ++ certs/intermediate/trust_anchor/self-cert.pem | 20 + .../trust_anchor/self-copy-cert.pem | 20 + certs/intermediate/trust_anchor/self-key.pem | 28 ++ .../trust_anchor/v1-root-cert.pem | 17 + .../trust_anchor/v1-root-leaf-cert.pem | 20 + .../trust_anchor/wrongca-cert.pem | 20 + .../trust_anchor/wrongca-int-cert.pem | 19 + .../trust_anchor/wrongca-int-leaf-cert.pem | 20 + .../trust_anchor/wrongca-old-int-cert.pem | 20 + .../trust_anchor/wrongca-root-cert.pem | 20 + 69 files changed, 1899 insertions(+), 2 deletions(-) create mode 100644 certs/intermediate/trust_anchor/alt-end-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-end-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-notca-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-old-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/alt-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/badkid-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/cross-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/cross-root-late-cert.pem create mode 100644 certs/intermediate/trust_anchor/dated-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/dated-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/dated-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/dated-root-crl.pem create mode 100644 certs/intermediate/trust_anchor/dated-root-empty-crl.pem create mode 100644 certs/intermediate/trust_anchor/dated-root-late-cert.pem create mode 100644 certs/intermediate/trust_anchor/ec-name-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-link-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-nokid-cert.pem create mode 100644 certs/intermediate/trust_anchor/expired-root-old-link-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-bad-sub-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-fake-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-q-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-sub-cert.pem create mode 100644 certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem create mode 100755 certs/intermediate/trust_anchor/gen_certs.sh create mode 100755 certs/intermediate/trust_anchor/gen_dated_certs.sh create mode 100644 certs/intermediate/trust_anchor/int-crl.pem create mode 100644 certs/intermediate/trust_anchor/leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/leaf-key.pem create mode 100644 certs/intermediate/trust_anchor/link-cert.pem create mode 100644 certs/intermediate/trust_anchor/link-key.pem create mode 100644 certs/intermediate/trust_anchor/link-noaki-cert.pem create mode 100644 certs/intermediate/trust_anchor/noaki-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/noaki-root-link-cert.pem create mode 100644 certs/intermediate/trust_anchor/nobc-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/nokid-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/notca-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/notca-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/notca-twin-cert.pem create mode 100644 certs/intermediate/trust_anchor/renewed-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/root-cert.pem create mode 100644 certs/intermediate/trust_anchor/root-key.pem create mode 100644 certs/intermediate/trust_anchor/self-cert.pem create mode 100644 certs/intermediate/trust_anchor/self-copy-cert.pem create mode 100644 certs/intermediate/trust_anchor/self-key.pem create mode 100644 certs/intermediate/trust_anchor/v1-root-cert.pem create mode 100644 certs/intermediate/trust_anchor/v1-root-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/wrongca-cert.pem create mode 100644 certs/intermediate/trust_anchor/wrongca-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem create mode 100644 certs/intermediate/trust_anchor/wrongca-old-int-cert.pem create mode 100644 certs/intermediate/trust_anchor/wrongca-root-cert.pem diff --git a/certs/check_cert_chains.sh b/certs/check_cert_chains.sh index e8b1cd936ed..3f4879fdd14 100755 --- a/certs/check_cert_chains.sh +++ b/certs/check_cert_chains.sh @@ -17,11 +17,34 @@ cd "$(dirname "$0")" || exit 1 # stated here, not parsed, since old OpenSSL cannot decode an ML-DSA cert. A # multi-certificate file is checked on its first certificate, which is the leaf. # Add lines here. +# Of the X509_verify_cert() test certs, those whose issuer is kept and in date. +ta=intermediate/trust_anchor pairs="rsapss/ecc-leaf-rsapss.pem rsapss/ca-rsapss.pem classic mldsa/ecc-leaf-mldsa44.pem mldsa/mldsa44-cert.pem mldsa server-cert-sha1.pem ca-cert.pem sha1 client-cert-sha1.pem ca-cert.pem sha1 -server-cert-sha1-root.pem ca-cert.pem classic" +server-cert-sha1-root.pem ca-cert.pem classic +$ta/link-cert.pem $ta/root-cert.pem classic +$ta/leaf-cert.pem $ta/link-cert.pem classic +$ta/notca-twin-cert.pem $ta/root-cert.pem classic +$ta/dated-int-cert.pem $ta/dated-root-cert.pem classic +$ta/dated-int-leaf-cert.pem $ta/dated-int-cert.pem classic +$ta/v1-root-leaf-cert.pem $ta/v1-root-cert.pem classic +$ta/renewed-root-leaf-cert.pem $ta/renewed-root-cert.pem classic +$ta/expired-root-int-leaf-cert.pem $ta/expired-root-int-cert.pem classic +$ta/expired-root-link-leaf-cert.pem $ta/expired-root-link-cert.pem classic +$ta/noaki-root-link-cert.pem $ta/noaki-root-cert.pem classic +$ta/noaki-root-leaf-cert.pem $ta/noaki-root-cert.pem classic +$ta/forge-int-cert.pem $ta/forge-root-cert.pem classic +$ta/forge-fake-int-cert.pem $ta/forge-q-cert.pem classic +$ta/forge-fake-int-leaf-cert.pem $ta/forge-fake-int-cert.pem classic +$ta/forge-sub-leaf-cert.pem $ta/forge-sub-cert.pem classic +$ta/alt-int-cert.pem $ta/alt-root-cert.pem classic +$ta/alt-end-int-cert.pem $ta/alt-end-cert.pem classic +$ta/alt-notca-int-cert.pem $ta/alt-end-cert.pem classic +$ta/alt-int-leaf-cert.pem $ta/alt-int-cert.pem classic +$ta/wrongca-cert.pem $ta/wrongca-root-cert.pem classic +$ta/wrongca-int-leaf-cert.pem $ta/wrongca-int-cert.pem classic" failed=0 diff --git a/certs/check_dates.sh b/certs/check_dates.sh index 64421e76e98..57432f4d14e 100755 --- a/certs/check_dates.sh +++ b/certs/check_dates.sh @@ -18,6 +18,12 @@ exp_expired="\ /test/expired/expired-ca.pem \ /test/expired/expired-cert.der \ /test/expired/expired-ca.der \ +/intermediate/trust_anchor/expired-root-cert.pem \ +/intermediate/trust_anchor/expired-int-cert.pem \ +/intermediate/trust_anchor/expired-root-old-link-cert.pem \ +/intermediate/trust_anchor/badkid-int-cert.pem \ +/intermediate/trust_anchor/alt-old-int-cert.pem \ +/intermediate/trust_anchor/wrongca-old-int-cert.pem \ /certeccrsa.pem \ /certeccrsa.der " diff --git a/certs/intermediate/include.am b/certs/intermediate/include.am index 23634bd3fda..ab05dbab4de 100644 --- a/certs/intermediate/include.am +++ b/certs/intermediate/include.am @@ -70,4 +70,70 @@ EXTRA_DIST += \ certs/intermediate/untrusted_anchor/loop-b-cert.pem \ certs/intermediate/untrusted_anchor/loop-b-key.pem \ certs/intermediate/untrusted_anchor/loop-leaf-cert.pem \ - certs/intermediate/untrusted_anchor/loop-leaf-key.pem + certs/intermediate/untrusted_anchor/loop-leaf-key.pem \ + certs/intermediate/trust_anchor/gen_certs.sh \ + certs/intermediate/trust_anchor/self-cert.pem \ + certs/intermediate/trust_anchor/self-copy-cert.pem \ + certs/intermediate/trust_anchor/self-key.pem \ + certs/intermediate/trust_anchor/root-cert.pem \ + certs/intermediate/trust_anchor/root-key.pem \ + certs/intermediate/trust_anchor/link-cert.pem \ + certs/intermediate/trust_anchor/link-key.pem \ + certs/intermediate/trust_anchor/link-noaki-cert.pem \ + certs/intermediate/trust_anchor/leaf-cert.pem \ + certs/intermediate/trust_anchor/leaf-key.pem \ + certs/intermediate/trust_anchor/notca-twin-cert.pem \ + certs/intermediate/trust_anchor/gen_dated_certs.sh \ + certs/intermediate/trust_anchor/dated-root-cert.pem \ + certs/intermediate/trust_anchor/dated-root-late-cert.pem \ + certs/intermediate/trust_anchor/expired-root-cert.pem \ + certs/intermediate/trust_anchor/expired-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/expired-int-cert.pem \ + certs/intermediate/trust_anchor/dated-int-cert.pem \ + certs/intermediate/trust_anchor/dated-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/notca-root-cert.pem \ + certs/intermediate/trust_anchor/notca-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/nobc-root-cert.pem \ + certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/v1-root-cert.pem \ + certs/intermediate/trust_anchor/v1-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/cross-root-cert.pem \ + certs/intermediate/trust_anchor/cross-root-late-cert.pem \ + certs/intermediate/trust_anchor/renewed-root-cert.pem \ + certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/expired-root-int-cert.pem \ + certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/expired-root-link-cert.pem \ + certs/intermediate/trust_anchor/expired-root-old-link-cert.pem \ + certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem \ + certs/intermediate/trust_anchor/expired-root-nokid-cert.pem \ + certs/intermediate/trust_anchor/ec-name-root-cert.pem \ + certs/intermediate/trust_anchor/nokid-root-cert.pem \ + certs/intermediate/trust_anchor/badkid-int-cert.pem \ + certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/noaki-root-cert.pem \ + certs/intermediate/trust_anchor/noaki-root-link-cert.pem \ + certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem \ + certs/intermediate/trust_anchor/forge-root-cert.pem \ + certs/intermediate/trust_anchor/forge-int-cert.pem \ + certs/intermediate/trust_anchor/forge-fake-int-cert.pem \ + certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/forge-q-cert.pem \ + certs/intermediate/trust_anchor/forge-bad-sub-cert.pem \ + certs/intermediate/trust_anchor/forge-sub-cert.pem \ + certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem \ + certs/intermediate/trust_anchor/alt-root-cert.pem \ + certs/intermediate/trust_anchor/alt-int-cert.pem \ + certs/intermediate/trust_anchor/alt-end-cert.pem \ + certs/intermediate/trust_anchor/alt-end-int-cert.pem \ + certs/intermediate/trust_anchor/alt-old-int-cert.pem \ + certs/intermediate/trust_anchor/alt-notca-int-cert.pem \ + certs/intermediate/trust_anchor/alt-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/wrongca-root-cert.pem \ + certs/intermediate/trust_anchor/wrongca-cert.pem \ + certs/intermediate/trust_anchor/wrongca-int-cert.pem \ + certs/intermediate/trust_anchor/wrongca-old-int-cert.pem \ + certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem \ + certs/intermediate/trust_anchor/int-crl.pem \ + certs/intermediate/trust_anchor/dated-root-empty-crl.pem \ + certs/intermediate/trust_anchor/dated-root-crl.pem diff --git a/certs/intermediate/trust_anchor/alt-end-cert.pem b/certs/intermediate/trust_anchor/alt-end-cert.pem new file mode 100644 index 00000000000..9591fa59e04 --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-end-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhKgAwIBAgICECowDQYJKoZIhvcNAQELBQAwJjEkMCIGA1UEAwwbd29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBHb25lMB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIz +MTIzNTk1OVowJTEjMCEGA1UEAwwad29sZlNTTCBDaGFpbiBUZXN0IEFsdCBFbmQw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0R/M+x1r1rHnuCnfULuKe +PegFtQ13FmEE3skKXksx1xS49WWaDm/qBzRsv2f/XtB5S/MpNvla/V0XJ6ot0hH/ +ZzJbewINOfSJ4n9n83cmvN6upSt1MlXDl5YJ4Ap4GfMBKFtxNwisyE/g7Wjw14HH +TCMctP4HtssnwjhmgiQ4AWsZaRD0eOUlLzR8SUDaAi6l1W9EGvBVJGpzmFv3zhT2 +7VPDmtIwV9P1BdBXxhTY3NVSXEM8QdGZYmtUbW+1s1wmpLQA+XVVFuEdTzm0K2Fq +U1HzDwbLTp0ilN4MD4kH2ypwc32yzn1oAhVCulysNZYBZC/9z67nMQIWTUZcsowj +AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBTwWguQFesj/Hc4vOQnO1RuB4pNvjAfBgNVHSMEGDAWgBSeiHzDRimUgflm +mGWL3evrEbqzWzANBgkqhkiG9w0BAQsFAAOCAQEAupEqyYgrL8PUIaj1m8x1GQ8I +MncaPw2PD+ysKXoLJ3V1yFggl4G/A57ypMkkptTxbfVpz6rKlZ3d3WbBvxBjc/ip +hUD42AkCExs+rICy08e3SsWDuYKAtH2VHzj0j9QgsXsE01v0xGp+icBKA5VTVr+X +2kOFCQd3qDYG5B0XQnhV4itDiyt+c/jOXewGgEAfsT73Jykgu96AbbacYObE5yLM +bMDcle82xKUSysKjE/zj5H/FXy0HEUXW0YGdDbeK78va6eWBhwZBaHCiAfMiJ1ym +FAoXB6Bn+fkEC+GJM7wk1hR2iaqV3Ee3eOnf4zgifr9zmOO7gdFRiOAGlCkn7g== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-end-int-cert.pem b/certs/intermediate/trust_anchor/alt-end-int-cert.pem new file mode 100644 index 00000000000..f1aace8bdc0 --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-end-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgICECswDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBFbmQwHhcNMDAwMTAxMDAwMDAwWhcNNDkxMjMx +MjM1OTU5WjAlMSMwIQYDVQQDDBp3b2xmU1NMIENoYWluIFRlc3QgQWx0IEludDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPLZLmN98lunvgtkdop0AxZ +GCYqpdXRitby2Nfc09x9ZAoEOthKr0KRb5joQ6VyXOdZ6o+fa+gpiEsJV6SKol92 +CSgMo58VI11gF+9jlVGUFoSJ6hMGeCnAzB+iGEIkEDGKG4mfWzuDaTKaWncXOAH8 +imQ1cvjWuNeEV303+vSPtSXopdWlJL7hXHr+bBC6htiPqgfKC/AZoq8Hj2OWOp5U +IbvObU30fKEZkJST+rXavIqS6IQbUHfbYKH+fLePwTEEIQDaOmQIaVoH2daDcDNx +KHtY3lIZnsklmsDEFcRAzkfTEKgVVrHwiketr2siMTWeKwVD/pXA0GlhFQGH5UkC +AwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFCwYox/xByzjdVH1K2RqtdVrXLEsMB8GA1UdIwQYMBaAFPBaC5AV6yP8dzi8 +5Cc7VG4Hik2+MA0GCSqGSIb3DQEBCwUAA4IBAQALYSEv93V84F3FOMMeHASBs24V +n5bMlVmdoCflL4W4g4jIYRWNdcTe+LZ91xb9nMtaUDzcoflo6m/2SetC8Nj01cMP +gH6w4Iw+5XVgRrVGx5gnQhkht7/Tb2JBM1vzWLhe5HYL9TAbrESsdIJRgDVQF9GA +Pm4u8i7EbxCKCZDUzeTZ0rVB3o+x17nq2Ui4hlbBRZ0Dgrct8THCgzHJPxHx7yPQ +8kRcuVkukNBxcEzYoRwoxLi2jIUrmhxPL/kptL0wxqcPrBHUDEUtAi5t0ut0c/wF +2uCdnY1usXnI5r4ByU4fWfWGL8twlDj0kVkVi0cpp7pIOhZ5pHUeecfr60g2 +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-int-cert.pem b/certs/intermediate/trust_anchor/alt-int-cert.pem new file mode 100644 index 00000000000..eb218e15ebd --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhKgAwIBAgICECgwDQYJKoZIhvcNAQELBQAwJjEkMCIGA1UEAwwbd29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBSb290MB4XDTIwMDEwMTAwMDAwMFoXDTQ5MTIz +MTIzNTk1OVowJTEjMCEGA1UEAwwad29sZlNTTCBDaGFpbiBUZXN0IEFsdCBJbnQw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDy2S5jffJbp74LZHaKdAM +WRgmKqXV0YrW8tjX3NPcfWQKBDrYSq9CkW+Y6EOlclznWeqPn2voKYhLCVekiqJf +dgkoDKOfFSNdYBfvY5VRlBaEieoTBngpwMwfohhCJBAxihuJn1s7g2kymlp3FzgB +/IpkNXL41rjXhFd9N/r0j7Ul6KXVpSS+4Vx6/mwQuobYj6oHygvwGaKvB49jljqe +VCG7zm1N9HyhGZCUk/q12ryKkuiEG1B322Ch/ny3j8ExBCEA2jpkCGlaB9nWg3Az +cSh7WN5SGZ7JJZrAxBXEQM5H0xCoFVax8IpHra9rIjE1nisFQ/6VwNBpYRUBh+VJ +AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBQsGKMf8Qcs43VR9StkarXVa1yxLDAfBgNVHSMEGDAWgBTXAZX2sreufynj +oPuU8EYr27E2IDANBgkqhkiG9w0BAQsFAAOCAQEALty2RFqt4BofP13PZlxs9M1G +njE3ZZ5opUWHN/Ow/AzMKUvSdsHgQHvYEJpITS8BfFUxxMP+iRlO/GGry5xv0oDQ +CRPADPaW+t28olIsqMTnJ6/0Ftgb4UIVV+8qEHe8uWzq+6no3jKYmx8C1+VpHM9x +Sh6nKpE2i/dsHG5mc7Qgu38uaBLTrGjTaNaF1CXus8HqGrdVXGhSQD2bVBHq5Vy0 +j4pJY9sOSH8WA9SdjzZO4MBRDkOehSHjoaerxXs8BKWK3AycR6wsvwkodpJXheZO +TMVq4qZwKApNnmYPR8Kmk6smpBJLpdc4zc75Uh/rFSLYrfPUjk4PtirdM4m80g== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-int-leaf-cert.pem b/certs/intermediate/trust_anchor/alt-int-leaf-cert.pem new file mode 100644 index 00000000000..50370584eca --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-int-leaf-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgICEC4wDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBJbnQwHhcNMDAwMTAxMDAwMDAwWhcNNDkxMjMx +MjM1OTU5WjAqMSgwJgYDVQQDDB93b2xmU1NMIENoYWluIFRlc3QgQWx0IEludCBM +ZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlzgYcPXTl41ocGCq +vrl5rK4S6MmkQG8oSRihk3Yfy9axvXYY46Xi97XZjI7SngT+x9SMNFuqGvp+Kq9D ++boEBU6UlYOR21Y6aK3qRCzZT/N/hITB+0qXX7Rf59dNfxz99SKfbP7v3SHU8zuN +uMI25+4cLzleREHV1E2vGgpUXJIhdmdYdK+8azbLdJb3kp3bpPZJTaI8zyKYgeF6 +0w+i2RhPVgZ3k5hhUgaYqIHwDAWU3fgJ2lqA0X8SPY99C7odTqXahcpfZOkbq7Sp +mcPgKeczRcr8IGVle3z1TJgmB8vtJYO0Hot09dI0N/5eUJz7uKxTlt6bZpBDc3Nc +s1kIAQIDAQABo2AwXjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAdBgNV +HQ4EFgQUnOwtwEg/u+zfuBIvIpGm3jdZbRUwHwYDVR0jBBgwFoAULBijH/EHLON1 +UfUrZGq11WtcsSwwDQYJKoZIhvcNAQELBQADggEBABlQWOjnG1ghCNVoGlHLude7 +4OG6hdTsbyQIKcr8/Y0Qa6SnrNYGbZ779ptNVavJxUaGiptY/m4t4wHk2mUl2Jw3 +qvXEald1uBCl1z7Az3GhoKqLa7y84NNh9lS6BrqoFy5o92O7mZsGp4vqfawWzDwy +y4190nawLc9UXloaoXEwnlsAzmvmK7YJMGaKN59d25/JBHDIuHR29tcqxsrlCoH+ +flc099l5YOWJp9v2+GoJ/jvYzKVmJqLSck5tU4FJbnyvghdrgMV78y67qh5kuOOv ++CZxSJVssxasHaYsq5JDybdaeP5zlLKka82bRg77u3knKnKZkM7slFiqe7nm9hk= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-notca-int-cert.pem b/certs/intermediate/trust_anchor/alt-notca-int-cert.pem new file mode 100644 index 00000000000..dd55b58a66a --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-notca-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf6gAwIBAgICEC0wDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBFbmQwHhcNMDAwMTAxMDAwMDAwWhcNNDkxMjMx +MjM1OTU5WjAlMSMwIQYDVQQDDBp3b2xmU1NMIENoYWluIFRlc3QgQWx0IEludDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPLZLmN98lunvgtkdop0AxZ +GCYqpdXRitby2Nfc09x9ZAoEOthKr0KRb5joQ6VyXOdZ6o+fa+gpiEsJV6SKol92 +CSgMo58VI11gF+9jlVGUFoSJ6hMGeCnAzB+iGEIkEDGKG4mfWzuDaTKaWncXOAH8 +imQ1cvjWuNeEV303+vSPtSXopdWlJL7hXHr+bBC6htiPqgfKC/AZoq8Hj2OWOp5U +IbvObU30fKEZkJST+rXavIqS6IQbUHfbYKH+fLePwTEEIQDaOmQIaVoH2daDcDNx +KHtY3lIZnsklmsDEFcRAzkfTEKgVVrHwiketr2siMTWeKwVD/pXA0GlhFQGH5UkC +AwEAAaNQME4wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQULBijH/EHLON1UfUrZGq1 +1WtcsSwwHwYDVR0jBBgwFoAU8FoLkBXrI/x3OLzkJztUbgeKTb4wDQYJKoZIhvcN +AQELBQADggEBAJWdRLg+BBQC7lJwUNBuskWiNB6jtG4/TGwIauOFNbICaLgOZ9rG +s3Fw+P530sVJqFmJFBZAB9jLxh11Ae3nx3zaZnPdfCYlNelrob3LNb3SJq9nk6cD +eGx4g0RA+TWMDsOgVyI1SyzesJ2L5XfK/rxPKDWB5YmKeBMiSJuy7EluRVl3k5R8 +cwclRhk/AyTMp5d2TWlCsJCW+sgzkAW0WDw8MyjFYpKMvKFq8RY2ofvdKHjmB7sw +0L3a4pgw2Xi/h2o+A/5g0Jg2dtGQdsn9Wuz0gXd66ln9QABSxQy7CEzamzfFk/hq +i/nKTvNW7Ah1g9a/ZhhZd5/tIMEbV7Ybfwo= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-old-int-cert.pem b/certs/intermediate/trust_anchor/alt-old-int-cert.pem new file mode 100644 index 00000000000..676810cb6c8 --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-old-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgICECwwDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBFbmQwHhcNMTAwMTAxMDAwMDAwWhcNMTkxMjMx +MjM1OTU5WjAlMSMwIQYDVQQDDBp3b2xmU1NMIENoYWluIFRlc3QgQWx0IEludDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPLZLmN98lunvgtkdop0AxZ +GCYqpdXRitby2Nfc09x9ZAoEOthKr0KRb5joQ6VyXOdZ6o+fa+gpiEsJV6SKol92 +CSgMo58VI11gF+9jlVGUFoSJ6hMGeCnAzB+iGEIkEDGKG4mfWzuDaTKaWncXOAH8 +imQ1cvjWuNeEV303+vSPtSXopdWlJL7hXHr+bBC6htiPqgfKC/AZoq8Hj2OWOp5U +IbvObU30fKEZkJST+rXavIqS6IQbUHfbYKH+fLePwTEEIQDaOmQIaVoH2daDcDNx +KHtY3lIZnsklmsDEFcRAzkfTEKgVVrHwiketr2siMTWeKwVD/pXA0GlhFQGH5UkC +AwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFCwYox/xByzjdVH1K2RqtdVrXLEsMB8GA1UdIwQYMBaAFPBaC5AV6yP8dzi8 +5Cc7VG4Hik2+MA0GCSqGSIb3DQEBCwUAA4IBAQAivwDYufI9NPnh+tbbMxGVmW9n +W229A60501HiILp6rYTb3naTzLak0M2fr7BBSgO2vctIWdMlavy16jitgKrCQbsp +KjDLMSeWmYUAKEG5Z9qK74Sg+ONubUhLq8sUV8TRyHE+KufBmhym6mdWUFgdy7ze +UJBKUj6K2UpKkr2/6hYQ7aQ9XvhjDK/z6ZrJJLb7HaoMsT8D7YCc1ZQ7hXvnT5Gj +/IRs8U/13ERoe4vc+BuFWy+HgGjqvplsjUlHVjyGQ6hKWPhWBCja/dX35L7nrX8f +tBkoqCuVEhExIgyEB3QcgnTwlJGyGsybnJ7wRSNDgvf1Qc5Xt/N+mw0T++6K +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/alt-root-cert.pem b/certs/intermediate/trust_anchor/alt-root-cert.pem new file mode 100644 index 00000000000..c54c4abccb0 --- /dev/null +++ b/certs/intermediate/trust_anchor/alt-root-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgICECcwDQYJKoZIhvcNAQELBQAwJjEkMCIGA1UEAwwbd29s +ZlNTTCBDaGFpbiBUZXN0IEFsdCBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIz +MTIzNTk1OVowJjEkMCIGA1UEAwwbd29sZlNTTCBDaGFpbiBUZXN0IEFsdCBSb290 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm1+XGBogLAYryEqy6HN9 +zFSvSijycHR+dQjjFQg2xMabOaFTGJs5e2iV1VSDxSsn/6dgaexpnc0ozlPt94wc +Ldo3NVl5aSSZe9o2tMCeUeiJTaPcUW6yIjDe31WuIkEZrs/urRVo1c6nqw9v0ibC +hrOlYIKmuXxTaEbTjuEDigoG/B9g2WgM5ZjyQgA+h+JdfzWhnkMNc9aQcA/nw+kN +uH6sqcyWh02zZHMT2A0iMYs6n5pCNg3/NG6eMosVLcogfF1hqCY5wufaBH8AsAUX +2q4kbqlrryK1nj7s7PXNxbFV6xMI1hGFY1NtxHIP9nzOHH+cNBbbl1UpBxgpuv9c +dQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQU1wGV9rK3rn8p46D7lPBGK9uxNiAwHwYDVR0jBBgwFoAU1wGV9rK3rn8p +46D7lPBGK9uxNiAwDQYJKoZIhvcNAQELBQADggEBAFz6HHnOa8sYuZ4tGXuZmn+9 +tt5CuEXtUeQvq6Mmo4C/QELZBCiPvcgorgVQKqGewFxG181KwOS5sPE0D/tASChQ +EgK0C2Tu+rgZz02Pw42XxlewEcQhrSkI6bj8D00SHoz6W/1y47YzqQ2dWJuNafaR +8xLGXF1QqSBeAv3b1mpalwgWb+aEZUUx7JiiSocLV7vRxEGiYQLhbre/QKluNHcr +l22WhLN8N9ko9MaOYjKiZchXh2ZvhlN+4NVk1Qv2ptdYCuuMBLkCqErqM1P4LIPP +B3bSkrfrYKeAqI7En13HxeF/I1F99tszZP7rMP2nBN0LOKK8JXPKp4TtAmgoPMg= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/badkid-int-cert.pem b/certs/intermediate/trust_anchor/badkid-int-cert.pem new file mode 100644 index 00000000000..32b279a410e --- /dev/null +++ b/certs/intermediate/trust_anchor/badkid-int-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgICEBUwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhd29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEtleSBJZCBSb290MB4XDTEwMDEwMTAwMDAwMFoX +DTE5MTIzMTIzNTk1OVowLDEqMCgGA1UEAwwhd29sZlNTTCBDaGFpbiBUZXN0IEJh +ZCBLZXkgSWQgSW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuYrr +jf6T4u1hnB0WVdiwrNb8DNzgB6tXPIug+HIjCfkLIdS/MZ4c1vFYhJskUtjyntd7 +zFCNjevYpEDa0kNS4hQ8FvgnJCeC5N9lJsA6sM+9juIKLcKsrzDv0g/AVfRCKqIv +Qynkmo3xD5aoFqdTnE992yWJX74ddaJqtI6ZZs0lTx/nE75V+Kvx/7iYS9O2CqmF +2uHD/vKYrieBgNZJMyzyoEtv/nH2asLoajdYGWW7dywUuv5TZrfUgYKVUwmdtVjl +NkheF2oPJXVTf3z2OaSZnyWogvsQCeGm7M2USTx4ADZ84XSFc9RrCC6M/eQIXKb+ +jjJ3hQz5bmr8eFd2EwIDAQABo2QwYjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQUqgX9IQlebBX8oDLxDtw5f4iCZcIwIAYDVR0jBBkw +FoAUAQEBAQEBAQEBAQEBAQEBAQEBAQEBMA0GCSqGSIb3DQEBCwUAA4IBAQAS400N +MC2CTRYSYPYvCN0RrJ/3dH81ONZ8mN5oWV+QO7c2TSEAw+fKMHDVVLnaYMZMMTqz +RHR2nyEBgO+eaqr+CcfSdbEG9kM8WsTWcwqzrZmD5IYQoLN73CNPqtTuRhVX3/gv +NJOwx0Z6xAnLNpCI17OHWJRC/YESNFeW/KmErid018Dw1x5mBqBeDGWp9okfZNuf +WNxU0GxuZchHm6EtnsLTRgqAk7oNXregAkughNXp75magxxv6g77NCmPUBBU50pc +Uabg+xnlBXOE1ZzdNcO26Gb+DloQXlC0vB+1O3rF7UPc13wqcYSTIcWBYyiMOpAL +77GJ56lxmwVMUSvF +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem b/certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem new file mode 100644 index 00000000000..d5323b6e10b --- /dev/null +++ b/certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOTCCAiGgAwIBAgICEBYwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhd29s +ZlNTTCBDaGFpbiBUZXN0IEJhZCBLZXkgSWQgSW50MB4XDTAwMDEwMTAwMDAwMFoX +DTQ5MTIzMTIzNTk1OVowMTEvMC0GA1UEAwwmd29sZlNTTCBDaGFpbiBUZXN0IEJh +ZCBLZXkgSWQgSW50IExlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDA3idXOiKA8cPeDYbow0+DJrJ+T08L3Td+mM8yHqVKoH++RFenQI+j4sMXXy0y +Yn0mRSTozBNZwrWTpJFpOOqwJ12bMsARoZmJI1Rg3mgo4XWQfIz6MuMLAfy2uWfw +4dX6l1VlbrWtzmBo+++kY0Yu0Q//3aQTTDVX4PXjXNfkjJKtCU7GZTobJcub2A2g +eVhZ+O8BlNmOEylnvEYh5rQ2pkPhAnZQCX9DkX9wX3kj9nE1s5+8MsPdiTcJowly +wkw3OuF06ZuvpLVjL9XxmQ5Naju0tvlPdRIYqIFMdKW/6Vj4I/5S0p1t6nfEu5Vz +qG9E6B38hmVWgswza6vjZx4LAgMBAAGjYDBeMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgeAMB0GA1UdDgQWBBRqgHxv9d/A7yClcJsTIeaFLW5ezDAfBgNVHSME +GDAWgBSqBf0hCV5sFfygMvEO3Dl/iIJlwjANBgkqhkiG9w0BAQsFAAOCAQEAe5L6 +0ZLpwz+7wml/O73HCLaTSN6UkgXBbNJG4WFvGy75+gD4UH3bh+Uf5BN7QD7qmjDL +9tXnInm7V+hOuRLuAgJO8vBQXgzzgXJLdYOjoT4Q0ZgTBp6p21fqysvL5Vz0COTj +MxfHQLDUE59q+Ou7ZhQrp7OlavVkhLVjgdQscu7zZtvuArBlcxEDiyUq4obV4TcL +IGey1UK5w+vAmSvcnFCjqKMOQKFFqcI1gMaf5lNEL6nNfhKo0zHFyP6bdtkcycu7 +gNBpN6IhuPIEj4jn5/TlmhDC4VWwzW5+Rffqtl0rPy4uqERPQ/bR/xbR85krg2eP +pate3ClBuo4pSAbRbg== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/cross-root-cert.pem b/certs/intermediate/trust_anchor/cross-root-cert.pem new file mode 100644 index 00000000000..35f90e664ab --- /dev/null +++ b/certs/intermediate/trust_anchor/cross-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgICEAowDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMCgxJjAkBgNVBAMMHXdvbGZTU0wgQ2hhaW4gVGVzdCBEYXRl +ZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkyIXJjCUUogh +c8/PFpl/DcSCAHV25ZJcc6t2MP4sXfETh438ARFhHuntg+OZY1laZr7ztICqYz46 +fmR+l1WtH7UWkXDwApJr0Z2HsinXNg3qrPEdhS8UZfgiSEXDQRmE4WEdnCMSyQh2 +lBsKkwZtSizrv6D9Esx834e6n8auliTH9flg80c7DKOsubAj/pTp+viQuq4oMELn +6ooNtEomLBIUJn2C9YyajpOrKcDsdTWluiZrp9vBcmI8CR+C7ih9qtGhgzTSDBOC +etqJsVr8jHYz7A51vbk5d2fjazF+beyW6rkQZzNCR1n7d2toegvJ+3cQCzxanQFd +Cg1fiMOR9wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUq3L7oqCxjNUZzyU91WHWyywepD8wHwYDVR0jBBgwFoAUrbh4 +8JovJ9q46nBja5XO+doYc/YwDQYJKoZIhvcNAQELBQADggEBAA2QE72+Kj1hBtqU +0VCuEKGcx5ZPPmigjqUUr+uiGss7rGIwIIFNWnlHHgISMR4WcAFeogFrT8lB1c7e +kHgkZ46cHCu3t9nXEKJuA8h5kLDtDjTvZCmv9p9Sl4hnXYCCwe3Rvl4XP+dqWIU/ +QxeurRH61AF/I34Wlqa6PMcFe/vD9jr7dwjFe+gRsESQ2Bc+7XzHH2Wf9LRomXRo +z6UiyrWNGkJooT2fEtnln2ZXJZV90TU7cZS5Wn7xYcZ478aPh61XE63/nOFASVk8 +7LwAAYjY+i3zV7MkcmzK/yk0cVz1U6wUUSbKoC1NG7tb6/bUoRYuAv1FYdufRLCZ +hO8uYNM= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/cross-root-late-cert.pem b/certs/intermediate/trust_anchor/cross-root-late-cert.pem new file mode 100644 index 00000000000..f251d0aa36f --- /dev/null +++ b/certs/intermediate/trust_anchor/cross-root-late-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgICEBAwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0yMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMCgxJjAkBgNVBAMMHXdvbGZTU0wgQ2hhaW4gVGVzdCBEYXRl +ZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkyIXJjCUUogh +c8/PFpl/DcSCAHV25ZJcc6t2MP4sXfETh438ARFhHuntg+OZY1laZr7ztICqYz46 +fmR+l1WtH7UWkXDwApJr0Z2HsinXNg3qrPEdhS8UZfgiSEXDQRmE4WEdnCMSyQh2 +lBsKkwZtSizrv6D9Esx834e6n8auliTH9flg80c7DKOsubAj/pTp+viQuq4oMELn +6ooNtEomLBIUJn2C9YyajpOrKcDsdTWluiZrp9vBcmI8CR+C7ih9qtGhgzTSDBOC +etqJsVr8jHYz7A51vbk5d2fjazF+beyW6rkQZzNCR1n7d2toegvJ+3cQCzxanQFd +Cg1fiMOR9wIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUq3L7oqCxjNUZzyU91WHWyywepD8wHwYDVR0jBBgwFoAUrbh4 +8JovJ9q46nBja5XO+doYc/YwDQYJKoZIhvcNAQELBQADggEBAJxU9slBGgAZUud5 +wTs0ehd8IM4hN8S+vI5fQHRP7aWM3QXQlhK5PWCsYi12SbscMTRPvp2JfxV4OmC0 +vn+f7FIS90aMXO6S3P4RFg8xStK9mpuKEzElf8q3MylUil6RqlaiI+YLAg0OiGAJ +2DzNifRHzpOPE/I13ZFYylRwN0917h3SXtd89lVjMa8mumU9SeLcICtz5hSnWBzS +rEfZ2UYsAU9TCftKhS2wIQOAbDwQV+TdEWe+WWXTeFPsxbOb/3qE0mN1duNX4OKu +QvnXNY8ioSzAc9GTAzJjUZTUVj+3tfKAj9uiEpjtgo4UhVOn497Yqdg6TFhnJDmx +M3R0KrE= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/dated-int-cert.pem b/certs/intermediate/trust_anchor/dated-int-cert.pem new file mode 100644 index 00000000000..9f18773db6e --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhCgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IERhdGVkIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAhMR8wHQYDVQQDDBZ3b2xmU1NMIENoYWluIFRlc3QgSW50MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra+hiNAhh+tgSmzi28PUj7lN +lDCepHySkrkxGgX57yCgl/KgqA4tyEA9hJLf+KyEnjIyRSF/Nnd6UbvBs1dNVy1a +gIItjegWjuGnKbI1ABw760zTPm3+Kx1FVhcKXf9mFkzX7cqMS+0p0AgrJSv1Zz0J +sgRbri7DjFMOPBUWUfOgLkKULFhQAKY5qBofI6HrFKwe9T7OWDCX241AYYlqbT7K +XSegW15DumEtqaXvSd88uvix0IyUsrQRWllx1eaCmn1Mj2qs1RMbis9vT5fPYcoO +TMDRL5agTLEqFJ549mGHw+4mkf8XZ5oKM3Dwk6niAaNLMYJQkTzsl/cRS5cfpQID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUzVh0dmMhuFJUE1ifqEQ7BrarTs0wHwYDVR0jBBgwFoAUq3L7oqCxjNUZzyU9 +1WHWyywepD8wDQYJKoZIhvcNAQELBQADggEBAHMsWLzf77EBf7/gqFYh2fqc9pmg +uuuyIarzcn1v+rXlDtBDNEnEcnIX7bbu0S38AzMeODlbpM0rQL8dYUdXZ4Ypqzup +2+jb+1VFaWDnp99AEGLQ4lIS01roP4JocMXPx3HydoMzKs9GnyxTlcyftSW+G/CL +DLO8drM5X3uQ0eG/YsUe0pa7GWFo9ZEJGVkdMSlFeSkEOPW7cihsFD+4kIaOJ0vd +HnNZkvnhHXp4lRfNL/a9xHhuOB8yX/Z4ov4kwlArneCVphn0bbMjJEqBmQrEawFE +jFLPT7010TRg/3qRf2i0LTRvohAq5b36y8RNBA7x2z3T0ssP8jrcVKB/ibE= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/dated-int-leaf-cert.pem b/certs/intermediate/trust_anchor/dated-int-leaf-cert.pem new file mode 100644 index 00000000000..29e7cb7e880 --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-int-leaf-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIzCCAgugAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwITEfMB0GA1UEAwwWd29s +ZlNTTCBDaGFpbiBUZXN0IEludDAeFw0wMDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5 +NTlaMCYxJDAiBgNVBAMMG3dvbGZTU0wgQ2hhaW4gVGVzdCBJbnQgTGVhZjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMDeJ1c6IoDxw94NhujDT4Mmsn5P +TwvdN36YzzIepUqgf75EV6dAj6PiwxdfLTJifSZFJOjME1nCtZOkkWk46rAnXZsy +wBGhmYkjVGDeaCjhdZB8jPoy4wsB/La5Z/Dh1fqXVWVuta3OYGj776RjRi7RD//d +pBNMNVfg9eNc1+SMkq0JTsZlOhsly5vYDaB5WFn47wGU2Y4TKWe8RiHmtDamQ+EC +dlAJf0ORf3BfeSP2cTWzn7wyw92JNwmjCXLCTDc64XTpm6+ktWMv1fGZDk1qO7S2 ++U91EhiogUx0pb/pWPgj/lLSnW3qd8S7lXOob0ToHfyGZVaCzDNrq+NnHgsCAwEA +AaNgMF4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFGqA +fG/138DvIKVwmxMh5oUtbl7MMB8GA1UdIwQYMBaAFM1YdHZjIbhSVBNYn6hEOwa2 +q07NMA0GCSqGSIb3DQEBCwUAA4IBAQBtvAMkUZ8lUc4Y1GzxlQSJTYYNmb9T+WtI +JpYro++9xu96zq/h1mxuZhfS1+L1cVpxnfSPhsmVpAyeIrXLiDhUrq1zCqFkWcLa +cighqsRc7vsurkYWSi6qVVJQ0whULg3I9h82hFrqDZZMGH6TkkoD3+bU/h53kdxT +cmS6pOhLLXbutcj9dRJv0O4b9JlcmmV58Ba9vG5Fr5tA2iDfvZhLLvZ0i/Me6RDN +XfmCmdyJG28Ws+0DGaXeb6oa2ODVc4EOyYptxgL5rLPxjjhqAJwCeIdK2T7dtWVz +tsg3Vyqt0XhiY+YH/ItGQjvWERN7fXdr083XogCsXCtZUEoQTJ44 +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/dated-root-cert.pem b/certs/intermediate/trust_anchor/dated-root-cert.pem new file mode 100644 index 00000000000..13ac16267f1 --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IERhdGVkIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAoMSYwJAYDVQQDDB13b2xmU1NMIENoYWluIFRlc3QgRGF0ZWQg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJMiFyYwlFKIIXPP +zxaZfw3EggB1duWSXHOrdjD+LF3xE4eN/AERYR7p7YPjmWNZWma+87SAqmM+On5k +fpdVrR+1FpFw8AKSa9Gdh7Ip1zYN6qzxHYUvFGX4IkhFw0EZhOFhHZwjEskIdpQb +CpMGbUos67+g/RLMfN+Hup/GrpYkx/X5YPNHOwyjrLmwI/6U6fr4kLquKDBC5+qK +DbRKJiwSFCZ9gvWMmo6TqynA7HU1pboma6fbwXJiPAkfgu4ofarRoYM00gwTgnra +ibFa/Ix2M+wOdb25OXdn42sxfm3sluq5EGczQkdZ+3draHoLyft3EAs8Wp0BXQoN +X4jDkfcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFKty+6KgsYzVGc8lPdVh1sssHqQ/MB8GA1UdIwQYMBaAFKty+6Kg +sYzVGc8lPdVh1sssHqQ/MA0GCSqGSIb3DQEBCwUAA4IBAQBctW6m089Eod0SpoGV +tCSum5vuYoEg9diziq6J23CgQpGMdVFkHdyRCK8bKAseBLfkpgVr9K5YKHKHWOEP +nPrSoA4PHPgP9lQ19rwJlnJkpEp/EF1zWpjHxhCNG6mAT7ausOTUFfzYgSrO3CQp +DSqz3SszSOVq8uzH8hWpiegpXvBqCIMibJYkpGM4MSJw+uozFcX4sVTaNmkrXu9k +mk6sgioIIU0fZjI0q8/BDSVfwJRKIcwiFN5ERYtKClZZ9+V58Js4ydFoFUh3SIOZ +q+XJvaCTvdP302a23F3nX/MCgtnZ9XDhOLKsmjy5wJEs2ANmgsEvLOcosreMcFHw +PThu +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/dated-root-crl.pem b/certs/intermediate/trust_anchor/dated-root-crl.pem new file mode 100644 index 00000000000..d9855f2a319 --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-root-crl.pem @@ -0,0 +1,12 @@ +-----BEGIN X509 CRL----- +MIIBujCBowIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB13b2xmU1NMIENo +YWluIFRlc3QgRGF0ZWQgUm9vdBcNMDAwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5 +WjAVMBMCAhADFw0xMDAxMDEwMDAwMDBaoDAwLjAfBgNVHSMEGDAWgBSrcvuioLGM +1RnPJT3VYdbLLB6kPzALBgNVHRQEBAICEAIwDQYJKoZIhvcNAQELBQADggEBACYg +Zsy1i2lCi0l/oLXqFsK2o7ohSvzNIPHv0abE9g5+doUMSU+RaDAYSRWOgtTU7heb +br7UcERFoPAKygBNIw9mu8CWilcOB176CYlWrug9/+S3bS7JI2K2AUzMu3pvEEzy +dgBKxeoBGGqKb/IuHnQzsKQ5wpW4NwHqmhSxPmENiTii+j/x9l4m54L8ZYj3lL/d +WwQpkJwQYFnRqsRRoq6Zr0/0gGMcfinw6gO2WQZ51erQMCA6H8aJ7Ff7Wo67HprI +DcUCPUJDM4yuY02mAp4JKssTUjOPpu41KOuzkJWh9zMFf23Ill972a8WAi3Uqfh2 +CNhR2QrKzKdpsZSmgZY= +-----END X509 CRL----- diff --git a/certs/intermediate/trust_anchor/dated-root-empty-crl.pem b/certs/intermediate/trust_anchor/dated-root-empty-crl.pem new file mode 100644 index 00000000000..9e84f26f327 --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-root-empty-crl.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBozCBjAIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDDB13b2xmU1NMIENo +YWluIFRlc3QgRGF0ZWQgUm9vdBcNMDAwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5 +WqAwMC4wHwYDVR0jBBgwFoAUq3L7oqCxjNUZzyU91WHWyywepD8wCwYDVR0UBAQC +AhABMA0GCSqGSIb3DQEBCwUAA4IBAQAWNz15tEIHbXzRJxY2CTL0G4jC1HSu+5Xr +zkFFcbCoGdCKhU50bzL0w7ADu7pHUmZ9kq8+/vZc+v20zjCVPBsK7kUhPzkY4UGb +T14/tx6Kj4BBC6npCO4jW/IHfqMKpfpPwQwNij0jGtPKhGqcUhhHTfpg/92Ln18Y +ca0IgnkUzbAvRcyHJ0h2kPLnDjRYNag6t2FnWDA4PkzlC//NT7Lum12p6Ar+dJDe +6O+aXWIzDlPcClkf/Yct+39PHc4WZ250pnnYQkduSQKJ/lhtMut7uHFrCwRBMo13 +IPz7X0IWbUw6CMWm8AohkCRtiBiYz4mxAya9n0hnEYhhjjwv/Hlb +-----END X509 CRL----- diff --git a/certs/intermediate/trust_anchor/dated-root-late-cert.pem b/certs/intermediate/trust_anchor/dated-root-late-cert.pem new file mode 100644 index 00000000000..4b1564eab2e --- /dev/null +++ b/certs/intermediate/trust_anchor/dated-root-late-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEA8wDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IERhdGVkIFJvb3QwHhcNMjAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAoMSYwJAYDVQQDDB13b2xmU1NMIENoYWluIFRlc3QgRGF0ZWQg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJMiFyYwlFKIIXPP +zxaZfw3EggB1duWSXHOrdjD+LF3xE4eN/AERYR7p7YPjmWNZWma+87SAqmM+On5k +fpdVrR+1FpFw8AKSa9Gdh7Ip1zYN6qzxHYUvFGX4IkhFw0EZhOFhHZwjEskIdpQb +CpMGbUos67+g/RLMfN+Hup/GrpYkx/X5YPNHOwyjrLmwI/6U6fr4kLquKDBC5+qK +DbRKJiwSFCZ9gvWMmo6TqynA7HU1pboma6fbwXJiPAkfgu4ofarRoYM00gwTgnra +ibFa/Ix2M+wOdb25OXdn42sxfm3sluq5EGczQkdZ+3draHoLyft3EAs8Wp0BXQoN +X4jDkfcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFKty+6KgsYzVGc8lPdVh1sssHqQ/MB8GA1UdIwQYMBaAFKty+6Kg +sYzVGc8lPdVh1sssHqQ/MA0GCSqGSIb3DQEBCwUAA4IBAQBVJ6hNnV0Pk5eInSOu +9jh6kbS6mnxWMQ387Fm5PZMfYmxfwkNqOY18oabhU9WjIeGPBeKeaHfigJaBkmt4 +iq6FcTShoN0HlPK4BaYCJ7zSQVUT1O8+5WPXtp7iSEM2BrP1hifbR8MEXdPx/KbB +mNgyTRT6SbY+WTS7tCfM18GVQxrCopVZ+l7ugGBsU0vPjt8OCg5fb2GySSwlEt+L +yyreJjmhC2ChKs1ARdjBgXbCFjk446y6VNFKUqZRjak0upxjh0Ln40NM4Ex0hLFZ +0Z4NLtAxU9xMd+Oqo+JB20+cCpRYqjcb6cfjEWBVJserQabHfqc7ze76vaorLO7E +Wxbk +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/ec-name-root-cert.pem b/certs/intermediate/trust_anchor/ec-name-root-cert.pem new file mode 100644 index 00000000000..43c13c33c4f --- /dev/null +++ b/certs/intermediate/trust_anchor/ec-name-root-cert.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBZzCCAQygAwIBAgIBBjAKBggqhkjOPQQDAjApMScwJQYDVQQDDB53b2xmU1NM +IFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDI0MDEyMDExWhgPMjA1NjEw +MjMwMTIwMTFaMCkxJzAlBgNVBAMMHndvbGZTU0wgVHJ1c3QtQW5jaG9yIFRlc3Qg +Um9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD1vcfxyPXSrpf+/9zrgyx33 +DPiYj/ItQjqnnoslJpa+uAWIlNEoc+EUhli3DpPLpsdomEnb252m9qeZGwrKsfOj +IzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMC +A0kAMEYCIQDAxCmKyFmUSEAx2tjI6Fw31JPc9IgbR0oQxkA4v9gcEwIhAPU6mTJo +wNj0cRmJF0K1WBtN73AkG4dooJIMeO2kknz3 +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-int-cert.pem b/certs/intermediate/trust_anchor/expired-int-cert.pem new file mode 100644 index 00000000000..ffcb84e19de --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhCgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IERhdGVkIFJvb3QwHhcNMTAwMTAxMDAwMDAwWhcNMTkx +MjMxMjM1OTU5WjAhMR8wHQYDVQQDDBZ3b2xmU1NMIENoYWluIFRlc3QgSW50MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra+hiNAhh+tgSmzi28PUj7lN +lDCepHySkrkxGgX57yCgl/KgqA4tyEA9hJLf+KyEnjIyRSF/Nnd6UbvBs1dNVy1a +gIItjegWjuGnKbI1ABw760zTPm3+Kx1FVhcKXf9mFkzX7cqMS+0p0AgrJSv1Zz0J +sgRbri7DjFMOPBUWUfOgLkKULFhQAKY5qBofI6HrFKwe9T7OWDCX241AYYlqbT7K +XSegW15DumEtqaXvSd88uvix0IyUsrQRWllx1eaCmn1Mj2qs1RMbis9vT5fPYcoO +TMDRL5agTLEqFJ549mGHw+4mkf8XZ5oKM3Dwk6niAaNLMYJQkTzsl/cRS5cfpQID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E +FgQUzVh0dmMhuFJUE1ifqEQ7BrarTs0wHwYDVR0jBBgwFoAUq3L7oqCxjNUZzyU9 +1WHWyywepD8wDQYJKoZIhvcNAQELBQADggEBAE0LyOIaHUH7bl+yzSXXQy+WhteK +QsHvdzwJVliWoSPtW5Hqubko2+CCUbpwoQnWv3lZojjuvSrDKSY/UEe2unC27lND +/9YyydaB6ha+NvGPtHi/uBRPLUJAZ8IvHo75cSA1AhcJuT7vXWpj6QD8h7nd6TyV +Fz1VXa7LSjl8rrgUSoKl8VMdDlAuzDAoHJxJenS5TJi+HEUI7R3+vEjz6C+1Uzof +PS0LHJkjF4UiKyVyOh/GE4mED9oqsGS/58oZLYv+e9WRja0pLGp2L+z3cqJHS9MV +n96y+ROEVeFpB9U5eL+kDU201Yx+NzdmiBIF1MwgZnj786X9iVYETSKatzo= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-cert.pem b/certs/intermediate/trust_anchor/expired-root-cert.pem new file mode 100644 index 00000000000..9fd862919d0 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0xMDAxMDEwMDAwMDBaFw0x +OTEyMzEyMzU5NTlaMCoxKDAmBgNVBAMMH3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCi7QJBMSku +R2ymR0jNsScmhj0IQtZUf/7I8HB61qAtYgmB9OEzMFIiqM4Gb8bB3JtacKZZWtwd +ha+GhMUIChn3dEenPeXapw9Skft8dPVXaIDsrumhIMHlpTH787B57xx5mcwarJER +UMel+BmOLqZCmh5kqiqMe96o05BV+X4pp3iNhaJA0Fcy2PaBkyeEZQtyHGAPnnUL +8Dr58WozbZaZnAlSWv7r3XaddXavK9TkDqqO6XXshYgn+BfE/3aMFyURNg1iqzYH +uXS5Ppt8+aP9WbwKwJG4jCitw/MKZQr9KOt7u7szypJdg78/EYBqJQY6Ly+QjHBp +FwrlLv97FJ/VAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBStuHjwmi8n2rjqcGNrlc752hhz9jAfBgNVHSMEGDAWgBSt +uHjwmi8n2rjqcGNrlc752hhz9jANBgkqhkiG9w0BAQsFAAOCAQEADQ3LXk9tFPx1 +OitF18NAJ2cTJ4ffWPrTPtTeu8iJGYOvpJg4xsIgaEINMh8sFJj0sQ4Cxq9ZmO8t +Veqk+OsgSeMU8M0MJjAWs2glWbSD23+Ltq2+h4VD2VobDlRUm7f7tSlwq3nLz6MC +JlFrHnRFjSU6Lk2YzlmRHsNggGuy8l/WtA9FXaRdL1PZQyqglYlS60OPUIru4ott +T8kouufaU2blsgUCI3Xl8vLJe6WBCMkB84PfsmRUIERcRR1pZvo6Snqs84sfPmng +MecM4p+a/JYYdyc+FMMXSL0E94hSmlFoQJGBxP/9xuxxQDjlkTb3afAhlNdQ8F8i +Vgm2c1jAJA== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-int-cert.pem b/certs/intermediate/trust_anchor/expired-root-int-cert.pem new file mode 100644 index 00000000000..ec363782366 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-int-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNzCCAh+gAwIBAgICEA0wDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMC4xLDAqBgNVBAMMI3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QgSW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA09rV +b+2yGPF2p6A88z57ZDXxyvuYiDWW70lwzK5o7t3ANnvm/ugTxw0KNFwYcbOt8WHZ +x4FPtUxakRuc6QxZszhhD3Mth1VovBiDGulDQP6btRgZ78quCFshLoYe56ibOnPb +FzbINMglP4p7CntTbMcVCfOncklSOpYVlGL8t302E452/xmYoI90WSrp+52YI2vs +9qIh1IJfMS1hKuPgv6WN2W+jRqxK83PWkO6CadDpD8Lazk2sjxC2oc+fd+8vzugQ +6fcdOV1akA52e9/pTDtczWpQMYrJKF0/yygqQ3pSFy+KIDSLq8Iu+Js7NMNwLizT +2TLM0TohdJYTnWT7bwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU/5HnTSzCFOOpgti90Q0J7HmvH2YwHwYDVR0jBBgw +FoAUrbh48JovJ9q46nBja5XO+doYc/YwDQYJKoZIhvcNAQELBQADggEBAEZ2oh9Z +dUTxAGShbXzh1EwMu790f/4Q2gb/TI0nycD3BvqRZgBYGsGEOhDj2aiMp/OiJZK7 +7TSj+atzS2Rc+7+FiRAfSIRI7d+Ysyxx6YfiKpotqsOQk0Ss5Vh1stMCLfcDl9wp +XCyyX4jI2mT41FIH9WrmrEEgSwnP3nI9+bOHNUc92eaposWa3CrplgLgF0meS/QR +Vw0Hak/IoHwXSrktMwvG024cabyIhC9bUq2ai8MvBP+wbYXSYfz7Y2/44O28b370 +UIo8eILooHiJ12Q6wVcBhbKlBXN9Js2W33f10mDMVGan73v71i9q2HWHP9xg2lxR +O+gsOO2DhUf+g0o= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem b/certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem new file mode 100644 index 00000000000..bfd3f0a3ff8 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPTCCAiWgAwIBAgICEA4wDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAwwjd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdCBJbnQwHhcNMDAwMTAxMDAwMDAw +WhcNNDkxMjMxMjM1OTU5WjAzMTEwLwYDVQQDDCh3b2xmU1NMIENoYWluIFRlc3Qg +RXhwaXJlZCBSb290IEludCBMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAwN4nVzoigPHD3g2G6MNPgyayfk9PC903fpjPMh6lSqB/vkRXp0CPo+LD +F18tMmJ9JkUk6MwTWcK1k6SRaTjqsCddmzLAEaGZiSNUYN5oKOF1kHyM+jLjCwH8 +trln8OHV+pdVZW61rc5gaPvvpGNGLtEP/92kE0w1V+D141zX5IySrQlOxmU6GyXL +m9gNoHlYWfjvAZTZjhMpZ7xGIea0NqZD4QJ2UAl/Q5F/cF95I/ZxNbOfvDLD3Yk3 +CaMJcsJMNzrhdOmbr6S1Yy/V8ZkOTWo7tLb5T3USGKiBTHSlv+lY+CP+UtKdbep3 +xLuVc6hvROgd/IZlVoLMM2ur42ceCwIDAQABo2AwXjAMBgNVHRMBAf8EAjAAMA4G +A1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUaoB8b/XfwO8gpXCbEyHmhS1uXswwHwYD +VR0jBBgwFoAU/5HnTSzCFOOpgti90Q0J7HmvH2YwDQYJKoZIhvcNAQELBQADggEB +AGhBw/o9c9rTP1yO4RywLQ50rD1pRTRGak86JKhJenqRlhvrES8R61NMIeOmly/u +2zTqAFKx1AVnsMlQpgxhloewf+D3VRqolV+JPNfYLmxdpQkulVBCEM2D4dtwXGhQ +NQTH9uNtscJO6UeHO7w6Y19PsGUEVioL+DW4iVckSGjmEcaQuzF51KowG28GMTzs +3CJBz+2afUe41tqI4WKJHnW8V32yrBEcFCCJicxJlWrhtMllk+/PJylT/4fONvXj +Eqwx4x8fHzngcG4Az/p+Wvbe9StBPGK7CzZvtCb5SENJvlcjLObSmsRDuMNkl/iw +SLiSAZoQjFcilVAws5nVjh4= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-leaf-cert.pem b/certs/intermediate/trust_anchor/expired-root-leaf-cert.pem new file mode 100644 index 00000000000..28ebd6a254f --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMC8xLTArBgNVBAMMJHdvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QgTGVhZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMDe +J1c6IoDxw94NhujDT4Mmsn5PTwvdN36YzzIepUqgf75EV6dAj6PiwxdfLTJifSZF +JOjME1nCtZOkkWk46rAnXZsywBGhmYkjVGDeaCjhdZB8jPoy4wsB/La5Z/Dh1fqX +VWVuta3OYGj776RjRi7RD//dpBNMNVfg9eNc1+SMkq0JTsZlOhsly5vYDaB5WFn4 +7wGU2Y4TKWe8RiHmtDamQ+ECdlAJf0ORf3BfeSP2cTWzn7wyw92JNwmjCXLCTDc6 +4XTpm6+ktWMv1fGZDk1qO7S2+U91EhiogUx0pb/pWPgj/lLSnW3qd8S7lXOob0To +HfyGZVaCzDNrq+NnHgsCAwEAAaNgMF4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8E +BAMCB4AwHQYDVR0OBBYEFGqAfG/138DvIKVwmxMh5oUtbl7MMB8GA1UdIwQYMBaA +FK24ePCaLyfauOpwY2uVzvnaGHP2MA0GCSqGSIb3DQEBCwUAA4IBAQAce5dStM8f +P+xSlqfr/J5GOiCxk7apO95EUaM7JJoTEIwyY1BdeE/rv9HfqwN1Y2EX+WqAUKjA +yXeVj657P5SOws8rqdteHqi3fHlqYWDUwotoZ5tXKEaiNnik6HDkY/bDeWuOz1ez +hAaWypA1mSkAjo5CsUpdiEbkAN6vaqBWxqhgz8C9c/s+JiX+Po3bpd2AS2ibkauU +pChjY6zQu9K45U0M0mvb9f1qOmEwJfvdL1/NJbzUM524Ha3EX+jRb++BCtx18oxx +0SNCCDSh3hTNxahMChK8W4XT/X+tkyhrR58HYDsM5/ZOOSXhWf7n/lqZNwptvcbD +KqbAPe9ctpiE +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-link-cert.pem b/certs/intermediate/trust_anchor/expired-root-link-cert.pem new file mode 100644 index 00000000000..d3d9abd48b5 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-link-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEBEwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMCoxKDAmBgNVBAMMH3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHm9mgnPTx +JhvX7NfamvCMJL3Z7e7y6IqvmfM3j2nXmut0My4cXuabc8nE5e4uPMQQfgVLNpqR +/pcPx4OX3u83GjSzfN6EWfGJq+z5HV13PrjrdleqamUSjLwZArGnwkJFprM2Ya0i +bd9qBCtlXg4Pn0ZlXTHNtFC+zEQbn7Z5HptGgsqv3sMb+ZYbsImkNGA1WyrHLBmK +Wz2fpqziek5uxMarUAt+orw/PcD9GjPkT+KazmyUQpFMGTHRPA68pD+22+PiuE7Q +dzM5GD1Qv4Y5veNP3YAuAj+YDSSD26i1DU2Bfbh9vlSMr5Gtbi7n3zDlOfhegoA3 +qxTmi3tOFUClAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBS46CSJ5Mfh5ggjcvqhdQ8WJQat1jAfBgNVHSMEGDAWgBSt +uHjwmi8n2rjqcGNrlc752hhz9jANBgkqhkiG9w0BAQsFAAOCAQEAjlC9dduzr6k3 +0obDzlLXU5BqIK0IdgVSvCzs7vYIt4lfI0LliUtdS4ScnNHodwwgocEHJCqoeQJQ +JqIycVhdKdvb1tFTY4DuW5LrKoNoNyts53wSQ2rhTZmSH2dHMbzcsPxCphMnN8UV +NStyVAgkjJPeGutltnvpUJTMoXSolm6bdyzBoNN2CLnB2PTlKVMQHZgYKniH8Z6D +Fyuo9NTHmLhG2V4G2NVmBesHKJBy5LUGs0oHSvMDVQbE1mBXc+H2jQMr5mzZRQCs +dT0jf3Z907wP5dAkYhmyGmFffbxsG2uJpQ6NWeWZlw41ETh2Ma3DUYdtukd2+wUV +/P9yqKX9Tg== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem b/certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem new file mode 100644 index 00000000000..f689b0acacb --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOjCCAiKgAwIBAgICEBMwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMDQxMjAwBgNVBAMMKXdvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QgTGluayBMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAwN4nVzoigPHD3g2G6MNPgyayfk9PC903fpjPMh6lSqB/vkRXp0CPo+LDF18t +MmJ9JkUk6MwTWcK1k6SRaTjqsCddmzLAEaGZiSNUYN5oKOF1kHyM+jLjCwH8trln +8OHV+pdVZW61rc5gaPvvpGNGLtEP/92kE0w1V+D141zX5IySrQlOxmU6GyXLm9gN +oHlYWfjvAZTZjhMpZ7xGIea0NqZD4QJ2UAl/Q5F/cF95I/ZxNbOfvDLD3Yk3CaMJ +csJMNzrhdOmbr6S1Yy/V8ZkOTWo7tLb5T3USGKiBTHSlv+lY+CP+UtKdbep3xLuV +c6hvROgd/IZlVoLMM2ur42ceCwIDAQABo2AwXjAMBgNVHRMBAf8EAjAAMA4GA1Ud +DwEB/wQEAwIHgDAdBgNVHQ4EFgQUaoB8b/XfwO8gpXCbEyHmhS1uXswwHwYDVR0j +BBgwFoAUuOgkieTH4eYII3L6oXUPFiUGrdYwDQYJKoZIhvcNAQELBQADggEBAGae +5WmrNr1mXPfVUgI3XNjEl+cPVlqOYBdjlosXrFZ6YTCMyHgYA9juN+n5Qy3Wu+ER +t98acPxypwPeEYpx7zVjalq5GWNDyEYPJp1VruRmUgjnhhEopk/XvpXYzul++zrI +ts4O9si6vw1QBHtNjbWBusEN3vpDy9ucooh3oDXIDkLeHRgK4vUCHRGwrF3MbGKh +32naCZ0aQA24uScaQw+k5mOQwty2YATPrVGax5Kb44tSdCNNgvwrG+D2ZeJsgW6f +akae9a9frnNqd1gv0GxW9SX5kvXjO8EuzI4orc3NcPMshAnZ02e+Pma1ds2Amhig +9XpCM1nP4gJTz18ag90= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-nokid-cert.pem b/certs/intermediate/trust_anchor/expired-root-nokid-cert.pem new file mode 100644 index 00000000000..7eee6dbf444 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-nokid-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC8zCCAdugAwIBAgICEBYwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMCoxKDAmBgNVBAMMH3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvn5QzbWOw +IlMlOzty/66/v+PPKULVRbWC+r/r461CfKC0CgnRm/W9tYMUs8sIQmaLRiwk2E2r +JAjCO9RAfgom8uvWZ+x+tNbt3iou1sW6n0l2aOQFgB7v4gdLORIb71o+kMjjG8Gm +CZVrEbII9uqMxwqjZS/VDuG8WnFFWM+P9aBdhlIKzhsCzZHhTDHV4fJwcWgUJfj8 +7ls2wsBTFX9jpKgLu5ydtvfun1nnuyW79qw5e9B7TMSTnguYF7V2JIIcYGIWwu7P +S7hKuxAUW33LTr5rXT33xS1B7FTC41CR9mFmrL8oKSgviR/gdZElsvWEfslJNUrJ +tTUNjqKF26X/AgMBAAGjIzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBASfehZfhyyzykmiUkABwyBArelDKvlHXT +gc1c4xdXvdCTlqjVoyAdCYtWO64TqIqRPZEpJyc55CAS4ZyhM1jWp7CrwprtWmtO +HqF+WVKAZk9si5kMBJ448W6dBlnA07TN+EZRkRtrPAR7qOpop4GSkybFbQs2p1Hu +dDE9NSpqBgdmcALh5w2PTWyEiU5mwwb2QjpDScOQVOtu9S3iUuzD8isnF1keK9xF +mEru1lWAt6X/h5Y3mSZQO0TJPpKw3vkDIldef4kFKOus+/TXsbTIWEEvIRp2lPqT +rS0OUsTiL007PHsnACuGfWMfRZ/RoInr8MfC6r1jYRdkPzyIXuuz +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/expired-root-old-link-cert.pem b/certs/intermediate/trust_anchor/expired-root-old-link-cert.pem new file mode 100644 index 00000000000..4b1268091c9 --- /dev/null +++ b/certs/intermediate/trust_anchor/expired-root-old-link-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEBIwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0xMDAxMDEwMDAwMDBaFw0x +OTEyMzEyMzU5NTlaMCoxKDAmBgNVBAMMH3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCHm9mgnPTx +JhvX7NfamvCMJL3Z7e7y6IqvmfM3j2nXmut0My4cXuabc8nE5e4uPMQQfgVLNpqR +/pcPx4OX3u83GjSzfN6EWfGJq+z5HV13PrjrdleqamUSjLwZArGnwkJFprM2Ya0i +bd9qBCtlXg4Pn0ZlXTHNtFC+zEQbn7Z5HptGgsqv3sMb+ZYbsImkNGA1WyrHLBmK +Wz2fpqziek5uxMarUAt+orw/PcD9GjPkT+KazmyUQpFMGTHRPA68pD+22+PiuE7Q +dzM5GD1Qv4Y5veNP3YAuAj+YDSSD26i1DU2Bfbh9vlSMr5Gtbi7n3zDlOfhegoA3 +qxTmi3tOFUClAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBS46CSJ5Mfh5ggjcvqhdQ8WJQat1jAfBgNVHSMEGDAWgBSt +uHjwmi8n2rjqcGNrlc752hhz9jANBgkqhkiG9w0BAQsFAAOCAQEAJkUfL5fiMRX9 +I6/tP3wSSkCQ60wdpaffcxtNi8cQfqhK9v5bPUwGS0aggJgFQpuIBMNaMW0e0Qsl +hpW6j5PyuN2fLPrn/PEcCdmBiD59Yk8yV1EG0cihy0keTWNB/9RpV4fV5y4YssjT +Wv7M+HbCeQyCcdFX4AVJuU3g6BjnQrV0QX4MBv6+TrfaAJuPiQJFIrj2wJlM9awc +UX2ZZSGWKQTlEphPN47bxyUZOoLbNwhhrXpKKU+kn/MUrg2kNivuD8PLGRZ2maOe +TSHi80G9N2v+IYeweNFS4tsgzfdKmfgmSAZlIJroXzxhnfXgA+w0/QVlaE7yg/+b +PJo0iqoZGg== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-bad-sub-cert.pem b/certs/intermediate/trust_anchor/forge-bad-sub-cert.pem new file mode 100644 index 00000000000..c1c8a89589b --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-bad-sub-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLTCCAhWgAwIBAgICECEwDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAwwcd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIEludDAeFw0wMDAxMDEwMDAwMDBaFw00OTEy +MzEyMzU5NTlaMCcxJTAjBgNVBAMMHHdvbGZTU0wgQ2hhaW4gVGVzdCBGb3JnZSBT +dWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFcXWvVAtvhSNRyNJ2 +HsFne8IIfdoe6zwKExtv7uhIobaruJnfSI/CH4og8MSdP5SI97IGT98XXFHu5JJ6 +n/rA2F+5W9Fl2gxOLnhOx2/qBPNB9I4pEe61KkiIazPwPe5yavw8YAUAuERuEs1M +qk4PzMDfICm9dcEMh6a9nWw3SR2ZkWni2ZdvUqtz0RnGGFK30hGBZfjGldgcHMbt +JaRpYcRYNjDmu2tw1EgWZagrRG805pUN8c588hCpj0MhSSSbx1jALPZyVvI6+Hwa +hSBoi/yuXV78k8G56NDzviocMvh+Ta6+7oZ9Q7WbxkQ1xpGw+lunDl/l1CHwNfLc +aQhFAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTeox/CjdGylkh7HI/k3Scy9AW7fjAfBgNVHSMEGDAWgBQAAAAAAAAA +AAAAAAAAAAAAAAAAADANBgkqhkiG9w0BAQsFAAOCAQEAVyvWcuGgAHHNfQ05U+rI +JN2YYWdaW0vyBTLDgOga0NjZe91nccN9nG+HdVWX0+Xl+EM9jj/tSKgWyBFRoZBc +9LJ0m4e+AlV1iHo0gM2KGer77larNIABwdaz8S7FmoWN0Etn4UcUqXrMzKJ85IDp +DAw0sleCd6X1OQ26Er/zrSqqc9Vx+2Fjn3aWUM27z3DIH3pH6OeT/orUKjTyg+yT +TFiIQse+nuvq32q2jVjpHo7rs1gKMzzNW1jOrMJVihICxkWSdmaYk4bL4WOQZ9xP +aGqG11UWmLW9e56n3hMVnkQl7DNGd5IcCuPvBvDKLNIaOwOM/YTcu9lpsLYO36bE +QA== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-fake-int-cert.pem b/certs/intermediate/trust_anchor/forge-fake-int-cert.pem new file mode 100644 index 00000000000..a8bb6ff1f00 --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-fake-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgICEB8wDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIFEwHhcNMDAwMTAxMDAwMDAwWhcNNDkxMjMx +MjM1OTU5WjAnMSUwIwYDVQQDDBx3b2xmU1NMIENoYWluIFRlc3QgRm9yZ2UgSW50 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvGdcU39Qy3bRqctGo2F +Pq4dPw7E6QvG5Esj5JRsVR2WtVi8d4A90vBKBwdYPeKIcv03QLO8+pXji5MAZO7T +1yGfQCR9fJrnXj3XDBiR+mIspKxAGYJiRPHr40DE9mmv7hlMUYevnIfBTSm5hPpP +XNe0C525AN+/C7GBkY7jN0Dls6URrTsuZFpfI3smOO3kKUmADecAgLC7zy1+s21L +Uj0cyMY1O3upsT/wHA0TnmeRAbmqiy5IkG7ogZ3g0974N3clFq8zfDs/d2+niIrG +E7Y+R2MplLMsXCx4CAE/JLdqiO4ICvRx7VME67DKsvfXxKbPrxXkDzYixPFXQzm3 +NQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUAAAAAAAAAAAAAAAAAAAAAAAAAAAwHwYDVR0jBBgwFoAUGjMo0gIX0Nw5 +lX7XwWWpjad8mdkwDQYJKoZIhvcNAQELBQADggEBAFUXxoCV9ygFe9fZV2lQiwcP +JLDgWPWqtvFyWrrHLLmlxmZmXVIrzXgzaDd2kegYSnbuBN3dXpk5rnor1sxDGgWU +u5U0/zQgon5UY7PbQhwCAkumZodRqw+ubyPsmVRyFHv9yAG2JhYYtHPppTaHd3eX +7NvZ/7UpYVZVQr1NTFXeacReTyJ1OlQmyaN15FRD+PUeUPbN4/PaxM+rH0ysu3jd +IhvuEfG/pBwKwBDT/hYkMSBdOPKTLAP+c42Ruhq1XMBXtokkH70u7CUmxCoe2lPz +o7SecPre/i5T2MvmCRI7+8dPVLnvY7Z/LSlv5KR6T0scP0Xqieb2c4P4SbRcoDU= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem b/certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem new file mode 100644 index 00000000000..6181ea46d5f --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNDCCAhygAwIBAgICECAwDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAwwcd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIEludDAeFw0wMDAxMDEwMDAwMDBaFw00OTEy +MzEyMzU5NTlaMDExLzAtBgNVBAMMJndvbGZTU0wgQ2hhaW4gVGVzdCBGb3JnZSBG +YWtlIEludCBMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvA4j +E64nUe5+rTxkXGXY9s44Z7UOlB86i324ZjyYMZctm5rYULh4Kiceb43j5nWR2Jbv +4Qw/dIwRa2a7NbncphGuyIc/imS5+ZT3dHhXjNw1lz2VGPpqy4LKUn9xFl3sMmqw +QZagDylRcko5wPu5UvgukRlKvHBo6dVTTYK49VgksD3Lll41tNR0GkI1nf0Wwaa8 +1UnSzVCifXTKXchyjg+Q3YfKwO1tk71lWgKGGBH2tKtHyI9m6TANU+9klOoQD1Nt +cs2ln5dfndujoSkDQfd9yjHs8FONeV7bCOnHow6TrwpQZUnlFhwfKw3MdYHBksve +u819EbinbQbybM9hPwIDAQABo2AwXjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE +AwIHgDAdBgNVHQ4EFgQUl2/1p1ILjdI2VngSgKCmL7edQ3IwHwYDVR0jBBgwFoAU +AAAAAAAAAAAAAAAAAAAAAAAAAAAwDQYJKoZIhvcNAQELBQADggEBAFZ5Q8lWvfi3 +7c8FbiFxjlEd7WojsibVj8ndSAZ2wjeKqjh1Ms9EDe4sT6JC4Ayl+Rlv8ZlIqUPG +u65D6aUNpASdFq/YHydsOIVgunJYLFIg7GViY40JZz8fdO9BmTtTbikkBNKlZPA4 +VK4n6D2dC9xtJ75C/R+yOK3pN8q9CvqjFuoVCTrGvcAss99lNSEBzFFuPWM05RQd +Qy7l4RUb9QarsEy/wxh/7lWMBkjjZmgV7LeOtjgJyKPL69TgXwWKqf43dl7ktkYB +5sgYjri0q9aBwq7P4ENDijSYCPhCDra/5l1cEJw1UyuMszE9RP0BXJep9B6P2Lmn +l/IVsyhX4do= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-int-cert.pem b/certs/intermediate/trust_anchor/forge-int-cert.pem new file mode 100644 index 00000000000..ced26b9abc9 --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-int-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLjCCAhagAwIBAgICEBswDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAnMSUwIwYDVQQDDBx3b2xmU1NMIENoYWluIFRlc3QgRm9yZ2Ug +SW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtzFpX1AmnJ8iDDYJ +7Zif2/f26guodqeyi2d7HrPM5L0ylGbFyVP3bUhUuD9tFPIC79K0j8M3QrxpjPi2 +BHuRFbnt1OftbXEghyOexLA32NrOjavlsB4E2Vydqb5hg0wTFUEQ5PytPXA781/L +P11lVced52F35lG9GZL4QRhydkC9G3ppNcHiixxI3GGi/cE/JyONt8MgmjjNtwE0 +tesJ3Or32pQyI8w8UadY4lFGWlLKyo7N0JfD9j14gp9gUCLhwRPH0fM5NZEaFpnV +u+A0xovYNZfhhseNrJsQzeRNfkuRV+rGAiQvViAoRvx8Fp5OVVKa+rf5gYu+MBLi +aUGrpQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQUBwcHBwcHBwcHBwcHBwcHBwcHBwcwHwYDVR0jBBgwFoAUi6sJzKDs +7J4Jho4rVpbP+ERdt3QwDQYJKoZIhvcNAQELBQADggEBAIgTYF+oGtld5ri1Rk4x +3PrXn1U+MNkw2UNESUrDDUNVtB0hxcTy7mp653p0UJ2ltXkQz3rO0oyTvuREuFbh ++3cPux5VIcjrnewOinKxq1zZVb5LB2R2yJdMCf47u7Lo+saqhm9s1WhQZIeT4M72 +vA+MJGC8w4Jin4Zg4OQGlRpxVGRCFkjdqgyDqA7gwLnBLVuSzdpI2PQOxQ5XXOtq +TzexmZmPbhqJwLv7Zzsq2vSn8k1sH6OP9OcAHVAlxu4JGB+qrxHheDsCt7vbiKG1 +Ry7mmCl+U/SzP917qLh+0uoMLr7QNFQkBxeGYVXSSFaAsiOve5olfdNmZklGKePa +Gx0= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-q-cert.pem b/certs/intermediate/trust_anchor/forge-q-cert.pem new file mode 100644 index 00000000000..04f2de16ffd --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-q-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgICEB4wDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAwwcd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIEludDAeFw0wMDAxMDEwMDAwMDBaFw00OTEy +MzEyMzU5NTlaMCUxIzAhBgNVBAMMGndvbGZTU0wgQ2hhaW4gVGVzdCBGb3JnZSBR +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxTMwWrqcLPjFpRkmnzT +eLav68exUtBpNcDXrLgA3IG9YtkJPjeSbBXvl10iChdL4VzMT3inHoYicFqQ6rwm +ezP/rX3b4mJNm8+D7ows4IBVB6tlczVWjU04RVNOpRT4NfniBuOiZfQ60bhxbQh1 +sSaEdQyNgpdpJtNB/sAgAtIOUUfWFy5eGmm7UH/I40dsezZj1+m9dVEs6qThE0qu +gw8Dq6FPMUTMy+2lhLKWU3vF1VGOSz3pmnGvdnBKFizzLEJHU+X/3V8eed9pJmgb +efvvRc5/97YubDbWUzHWd3A1M5lVNKqOPl0/s59cZTcG5NMU0TuDZ6mXc6tb/nRx +hQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUGjMo0gIX0Nw5lX7XwWWpjad8mdkwDQYJKoZIhvcNAQELBQADggEBABNF +brTrWfUgs/vTfzN96tRtIRN1u7jZ+7NVtPTnuUf3/doYRk/ebohJXSOkh7yYaQFG +XJhBSxFLHTO/O67HLcQr2Ak7RxnxcgkNMToUhbr9IJanT9OdeTQp+T7G/Zn6RPAA +4GoM7dfjg5dp1Gv2+ZHq86LHIP9oHHyntwHUv2HDKWvtEEV2ZLwhOgOLUsmQkG/q +Ik3rLABnhORnFzK/8X3fW9dEapqheICKrsQ1lbUKeN8puAPKH2c3Nu8aFrtsHLLl +OHHQVBg9igMhn+YyRfUmQTxmdp6+H7BhA5JHLSV9+d0tY3TlnuJwqark7jjrdG5f +ZJbx1g/gYAem6BKQ7s4= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-root-cert.pem b/certs/intermediate/trust_anchor/forge-root-cert.pem new file mode 100644 index 00000000000..4acc23e1fbf --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEBowDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAoMSYwJAYDVQQDDB13b2xmU1NMIENoYWluIFRlc3QgRm9yZ2Ug +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPcxD1n1/tLuqqmt +O/EcuvbdG4JB1crpYEpPfcEgpk6aIzX2KX2KAnwCfQHrRoR1GID0Be3V/Sg7cG7A +020olQ2mVABlfjNMBjEWNdP2Sh3fbzTOS515a/2LWybvkvo0AW5ENjWkyNxLisji +u6pBbNYAZrD3COVIXD+aRXDXFwlApOX6v5OzVjirWaZ+jhyF+SWOfCE3oywH02d2 +muudOBjSvEdXArqNtt/jo3uDCuaj2Iez+ggAzrayZ8qO46g8IOk6vliGFAZmhCVC +mDuc53Y/ebHPh+HIZDYvCN18cVA111eEmG5+jCusHxYkOs7Ra3IZocjV5lBkAyun +Tsb2OF0CAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFIurCcyg7OyeCYaOK1aWz/hEXbd0MB8GA1UdIwQYMBaAFIurCcyg +7OyeCYaOK1aWz/hEXbd0MA0GCSqGSIb3DQEBCwUAA4IBAQC79KRxGcNeuJXKd0l7 +K9oywUbYX8Y+WB8zgUvLQqIzRuLoUgH5sUIHW3Y2DrzD3bnLjQQCR1SKsXkgv5EM +a/dnaP3/uWR5GJJW9f+BFw3/XSQq6LW4lNjqT0qpEPV2QFQU4brQOhOGGgireOz4 +tZ0iJ+7B0VBRagGkZbWtc8Y9ea/jRmq6GW/wT0GBi8m3QsRvTqZVX7JHD/4KJX2k +2utiWNGhYGY9Nx+jvWzaektapODG1hofAip1CMfwDWgXZrE/G3xgKFJBkGfnJwJ/ +EofL2XDFdpOVdHp3NQk2t1guyCYZUkFPdUddkISMXs4y/qAxgy4U5OuT4+aolEBf +P6rc +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-sub-cert.pem b/certs/intermediate/trust_anchor/forge-sub-cert.pem new file mode 100644 index 00000000000..5bc746f56b7 --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-sub-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgICECIwDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAwwcd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIEludDAeFw0wMDAxMDEwMDAwMDBaFw00OTEy +MzEyMzU5NTlaMCcxJTAjBgNVBAMMHHdvbGZTU0wgQ2hhaW4gVGVzdCBGb3JnZSBT +dWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFcXWvVAtvhSNRyNJ2 +HsFne8IIfdoe6zwKExtv7uhIobaruJnfSI/CH4og8MSdP5SI97IGT98XXFHu5JJ6 +n/rA2F+5W9Fl2gxOLnhOx2/qBPNB9I4pEe61KkiIazPwPe5yavw8YAUAuERuEs1M +qk4PzMDfICm9dcEMh6a9nWw3SR2ZkWni2ZdvUqtz0RnGGFK30hGBZfjGldgcHMbt +JaRpYcRYNjDmu2tw1EgWZagrRG805pUN8c588hCpj0MhSSSbx1jALPZyVvI6+Hwa +hSBoi/yuXV78k8G56NDzviocMvh+Ta6+7oZ9Q7WbxkQ1xpGw+lunDl/l1CHwNfLc +aQhFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTeox/CjdGylkh7HI/k3Scy9AW7fjANBgkqhkiG9w0BAQsFAAOCAQEA +Bq4JK7v4da0jx6z0sv4gBIUzMb6v5gQ/b57Qk2VMHzi+mQ205409t0SudqmoXthv +B1qN9F10VwP35fHG0VI459mEBEM1vHfhoOPO1OHe66LnscRauRu0L9P4pv8KIk6Z +9UTDpxmGRTAcSD2Gjeg6/ufYLNXexaA8OZJKpw9pbjhvx1lqdZUyvr1b3wQAesz5 +cg+Qd36yQOSxI/1eFptpcJW6gzvQBhpfnflQfF8tPXL6EAQEybsG0Dn1t3MJifG3 +3UDOHycTZNx0FZAPymx5YLeW8hhf72PAOqEsz7W36feBPo8e4CKHAeLyyROtQUP0 +iSoHzl+vL86c3KI0CcuNTg== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem b/certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem new file mode 100644 index 00000000000..3c71859a941 --- /dev/null +++ b/certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICECMwDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAwwcd29s +ZlNTTCBDaGFpbiBUZXN0IEZvcmdlIFN1YjAeFw0wMDAxMDEwMDAwMDBaFw00OTEy +MzEyMzU5NTlaMCwxKjAoBgNVBAMMIXdvbGZTU0wgQ2hhaW4gVGVzdCBGb3JnZSBT +dWIgTGVhZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwOIxOuJ1Hu +fq08ZFxl2PbOOGe1DpQfOot9uGY8mDGXLZua2FC4eConHm+N4+Z1kdiW7+EMP3SM +EWtmuzW53KYRrsiHP4pkufmU93R4V4zcNZc9lRj6asuCylJ/cRZd7DJqsEGWoA8p +UXJKOcD7uVL4LpEZSrxwaOnVU02CuPVYJLA9y5ZeNbTUdBpCNZ39FsGmvNVJ0s1Q +on10yl3Ico4PkN2HysDtbZO9ZVoChhgR9rSrR8iPZukwDVPvZJTqEA9TbXLNpZ+X +X53bo6EpA0H3fcox7PBTjXle2wjpx6MOk68KUGVJ5RYcHysNzHWBwZLL3rvNfRG4 +p20G8mzPYT8CAwEAAaNgMF4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4Aw +HQYDVR0OBBYEFJdv9adSC43SNlZ4EoCgpi+3nUNyMB8GA1UdIwQYMBaAFN6jH8KN +0bKWSHscj+TdJzL0Bbt+MA0GCSqGSIb3DQEBCwUAA4IBAQC/JXhiLenxhCh5er/G +E1/8qGGcGIWnRqKCTNPZ2qVXqDPtSy0i2nGiGiqZOKZfhc91NjYExgs6I9R2hnT5 +zggo0OYCilmOyf64tVfmtml6/CczWAVh9pqPU7TMdaPfa8/vCD/SdujCKbM3TLAa +fteWr1niIkl4p90Hs+ITNXRYd+YOGk1CZBfwsF6MSttl20K69+KNKvVYindqiXEg +fMi8feXgzdTCdsUTB0vlNk1baogkmXDf8kW4yqB8uvAA5RWmYQcfVbLxLMDPdoio +qlS69r9/1PyJQJGIFrxEzbyz1UYb2tYHvIJKLox9jjRPZXRUNAMmS9afdJ4g8TWO +g9oV +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/gen_certs.sh b/certs/intermediate/trust_anchor/gen_certs.sh new file mode 100755 index 00000000000..95a46393e03 --- /dev/null +++ b/certs/intermediate/trust_anchor/gen_certs.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# Regenerate the certificate set used by the chain tests of +# X509_verify_cert() in tests/api/test_ossl_x509_str.c. +# +# self-cert a self-signed CA +# self-copy-cert the same name and key as self-cert, another serial +# root-cert a self-signed CA +# link-cert the name of root-cert with its own key, issued by +# root-cert: self-issued, but not self-signed +# link-noaki-cert link-cert without an authority key identifier +# leaf-cert issued by link-cert +# notca-twin-cert a CA with the name of notca-root-cert +# (gen_dated_certs.sh) and the key of leaf-cert, issued by +# root-cert, without a subject key id +# ec-name-root-cert an EC P-256 root with the name of root-cert, without +# key ids +# +# RSA-2048 / SHA-256, ~30 year validity so the regression does not expire. +# +# Requires: openssl. + +set -e +cd "$(dirname "$0")" + +OPENSSL="${OPENSSL:-openssl}" +DAYS=10957 +RSA_BITS=2048 + +CA_EXT=$(mktemp) +CA_NOAKI_EXT=$(mktemp) +CA_NOSKID_EXT=$(mktemp) +LEAF_EXT=$(mktemp) +trap 'rm -f "$CA_EXT" "$CA_NOAKI_EXT" "$CA_NOSKID_EXT" "$LEAF_EXT" *.csr' \ + EXIT + +cat > "$CA_EXT" <<'EXT' +basicConstraints = critical, CA:TRUE +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +EXT + +cat > "$CA_NOAKI_EXT" <<'EXT' +basicConstraints = critical, CA:TRUE +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +subjectKeyIdentifier = hash +authorityKeyIdentifier = none +EXT + +cat > "$CA_NOSKID_EXT" <<'EXT' +basicConstraints = critical, CA:TRUE +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +subjectKeyIdentifier = none +authorityKeyIdentifier = keyid:always +EXT + +cat > "$LEAF_EXT" <<'EXT' +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +EXT + +genkey() { "$OPENSSL" genrsa -out "$1" "$RSA_BITS" 2>/dev/null; } + +# Self-sign: selfsign +selfsign() { + "$OPENSSL" req -x509 -new -key "$1" -sha256 -subj "/CN=$3" -set_serial "$4" \ + -days "$DAYS" -extensions v3 \ + -config <(printf '[req]\ndistinguished_name=dn\n[dn]\n[v3]\n%s' \ + "$(cat "$CA_EXT")") \ + -out "$2" +} + +# Sign a CSR: signcert +signcert() { + "$OPENSSL" x509 -req -in "$1" -CA "$3" -CAkey "$4" -set_serial "$5" \ + -sha256 -days "$DAYS" -extfile "$6" -out "$2" 2>/dev/null +} + +# Self-signed copies ----------------------------------------------------------- +genkey self-key.pem +selfsign self-key.pem self-cert.pem "wolfSSL Trust-Anchor Test Self-Signed" 1 +selfsign self-key.pem self-copy-cert.pem \ + "wolfSSL Trust-Anchor Test Self-Signed" 2 + +# Self-issued link cert -------------------------------------------------------- +genkey root-key.pem +selfsign root-key.pem root-cert.pem "wolfSSL Trust-Anchor Test Root" 1 + +genkey link-key.pem +"$OPENSSL" req -new -key link-key.pem -sha256 \ + -subj "/CN=wolfSSL Trust-Anchor Test Root" -out link.csr +signcert link.csr link-cert.pem root-cert.pem root-key.pem 2 "$CA_EXT" +signcert link.csr link-noaki-cert.pem root-cert.pem root-key.pem 3 \ + "$CA_NOAKI_EXT" + +genkey leaf-key.pem +"$OPENSSL" req -new -key leaf-key.pem -sha256 \ + -subj "/CN=wolfSSL Trust-Anchor Test Leaf" -out leaf.csr +signcert leaf.csr leaf-cert.pem link-cert.pem link-key.pem 4 "$LEAF_EXT" + +# Another signer's name -------------------------------------------------------- +"$OPENSSL" req -new -key leaf-key.pem -sha256 \ + -subj "/CN=wolfSSL Chain Test Not CA Root" -out twin.csr +signcert twin.csr notca-twin-cert.pem root-cert.pem root-key.pem 5 \ + "$CA_NOSKID_EXT" + +# A root of another key type -------------------------------------------------- +"$OPENSSL" ecparam -name prime256v1 -genkey -noout -out ec-name-root-key.pem +"$OPENSSL" req -x509 -new -key ec-name-root-key.pem -sha256 \ + -subj "/CN=wolfSSL Trust-Anchor Test Root" -set_serial 6 -days "$DAYS" \ + -extensions v3 \ + -config <(printf '[req]\ndistinguished_name=dn\n[dn]\n[v3]\n%s\n%s\n%s\n' \ + "basicConstraints = critical, CA:TRUE" \ + "keyUsage = critical, keyCertSign, cRLSign" \ + "subjectKeyIdentifier = none") \ + -out ec-name-root-cert.pem +rm -f ec-name-root-key.pem + +echo "Completed" diff --git a/certs/intermediate/trust_anchor/gen_dated_certs.sh b/certs/intermediate/trust_anchor/gen_dated_certs.sh new file mode 100755 index 00000000000..f0d75dff51c --- /dev/null +++ b/certs/intermediate/trust_anchor/gen_dated_certs.sh @@ -0,0 +1,432 @@ +#!/bin/bash + +# Regenerate the certificates with fixed dates used by the chain check tests +# of X509_verify_cert() in tests/api/test_ossl_x509_str.c. +# +# dated-root-cert a self-signed CA, valid 2000 to 2049 +# dated-root-late-cert dated-root-cert valid 2020 to 2049, same name and +# key +# expired-root-cert a self-signed CA, valid 2010 to 2019 +# expired-root-leaf-cert issued by expired-root-cert, valid 2000 to 2049 +# expired-int-cert a CA issued by dated-root-cert, valid 2010 to 2019 +# dated-int-cert expired-int-cert valid 2000 to 2049, same name and +# key +# dated-int-leaf-cert issued by dated-int-cert, valid 2000 to 2049 +# notca-root-cert a self-signed cert with CA:FALSE +# notca-root-leaf-cert issued by notca-root-cert +# nobc-root-cert a self-signed v3 cert without basicConstraints or +# keyUsage +# nobc-root-leaf-cert issued by nobc-root-cert +# v1-root-cert a self-signed X.509 v1 cert +# v1-root-leaf-cert issued by v1-root-cert +# cross-root-cert dated-root-cert's name and key, issued by +# expired-root-cert, valid 2000 to 2049 +# cross-root-late-cert cross-root-cert valid 2020 to 2049 +# renewed-root-cert a self-signed CA with expired-root-cert's name and +# a new key, valid 2020 to 2049 +# renewed-root-leaf-cert issued by renewed-root-cert, no authority key id, +# valid 2000 to 2049 +# expired-root-int-cert a CA issued by expired-root-cert, valid 2000 to +# 2049 +# expired-root-int-leaf-cert issued by expired-root-int-cert, valid 2000 to +# 2049 +# expired-root-link-cert a CA with expired-root-cert's name and a new key, +# issued by expired-root-cert, valid 2000 to 2049 +# expired-root-old-link-cert expired-root-link-cert valid 2010 to 2019 +# expired-root-link-leaf-cert issued by expired-root-link-cert, valid 2000 +# to 2049 +# expired-root-nokid-cert a self-signed CA with expired-root-cert's name, a +# new key and no key ids, valid 2000 to 2049 +# nokid-root-cert a self-signed CA without key ids, valid 2000 to +# 2049 +# badkid-int-cert a CA issued by nokid-root-cert with an authority +# key id that does not match, valid 2010 to 2019 +# badkid-int-leaf-cert issued by badkid-int-cert, valid 2000 to 2049 +# noaki-root-cert a self-signed CA without an authority key id, +# valid 2000 to 2049 +# noaki-root-link-cert a CA with noaki-root-cert's name and a new key, +# issued by noaki-root-cert, valid 2000 to 2049 +# noaki-root-leaf-cert issued by noaki-root-cert, valid 2000 to 2049 +# forge-root-cert a self-signed CA +# forge-int-cert a CA issued by forge-root-cert, subject key id +# 07:07:... +# forge-fake-int-cert a CA with forge-int-cert's name, a new key and the +# subject key id 00:00:..., issued by forge-q-cert +# forge-fake-int-leaf-cert issued by forge-fake-int-cert +# forge-q-cert a CA without an authority key id, signed by the +# forge-fake-int-cert key in forge-int-cert's name +# forge-bad-sub-cert a CA with the authority key id 00:00:..., signed +# by another key in forge-int-cert's name +# forge-sub-cert forge-bad-sub-cert's name and key, without an +# authority key id, signed by the +# forge-fake-int-cert key in forge-int-cert's name +# forge-sub-leaf-cert issued by forge-sub-cert +# alt-root-cert a self-signed CA +# alt-int-cert a CA issued by alt-root-cert, valid 2020 to 2049 +# alt-end-cert a CA issued by a CA that is not kept, so no +# chain goes on above it +# alt-end-int-cert alt-int-cert's name and key, issued by +# alt-end-cert, valid 2000 to 2049 +# alt-old-int-cert alt-end-int-cert valid 2010 to 2019 +# alt-notca-int-cert alt-end-int-cert with CA:FALSE +# alt-int-leaf-cert issued by the key of alt-int-cert +# wrongca-root-cert a self-signed CA +# wrongca-cert a CA issued by wrongca-root-cert +# wrongca-int-cert a CA without an authority key id, named as issued +# by wrongca-cert but signed by another key +# wrongca-old-int-cert wrongca-int-cert valid 2010 to 2019, with the +# authority key id of that key +# wrongca-int-leaf-cert issued by wrongca-int-cert +# int-crl an empty CRL of the expired-int-cert key +# dated-root-empty-crl an empty CRL of dated-root-cert +# dated-root-crl a CRL of dated-root-cert, revokes expired-int-cert +# +# RSA-2048 / SHA-256. The keys are not kept. +# +# Requires: OpenSSL 3.0 or later and GNU sed. + +set -e +cd "$(dirname "$0")" + +OPENSSL="${OPENSSL:-openssl}" +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT + +cat > "$WORK/ca.cnf" <<'CNF' +[ca] +default_ca = ca_sect +[ca_sect] +dir = . +database = $dir/index.txt +new_certs_dir = $dir +serial = $dir/serial +crlnumber = $dir/crlnumber +crl_extensions = crl_ext +default_md = sha256 +policy = policy_any +unique_subject = no +[policy_any] +commonName = supplied +[req] +distinguished_name = dn +[dn] +[ca_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +[notca_ext] +basicConstraints = critical, CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +[nobc_ext] +subjectKeyIdentifier = hash +[leaf_ext] +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +[v1_leaf_ext] +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature +subjectKeyIdentifier = hash +[noaki_leaf_ext] +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature +subjectKeyIdentifier = hash +authorityKeyIdentifier = none +[nokid_ca_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = none +authorityKeyIdentifier = none +[badkid_ca_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = hash +2.5.29.35 = DER:30168014010101010101010101010101010101010101010101 +[noaki_ca_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = hash +authorityKeyIdentifier = none +[forge_int_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +2.5.29.14 = DER:04140707070707070707070707070707070707070707 +authorityKeyIdentifier = keyid:always +[forge_fake_ext] +basicConstraints = critical, CA:TRUE +keyUsage = critical, keyCertSign, cRLSign +2.5.29.14 = DER:04140000000000000000000000000000000000000000 +authorityKeyIdentifier = keyid:always +[crl_ext] +authorityKeyIdentifier = keyid:always +CNF +: > "$WORK/index.txt" +echo 1000 > "$WORK/crlnumber" + +genkey() { "$OPENSSL" genrsa -out "$WORK/$1" 2048 2>/dev/null; } + +# The serial of each cert, as in the committed set. Sets were made in +# separate runs, so some serials repeat. +serial_of() { + case "$1" in + dated-root-cert.pem|wrongca-root-cert.pem) echo 1000 ;; + expired-root-cert.pem|wrongca-cert.pem) echo 1001 ;; + expired-root-leaf-cert.pem|wrongca-gone-self.pem) echo 1002 ;; + expired-int-cert.pem|wrongca-int-cert.pem) echo 1003 ;; + dated-int-cert.pem|wrongca-old-int-cert.pem) echo 1004 ;; + dated-int-leaf-cert.pem|wrongca-int-leaf-cert.pem) echo 1005 ;; + notca-root-cert.pem) echo 1006 ;; + notca-root-leaf-cert.pem) echo 1007 ;; + nobc-root-cert.pem|v1-root-cert.pem) echo 1008 ;; + nobc-root-leaf-cert.pem|v1-root-leaf-cert.pem) echo 1009 ;; + cross-root-cert.pem) echo 100A ;; + renewed-root-cert.pem) echo 100B ;; + renewed-root-leaf-cert.pem) echo 100C ;; + expired-root-int-cert.pem) echo 100D ;; + expired-root-int-leaf-cert.pem) echo 100E ;; + dated-root-late-cert.pem) echo 100F ;; + cross-root-late-cert.pem) echo 1010 ;; + expired-root-link-cert.pem) echo 1011 ;; + expired-root-old-link-cert.pem) echo 1012 ;; + expired-root-link-leaf-cert.pem) echo 1013 ;; + nokid-root-cert.pem) echo 1014 ;; + badkid-int-cert.pem) echo 1015 ;; + badkid-int-leaf-cert.pem|expired-root-nokid-cert.pem) echo 1016 ;; + noaki-root-cert.pem) echo 1019 ;; + noaki-root-link-cert.pem|forge-root-cert.pem) echo 101A ;; + noaki-root-leaf-cert.pem|forge-int-cert.pem) echo 101B ;; + forge-fake-self.pem) echo 101C ;; + forge-junk-self.pem) echo 101D ;; + forge-q-cert.pem) echo 101E ;; + forge-fake-int-cert.pem) echo 101F ;; + forge-fake-int-leaf-cert.pem) echo 1020 ;; + forge-bad-sub-cert.pem) echo 1021 ;; + forge-sub-cert.pem) echo 1022 ;; + forge-sub-leaf-cert.pem) echo 1023 ;; + alt-root-cert.pem) echo 1027 ;; + alt-int-cert.pem) echo 1028 ;; + alt-gone-self.pem) echo 1029 ;; + alt-end-cert.pem) echo 102A ;; + alt-end-int-cert.pem) echo 102B ;; + alt-old-int-cert.pem) echo 102C ;; + alt-notca-int-cert.pem) echo 102D ;; + alt-int-leaf-cert.pem) echo 102E ;; + *) echo "No serial for $1" >&2; return 1 ;; + esac +} + +# Issue a cert, with a database of its own so that serials can repeat: +# issue +# +issue() { + local sign ext + : > "$WORK/index.txt" + serial_of "$1" > "$WORK/serial" + "$OPENSSL" req -new -key "$WORK/$3" -subj "/CN=$2" -config "$WORK/ca.cnf" \ + -out "$WORK/req.csr" + if [ "$4" = "self" ]; then + sign=(-selfsign -keyfile "$WORK/$5") + else + sign=(-cert "$WORK/$4" -keyfile "$WORK/$5") + fi + ext=() + if [ "$6" != "none" ]; then + ext=(-extensions "$6") + fi + (cd "$WORK" && "$OPENSSL" ca -batch -config ca.cnf -notext \ + "${sign[@]}" "${ext[@]}" -startdate "$7" -enddate "$8" \ + -in req.csr -out "$1" 2>/dev/null) +} + +# Make a CRL that lists the certs revoked so far: +# crl +crl() { + (cd "$WORK" && "$OPENSSL" ca -gencrl -config ca.cnf \ + -cert "$2" -keyfile "$3" -crl_lastupdate "$LONG" \ + -crl_nextupdate "$LONG_END" -out "$1" 2>/dev/null) +} + +# Revoke a cert at the start of 2010: revoke +revoke() { + (cd "$WORK" && "$OPENSSL" ca -config ca.cnf -revoke "$1" \ + -cert "$2" -keyfile "$3" 2>/dev/null) + sed -i 's/^\(R\t[^\t]*\t\)[^\t]*/\1100101000000Z/' "$WORK/index.txt" +} + +OLD=20100101000000Z +OLD_END=20191231235959Z +NEW=20200101000000Z +LONG=20000101000000Z +LONG_END=20491231235959Z + +genkey root.key +genkey expired-root.key +genkey int.key +genkey notca.key +genkey nobc.key +genkey v1.key +genkey leaf.key +genkey renewed-root.key +genkey expired-root-int.key +genkey expired-root-link.key +genkey expired-root-nokid.key +genkey nokid-root.key +genkey badkid-int.key +genkey noaki-root.key +genkey noaki-link.key +genkey forge-root.key +genkey forge-int.key +genkey forge-fake.key +genkey forge-junk.key +genkey forge-q.key +genkey forge-sub.key +genkey alt-root.key +genkey alt-int.key +genkey alt-end.key +genkey alt-gone.key +genkey wrongca-root.key +genkey wrongca.key +genkey wrongca-gone.key +genkey wrongca-int.key + +issue dated-root-cert.pem "wolfSSL Chain Test Dated Root" root.key self \ + root.key ca_ext $LONG $LONG_END +issue expired-root-cert.pem "wolfSSL Chain Test Expired Root" \ + expired-root.key self expired-root.key ca_ext $OLD $OLD_END +issue expired-root-leaf-cert.pem "wolfSSL Chain Test Expired Root Leaf" \ + leaf.key expired-root-cert.pem expired-root.key leaf_ext $LONG $LONG_END +issue expired-int-cert.pem "wolfSSL Chain Test Int" int.key \ + dated-root-cert.pem root.key ca_ext $OLD $OLD_END +issue dated-int-cert.pem "wolfSSL Chain Test Int" int.key \ + dated-root-cert.pem root.key ca_ext $LONG $LONG_END +issue dated-int-leaf-cert.pem "wolfSSL Chain Test Int Leaf" leaf.key \ + dated-int-cert.pem int.key leaf_ext $LONG $LONG_END +issue notca-root-cert.pem "wolfSSL Chain Test Not CA Root" notca.key self \ + notca.key notca_ext $LONG $LONG_END +issue notca-root-leaf-cert.pem "wolfSSL Chain Test Not CA Root Leaf" \ + leaf.key notca-root-cert.pem notca.key leaf_ext $LONG $LONG_END +issue nobc-root-cert.pem "wolfSSL Chain Test No BC Root" nobc.key self \ + nobc.key nobc_ext $LONG $LONG_END +issue nobc-root-leaf-cert.pem "wolfSSL Chain Test No BC Root Leaf" \ + leaf.key nobc-root-cert.pem nobc.key leaf_ext $LONG $LONG_END +issue v1-root-cert.pem "wolfSSL Chain Test V1 Root" v1.key self v1.key \ + none $LONG $LONG_END +issue v1-root-leaf-cert.pem "wolfSSL Chain Test V1 Root Leaf" leaf.key \ + v1-root-cert.pem v1.key v1_leaf_ext $LONG $LONG_END +issue cross-root-cert.pem "wolfSSL Chain Test Dated Root" root.key \ + expired-root-cert.pem expired-root.key ca_ext $LONG $LONG_END +issue renewed-root-cert.pem "wolfSSL Chain Test Expired Root" \ + renewed-root.key self renewed-root.key ca_ext $NEW $LONG_END +issue renewed-root-leaf-cert.pem "wolfSSL Chain Test Renewed Root Leaf" \ + leaf.key renewed-root-cert.pem renewed-root.key noaki_leaf_ext $LONG \ + $LONG_END +issue expired-root-int-cert.pem "wolfSSL Chain Test Expired Root Int" \ + expired-root-int.key expired-root-cert.pem expired-root.key ca_ext \ + $LONG $LONG_END +issue expired-root-int-leaf-cert.pem \ + "wolfSSL Chain Test Expired Root Int Leaf" leaf.key \ + expired-root-int-cert.pem expired-root-int.key leaf_ext $LONG $LONG_END +issue dated-root-late-cert.pem "wolfSSL Chain Test Dated Root" root.key self \ + root.key ca_ext $NEW $LONG_END +issue cross-root-late-cert.pem "wolfSSL Chain Test Dated Root" root.key \ + expired-root-cert.pem expired-root.key ca_ext $NEW $LONG_END +issue expired-root-link-cert.pem "wolfSSL Chain Test Expired Root" \ + expired-root-link.key expired-root-cert.pem expired-root.key ca_ext \ + $LONG $LONG_END +issue expired-root-old-link-cert.pem "wolfSSL Chain Test Expired Root" \ + expired-root-link.key expired-root-cert.pem expired-root.key ca_ext \ + $OLD $OLD_END +issue expired-root-link-leaf-cert.pem \ + "wolfSSL Chain Test Expired Root Link Leaf" leaf.key \ + expired-root-link-cert.pem expired-root-link.key leaf_ext $LONG $LONG_END +issue expired-root-nokid-cert.pem "wolfSSL Chain Test Expired Root" \ + expired-root-nokid.key self expired-root-nokid.key nokid_ca_ext $LONG \ + $LONG_END +issue nokid-root-cert.pem "wolfSSL Chain Test No Key Id Root" nokid-root.key \ + self nokid-root.key nokid_ca_ext $LONG $LONG_END +issue badkid-int-cert.pem "wolfSSL Chain Test Bad Key Id Int" badkid-int.key \ + nokid-root-cert.pem nokid-root.key badkid_ca_ext $OLD $OLD_END +issue badkid-int-leaf-cert.pem "wolfSSL Chain Test Bad Key Id Int Leaf" \ + leaf.key badkid-int-cert.pem badkid-int.key leaf_ext $LONG $LONG_END +issue noaki-root-cert.pem "wolfSSL Chain Test No AKI Root" noaki-root.key \ + self noaki-root.key noaki_ca_ext $LONG $LONG_END +issue noaki-root-link-cert.pem "wolfSSL Chain Test No AKI Root" \ + noaki-link.key noaki-root-cert.pem noaki-root.key ca_ext $LONG $LONG_END +issue noaki-root-leaf-cert.pem "wolfSSL Chain Test No AKI Root Leaf" \ + leaf.key noaki-root-cert.pem noaki-root.key leaf_ext $LONG $LONG_END + +# A forged chain. The subject key id 00:00:... puts forge-fake-int-cert in +# the first row of the cert manager's CA table, before forge-int-cert, so a +# lookup by name finds it first. forge-fake-self and forge-junk-self only +# sign other certs. +issue forge-root-cert.pem "wolfSSL Chain Test Forge Root" forge-root.key \ + self forge-root.key ca_ext $LONG $LONG_END +issue forge-int-cert.pem "wolfSSL Chain Test Forge Int" forge-int.key \ + forge-root-cert.pem forge-root.key forge_int_ext $LONG $LONG_END +issue forge-fake-self.pem "wolfSSL Chain Test Forge Int" forge-fake.key \ + self forge-fake.key forge_fake_ext $LONG $LONG_END +issue forge-junk-self.pem "wolfSSL Chain Test Forge Int" forge-junk.key \ + self forge-junk.key forge_fake_ext $LONG $LONG_END +issue forge-q-cert.pem "wolfSSL Chain Test Forge Q" forge-q.key \ + forge-fake-self.pem forge-fake.key noaki_ca_ext $LONG $LONG_END +issue forge-fake-int-cert.pem "wolfSSL Chain Test Forge Int" forge-fake.key \ + forge-q-cert.pem forge-q.key forge_fake_ext $LONG $LONG_END +issue forge-fake-int-leaf-cert.pem "wolfSSL Chain Test Forge Fake Int Leaf" \ + leaf.key forge-fake-int-cert.pem forge-fake.key leaf_ext $LONG $LONG_END +issue forge-bad-sub-cert.pem "wolfSSL Chain Test Forge Sub" forge-sub.key \ + forge-junk-self.pem forge-junk.key ca_ext $LONG $LONG_END +issue forge-sub-cert.pem "wolfSSL Chain Test Forge Sub" forge-sub.key \ + forge-fake-self.pem forge-fake.key noaki_ca_ext $LONG $LONG_END +issue forge-sub-leaf-cert.pem "wolfSSL Chain Test Forge Sub Leaf" leaf.key \ + forge-sub-cert.pem forge-sub.key leaf_ext $LONG $LONG_END + +# Two paths to the issuer of alt-int-leaf-cert: through alt-int-cert to +# alt-root-cert, or through alt-end-cert, which alt-gone-self signed and so +# ends nowhere. +issue alt-root-cert.pem "wolfSSL Chain Test Alt Root" alt-root.key self \ + alt-root.key ca_ext $LONG $LONG_END +issue alt-int-cert.pem "wolfSSL Chain Test Alt Int" alt-int.key \ + alt-root-cert.pem alt-root.key ca_ext $NEW $LONG_END +issue alt-gone-self.pem "wolfSSL Chain Test Alt Gone" alt-gone.key self \ + alt-gone.key ca_ext $LONG $LONG_END +issue alt-end-cert.pem "wolfSSL Chain Test Alt End" alt-end.key \ + alt-gone-self.pem alt-gone.key ca_ext $LONG $LONG_END +issue alt-end-int-cert.pem "wolfSSL Chain Test Alt Int" alt-int.key \ + alt-end-cert.pem alt-end.key ca_ext $LONG $LONG_END +issue alt-old-int-cert.pem "wolfSSL Chain Test Alt Int" alt-int.key \ + alt-end-cert.pem alt-end.key ca_ext $OLD $OLD_END +issue alt-notca-int-cert.pem "wolfSSL Chain Test Alt Int" alt-int.key \ + alt-end-cert.pem alt-end.key notca_ext $LONG $LONG_END +issue alt-int-leaf-cert.pem "wolfSSL Chain Test Alt Int Leaf" leaf.key \ + alt-int-cert.pem alt-int.key leaf_ext $LONG $LONG_END + +# A trusted issuer whose key did not sign: wrongca-int-cert names +# wrongca-cert's name as its issuer and has no authority key id, but the key +# of wrongca-gone-self signed it. wrongca-old-int-cert is its untrusted twin, +# out of date, with the authority key id of that key. +issue wrongca-root-cert.pem "wolfSSL Chain Test Wrong CA Root" \ + wrongca-root.key self wrongca-root.key ca_ext $LONG $LONG_END +issue wrongca-cert.pem "wolfSSL Chain Test Wrong CA" wrongca.key \ + wrongca-root-cert.pem wrongca-root.key ca_ext $LONG $LONG_END +issue wrongca-gone-self.pem "wolfSSL Chain Test Wrong CA" wrongca-gone.key \ + self wrongca-gone.key ca_ext $LONG $LONG_END +issue wrongca-int-cert.pem "wolfSSL Chain Test Wrong CA Int" \ + wrongca-int.key wrongca-gone-self.pem wrongca-gone.key noaki_ca_ext \ + $LONG $LONG_END +issue wrongca-old-int-cert.pem "wolfSSL Chain Test Wrong CA Int" \ + wrongca-int.key wrongca-gone-self.pem wrongca-gone.key ca_ext $OLD \ + $OLD_END +issue wrongca-int-leaf-cert.pem "wolfSSL Chain Test Wrong CA Int Leaf" \ + leaf.key wrongca-int-cert.pem wrongca-int.key leaf_ext $LONG $LONG_END + +crl int-crl.pem dated-int-cert.pem int.key +crl dated-root-empty-crl.pem dated-root-cert.pem root.key +revoke expired-int-cert.pem dated-root-cert.pem root.key +crl dated-root-crl.pem dated-root-cert.pem root.key + +# Only now, so a failed run leaves the certs and CRLs as they were. +cp "$WORK"/*-cert.pem "$WORK"/*-crl.pem . +echo "Completed" diff --git a/certs/intermediate/trust_anchor/int-crl.pem b/certs/intermediate/trust_anchor/int-crl.pem new file mode 100644 index 00000000000..fc26971c912 --- /dev/null +++ b/certs/intermediate/trust_anchor/int-crl.pem @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBnDCBhQIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZ3b2xmU1NMIENo +YWluIFRlc3QgSW50Fw0wMDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaoDAwLjAf +BgNVHSMEGDAWgBTNWHR2YyG4UlQTWJ+oRDsGtqtOzTALBgNVHRQEBAICEAAwDQYJ +KoZIhvcNAQELBQADggEBAJtW0FfVR3p8+bJV01Xb4HG2qeHOsjDTSx3yMF10h1aL +xaMt6kQx7BoAQLA/SN1GnyK/h5Jdn6NWioabxbH5qjJ+XMIdRK0tX/uOHL+wiVZ1 +5pm+vR8W/Lko3XR1gd5mL03vm4SDXE5/6ja2AypWIuq8dtfdKBPfhiuxHlUckbDw +w+KllHwnLkVVwrVq7/ilmuj18d5QwyEuHy2nUhr+GcS2L+6VyKfgf2Fv9jqEe1sD +qVnIWADZK7zAIZ05LqCb85dv0lqJV/2L9ko4D5BVJfbv3oX9H/SqxWSE0L1cPs7C +s8WslATFIt6f4tqndj3FpsC5hQNWVDd/wF3o8s9WGqc= +-----END X509 CRL----- diff --git a/certs/intermediate/trust_anchor/leaf-cert.pem b/certs/intermediate/trust_anchor/leaf-cert.pem new file mode 100644 index 00000000000..7adb5adda77 --- /dev/null +++ b/certs/intermediate/trust_anchor/leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgIBBDANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB53b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDIxMTI0NzAxWhgPMjA1 +NjEwMjAxMjQ3MDFaMCkxJzAlBgNVBAMMHndvbGZTU0wgVHJ1c3QtQW5jaG9yIFRl +c3QgTGVhZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKcyOCJ5zCTQ +h+pEebPAzTbxb168tT9kDu6BcI6ZY5j/qBiIdHBKAwKnUzYvxXl1oWUshz9lSN60 +pT+Xi4M8BJT7hp1ZeYNCKP+hHBDqoqjlBJZa38YzjWCBWDHfl7LhexKO6PsXPSA8 +IzEcpc798rF5VGT37yY6QJwiuL3JqYGWvys3mJlFV9Q9WD5aCZBJpF8huPgzwdKI +xVZOMEvsyRINyBqx8ixijAOGjZV/AFM1gAUR49AGOBPY/QYiC0LlUm7c1ZvvqQ9b +SsoKU8kqhucYM6EHTk9+VTnPnk+3hSkHjGzs+DcfP55rcZivEN6+LzlIBe8RxW6D +ZeixglU0jjUCAwEAAaNgMF4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4Aw +HQYDVR0OBBYEFLPjVkrc2IFpN8IOcXQDYuaLIAmNMB8GA1UdIwQYMBaAFID3Ff/B +1OglhclVjdLKGxCuWkyJMA0GCSqGSIb3DQEBCwUAA4IBAQBt2A/Yzw/0BvtJlOoq +duhiATreEm2nH9ej3RVeCOAdcpZuh5Tv0dX0NSgtT6L/p0P0Ag6Sq77qSB2o7DZJ +axxtIxIpn7belfv3A+wsYW67hz0XVBQAwjpoWoq55jel8lov6kfj5btdcO+A+Z05 +RDUnCSlnxnjJxYdcGdyzW4f7LXeWgTGsU3RyqXSzG2kUzV5NMANI08XqdX2eCJJs +oXrkCznaDsRTT4u5JyjtKzCWs5kYvHA4txFBsaKs8fH/+OVp3Xk6mtgIWAzeaz9x +aXl7owqqo3RcUyo3giYojD+IRtlWi8uJ++9g0C1uyjqxOFFMrEFHm1MMl94Wdzu+ +fF0f +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/leaf-key.pem b/certs/intermediate/trust_anchor/leaf-key.pem new file mode 100644 index 00000000000..dd9115b5906 --- /dev/null +++ b/certs/intermediate/trust_anchor/leaf-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnMjgiecwk0Ifq +RHmzwM028W9evLU/ZA7ugXCOmWOY/6gYiHRwSgMCp1M2L8V5daFlLIc/ZUjetKU/ +l4uDPASU+4adWXmDQij/oRwQ6qKo5QSWWt/GM41ggVgx35ey4XsSjuj7Fz0gPCMx +HKXO/fKxeVRk9+8mOkCcIri9yamBlr8rN5iZRVfUPVg+WgmQSaRfIbj4M8HSiMVW +TjBL7MkSDcgasfIsYowDho2VfwBTNYAFEePQBjgT2P0GIgtC5VJu3NWb76kPW0rK +ClPJKobnGDOhB05PflU5z55Pt4UpB4xs7Pg3Hz+ea3GYrxDevi85SAXvEcVug2Xo +sYJVNI41AgMBAAECggEAIhUXI1P/vNtzcVpvEqjxTEtwH/hI3Di54H2rY646AtTO +v92FGuTDc1WHnWqkjyte8KBgm5g6gAeEDhAG5QQljPcvDEFSh2LDt8RwU7R43wP8 +aOfyaNVWcv866sPBCTp0m6ZdMN4eXEVWCStlU2oqAGqSvRhbfEfwpsNfNHT45Ezx +ldcP2XJfgQGzyX1HJIm9Fqn5fBf5b/RdAcUtIIFYuPezSK2OQsUVtbyEIfJsv3BL +KRcdnBRKrAK/XULyG/wjQk1rru/d8RRLiBzzecKSrl2n2y02YAxtRNbXXqD3CkTO +qpOpAybQwrdSVgLYJeyoDdCQRjOgdEw880sZPaTX4QKBgQDXX0x6n5EKVmO1dwwm +CSp6ie0+2TaTGO+jlxLH2jujxbCfw8NXr+TZPoPZNZRYDhpA9aUArUwF5bZLVKz5 +OIMzG1Q3G63XbXkfmvq9+/kUeuqjkvEvsBET0uE6ij2bkyBgDmt1u5Z9JMJbnVK7 +FQ6bzw4JKInxiZVYOMk5dg64kQKBgQDGvGnJVb8hYc/Fa6PDNFLD62TSJtse6tlu +leyMEELj97me8pJz4xH8pN7zN+xx/End1R5TwkZ6pYdCwJlno+rQCJEjGREP/1V8 +YGxs3dqap7puSNZFAAJQS7V7pfeCVYemaDcIiWRVaXAddVrHp7vb/jmaOlI8seCp +7VdPl9BtZQKBgDqUv1azRF5NdT2Mrg9Cev1tusxXhNKp+7TBiOg3YfV/9jTeqKmX +PGYi7PYN7tWssFNu/XybpowMGXOQCqSXTIiALH3412Y7brC8FOvMN8nsTRpFA7l2 +6I490+l6ZJuGf03hXjVd2ucbMop81l3wslKLFUREdJDwXWDrwUAR7pshAoGBAMZC +iLZWWIqQ7ZnhvEmtFTRmzPnp0YeSer+byZQhVNnf9Mng7MG3seX9BH5XOAauzXIE +fnDIy07ne3zYbzG5kTNgmIoPI7p1225tMz2YRdErqw0ClN/ZFnXMUYy9EaJweRdF +uEFH71RWD4cT4DB14S+i+wwlSo1KVvquz/+TUFcRAoGABO67ydAUOd4tRQf83xNY +OLw8lEcEv+fuTAKDFKTZ4mZKX8ONOe2nShcX6MJTehCKqqkbHtNX66HNeyFNQcp9 +STP2nEp5X6KfZjJPbMZ6Z0jwJFHjMsbXElr37Gff9nEa1F3ckWp+/uuzh3XWNs8C +bDsXCgIZiylm6kVzqSoARmY= +-----END PRIVATE KEY----- diff --git a/certs/intermediate/trust_anchor/link-cert.pem b/certs/intermediate/trust_anchor/link-cert.pem new file mode 100644 index 00000000000..3e349d2abbb --- /dev/null +++ b/certs/intermediate/trust_anchor/link-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB53b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDIxMTI0NzAxWhgPMjA1 +NjEwMjAxMjQ3MDFaMCkxJzAlBgNVBAMMHndvbGZTU0wgVHJ1c3QtQW5jaG9yIFRl +c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCbixPO1Frv +E9KsBdoku1IDW2vswd98ZlLO524GaOf5wMpkOyM7SpB2CHDfJmC6FGbGHO7WqF35 +LiPbIXc/KaGUndOBg4nyoDo11fPY4ky3b5a6qDqLVys3ZiBvrtEs4DiuAzcurxQj +cv52r9gMaSkc5hFmopR9ljb56IUtv9QlXq/ihD06vv/t9m9GXI8pIwulLH4oMEE0 +bESAozPEo7I4d9veDFpEh4l2wxwE6ukrffMZ+b2WU8Jn6T1tkzB+noyVl+7RdV2h +S3YzQJ+DxB6YSImsYpszpV4uJ/3wZMXaiyfwpp7jcbdj+WbQyeT3+mKj6uGQrQCO +DznAIaDlW88CAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFID3Ff/B1OglhclVjdLKGxCuWkyJMB8GA1UdIwQYMBaAFIwO +RxqU9YE1l9Tosboq5IOhHV4nMA0GCSqGSIb3DQEBCwUAA4IBAQCQ7y5ehDBrJw82 +C/cmeqwyrv0pNuFcyd3a5NqFsF05rQhTUdA5pJGMLLLPsHAJsy2emEI/AcrX2oX8 +A4Djb2N6yQsMwBptNa4KdfOKjxyw0Htd5ApDPjzIbDjqjwJ/7DeAkzxRUarYLh5D +u4a1Al2xmjxeAK3Joxbxt7++2KCsF3ptr8A2PnUgKh1j8+SvlC9arb4Xi9B4ObWq +pHqZm2lJTt/n2g3/e1IWrKQ7HI5QgtAzmJ1yebqJb8brJ98N7NBe1Kba92kZyWvA +8jTi2YKlQmMLzGbUfVhYmAx2MbS7KdonLX3iy5XKS4YrSV1v4HyCEqDMehrQZZWJ +ssLik18d +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/link-key.pem b/certs/intermediate/trust_anchor/link-key.pem new file mode 100644 index 00000000000..3684ce3f4d1 --- /dev/null +++ b/certs/intermediate/trust_anchor/link-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAm4sTztRa7xPS +rAXaJLtSA1tr7MHffGZSzuduBmjn+cDKZDsjO0qQdghw3yZguhRmxhzu1qhd+S4j +2yF3PymhlJ3TgYOJ8qA6NdXz2OJMt2+Wuqg6i1crN2Ygb67RLOA4rgM3Lq8UI3L+ +dq/YDGkpHOYRZqKUfZY2+eiFLb/UJV6v4oQ9Or7/7fZvRlyPKSMLpSx+KDBBNGxE +gKMzxKOyOHfb3gxaRIeJdsMcBOrpK33zGfm9llPCZ+k9bZMwfp6MlZfu0XVdoUt2 +M0Cfg8QemEiJrGKbM6VeLif98GTF2osn8Kae43G3Y/lm0Mnk9/pio+rhkK0Ajg85 +wCGg5VvPAgMBAAECggEADwTR+SRv+KXwihqchjXGXuR2H4ShsCyXdK4/lkZtAxG9 +j8qgYI9CBuESqBfZJUJn+YT0l/HTGn4mCT5lbf1V4LdMVe7LyVj1rN7/z6OXhIXq +mfqfcu6PSIZpse5415oZOIeRJp9poDdiGHzmxKu1vjKhBLWtHckWiojoMbevNU/l +7biKAqv6aFxXq4ftI7BiKtQfLNc8uzfDKt0IleSixPIz5aWVEPAsstM14XVMCacK +jUcN03rCR83sIJzHhX1vsyPY0BWY+uT4bbU0XwzgGOKeswaZxGxDf9ohiFMWy1MW +D3eaZdNzG+ISq8ObSBIQoeLftLE81sCm8knv7EvXsQKBgQDw7nAlX5JQjhe04n/u +UiL/u48VQBHD0um+PLiX1R+GEnuSnFjwSvgftMoT7bxmh11CnTd8Hkohgp1Sz3Gx +OjkqzKaROS+A/9jqoVW96u/BdSW5zPEV/71RjrJmfb4OE9pf0T3ETpkYmDTd/+YC +xrZYBUM+e1n+35/5B+BslMuPiQKBgQDMp2QOlkpZjhHEUd4O8cF3KRkleKTp+ntR +rLuZNTSnUk9L4fxuvWYBYyovm0NzDUZ/pgW8Q4uPrkXsg/JbpRp+sQt/vc+WZczj +JMSPO6MyyAk6vxIwuFk58G5HIVpoiqpr5klkQRG+mMVpwhNJC9q0av6kX1p85yEy +wG6iFUeilwKBgHyYkl6BgkUudowQ5l/1Eix2bpRqwcUnecHr3PqIOPk47bbwK/54 +hsC+2hJcS6YqMsyUSXFiqKuTxJBiv0UL+yxrEWjgzfREW4VbUeAZc0v0rUHc/oCF +9P4Mca4XMQZOtgv/RZYTn5DchdqYz/Z3lS0H67KQUAZC15QNwr3kZTEhAoGARw67 +xADU0eFtM9AFHQU92kTv0T3hbMgmt0YVYO3DWXFPzRYrErw6RbMLu7naIoFyf/HC +Pgrp0zp8VtG9faqG14RbbMAX6q1wBVIH2X+oe4JPChG7nCT1/hMIBzSCcpfq8knk +I6YgJiBfMwhKr+soxcn8nk/pWELcGQYNo8NluwMCgYBcjVqrHJ6u5bBmum+kIWi5 ++RS31zflxruy0oX+55NndS4TFqnHP6lmVnnHeZIzBxqfKKjJAqy3o4zF+zKQ+E/+ +5BSQVZ2fHiuHLMdOVfXUNrOR/CTd5TFrP00mCZP6XcLqDC2RyC1oJ1elKpxfGsNF +5eLgn6AYerET6zBr80DH0A== +-----END PRIVATE KEY----- diff --git a/certs/intermediate/trust_anchor/link-noaki-cert.pem b/certs/intermediate/trust_anchor/link-noaki-cert.pem new file mode 100644 index 00000000000..814575c68a1 --- /dev/null +++ b/certs/intermediate/trust_anchor/link-noaki-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfmgAwIBAgIBAzANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB53b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDIxMTI0NzAxWhgPMjA1 +NjEwMjAxMjQ3MDFaMCkxJzAlBgNVBAMMHndvbGZTU0wgVHJ1c3QtQW5jaG9yIFRl +c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCbixPO1Frv +E9KsBdoku1IDW2vswd98ZlLO524GaOf5wMpkOyM7SpB2CHDfJmC6FGbGHO7WqF35 +LiPbIXc/KaGUndOBg4nyoDo11fPY4ky3b5a6qDqLVys3ZiBvrtEs4DiuAzcurxQj +cv52r9gMaSkc5hFmopR9ljb56IUtv9QlXq/ihD06vv/t9m9GXI8pIwulLH4oMEE0 +bESAozPEo7I4d9veDFpEh4l2wxwE6ukrffMZ+b2WU8Jn6T1tkzB+noyVl+7RdV2h +S3YzQJ+DxB6YSImsYpszpV4uJ/3wZMXaiyfwpp7jcbdj+WbQyeT3+mKj6uGQrQCO +DznAIaDlW88CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFID3Ff/B1OglhclVjdLKGxCuWkyJMA0GCSqGSIb3DQEBCwUA +A4IBAQCFVADQ0o8nBgvQFcFmu6GWNJmaOVMAlAzB8OAO21UnoobtJSRMKXbM1iaL +9clY2bWPBbGWq97AfLedYZOGuv2d+bXUU/75wfdwyEIKLojSn0LB2QxIwkWLAqHz +yTINOZ8pBkHenFm91dRGs36jTw0D2W7dX3jQK5TL+d6uBrXmVqW/TZ16KvGQ5G4W +CYUtnJmMnim7vJ+tnEpfk3fZpCnzD5c8Y/n1bsE2xzhPs3TDGgubb7ts9lv/sIb0 +gmVae8+kZykJNc+72EASoUN8HUOxkvhGFHnlw1yxi9gxtq2TBl1js1oaxY7F2Gkd +7N/R6igT1mgH3IHbe9853C9pXPBF +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/noaki-root-cert.pem b/certs/intermediate/trust_anchor/noaki-root-cert.pem new file mode 100644 index 00000000000..2a2e8252c7a --- /dev/null +++ b/certs/intermediate/trust_anchor/noaki-root-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEDCCAfigAwIBAgICEBkwDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAwwed29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEFLSSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5 +MTIzMTIzNTk1OVowKTEnMCUGA1UEAwwed29sZlNTTCBDaGFpbiBUZXN0IE5vIEFL +SSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqwGk6FcP12Wd +9ST4BAyGiVGzwu3EKiT2LKzPcghRsTUWheLfZZYCgnb3hf9yqXcVOsR9ZoIURrHS +MYeQoY4tOGviv7JITa/YEZEkrNBLk0aE9yi/sNhStQ8nA8YvcGrkYyzqKBjml7/F +SmWMSOTCyKxI+TwAMj3JXSPpv3vzeYsl9nyfH+JZrNRRJgQX71vYpFqH9y6o6P7K +rl3wOtWjZebNI/DzJPT0zl7cV6haxBWLovf5QeQZR2b2+JbCvVPWivRO6Dh29WSf +njxYC2HmX6V9xDe9Aj3ApDvsdUdW8Phl0p29iQqU2EQBAPwI3cDkwlLMSXsqX/v7 +ZA+OgddKSQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUYph4W3RTyMWoquBRIxpEKmDhaXowDQYJKoZIhvcNAQELBQAD +ggEBAAWLe4GpwqnTcM7ogMZ5e806v6Ls74Vte+4JHHMykBXBNvheyF7OO35+2PuD +x9sGmmYLyOY/Xc7Q2sbPVGwLvjmBBOcvZywrnFbtUgxbgNLMwhGNQg56bQSyZnXX +FJkELinkFNLEyygcQK/spWm9T9fGzNl85uzMhhuxz6Gm4DLdAFy+FMLi1LRwxWE1 +gxJoPFCFTGkq3q437cIf2+2SnBRlU0PFnDhwYpsO0or/yH1ff+3RH9WQB8FluEkS +RBGHXqBU4AQY6LpDP5KCaXFPSk3a8WdEBM7eBgSA5w+L8YdBlZjp88SYmozJHdXt +nLYjHrhS8tpfR/D5TJAfS5Oi4kg= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem b/certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem new file mode 100644 index 00000000000..15496422307 --- /dev/null +++ b/certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEBswDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAwwed29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEFLSSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5 +MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjd29sZlNTTCBDaGFpbiBUZXN0IE5vIEFL +SSBSb290IExlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQ6M5g +fTl6yl6X0wiRUDORCrdVZVu1pBcl2lq4rsubHgUz9rORSBsS9/Cea8YOt9kVq+gj +ftiVHsSdPiKCVN4Mu1KtPIaAPEljMqOZX9wQK6VPYFjb404gkxAKmijqLeYVHITM +N6NxcG6DBapvlAJqyJtbwtT8lxVN4P3mUdn4Y2eNFOYhOTSou52QXVsxuS0UjCqT +NvvV5P0z3K+nnKEGCUNErcWEmX+zp4j4aNTL5UjpD4UN5PkW71iQBEvXzzwr++ba +Pc7eGUlCZnItjQksv/iam6Hw7M/Gcnc2e2hHdlx3nzswd8gB6So3KyxP9dI9/J72 +8v9stoJiiS+d39UNAgMBAAGjYDBeMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD +AgeAMB0GA1UdDgQWBBTD5WGW3KdPoBYEDzUq1sHQGJK+5DAfBgNVHSMEGDAWgBRi +mHhbdFPIxaiq4FEjGkQqYOFpejANBgkqhkiG9w0BAQsFAAOCAQEAKxgoJbVrPfAB +C4eqadevk9spAHtV9mavIBNP4kSba7lsRNC+JjwXkDjvs0puq30ab2iL5uB5ROMd +02GmTq9WD5kIbY0+ksVIl9Q7rINlozPHP6XVAWWxx2EwlE2vLo7T7YzwRMWr+kr9 +89HbVIjWeFxu2KsRhpr4i6XbQ2Zj+bIgtTK62BzekjMPfGlYE9BpsMAkZAxjVN7t +2VhYPrAez0K7QfluBIhXx7Uv8v78JM2a2Fec3pSr6IRXlts2Fo0CwGMl/CfjVlLg +CyoT+CF+/TQzXgBWC7ly8x8h8RiYY+v9PfeJS/jKEvt9WIPVGgk0z9J+E877hX0Q +berTIgvZZw== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/noaki-root-link-cert.pem b/certs/intermediate/trust_anchor/noaki-root-link-cert.pem new file mode 100644 index 00000000000..c1171d8b01e --- /dev/null +++ b/certs/intermediate/trust_anchor/noaki-root-link-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgICEBowDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAwwed29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEFLSSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5 +MTIzMTIzNTk1OVowKTEnMCUGA1UEAwwed29sZlNTTCBDaGFpbiBUZXN0IE5vIEFL +SSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6EV+czr84HTk +o4xKINhMvQ2DRIiY9XwsP0nZqafTfH8ZwDhLd5ziiIDUDDamB2277exSJzv+5YZh +PaE79OiQBHUjcgy/OX+ewCSyvcegAWpvYwzcQ3hUw5X/c5S/vzQWw6K12eJiILmG +j9k+XpUKpzr+faiGwJYWq3t2STBtwx7qOzxbvS2gaUZGg/dzsHPOiBJGCMvYr2t7 +rlnkFRVnczWx8jrYwO7NozuNvjccCqH5synrT0s2D11QdBqs4WT+bOw9shQyTwUe +j5mgwboR0isjYpuAqVOqinTugVALLRsu/v48GrN+iRY1ahb+74BepsjDGiN8c7iy +Q1IrwrtYtwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUcjqT/XBIBwt5wH6d5sZyXAvl7zEwHwYDVR0jBBgwFoAUYph4 +W3RTyMWoquBRIxpEKmDhaXowDQYJKoZIhvcNAQELBQADggEBAJiPVTfBz7v/ECbk +bFpwyBqt4jgjeSm8hv/8uaTagygeuWhmRE8zKhKLJvNxIhZxYorWnOqJM68rpCmx +OFCxIk5oa5N9nlPoT6K0zk6Q1i8D6IlDsQ7KHW/sKyoqPnD6N7svV/0P1pzFhrj3 +TC55wNMd/UyRRD+5/WCmLIYGJxapik61oM4qNJZQxqckeLUt7//+eTkkqHmKdcu/ +4HVacI+sXfRHr1+eOWBpkQqYrfbTlrGPXODFefUXDHGuNNizY++JP/niD3Av2Hl1 +gjbA1v0BpiN0KEc+Iu1VMq+84M46Rx5aLF7moxwyspG0KZVA0uMx2Y+aylMsEQ5O +sUPdmZs= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/nobc-root-cert.pem b/certs/intermediate/trust_anchor/nobc-root-cert.pem new file mode 100644 index 00000000000..7b97c7f5dbc --- /dev/null +++ b/certs/intermediate/trust_anchor/nobc-root-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7TCCAdWgAwIBAgICEAgwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEJDIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAoMSYwJAYDVQQDDB13b2xmU1NMIENoYWluIFRlc3QgTm8gQkMg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPcrAsuJSdliuRlx +XWN62ntyUw/6FVnI9GK6kOMca3RNsqNQp4zX+0oalIW/dTVb6S8M0hGS1bxp1fe2 +t+Z7mT5DHVu4y3mH8yWw1a/mlUayl8GogMPgqwhoZe14ZxE9DV9ElAPz6rwhM9EI +dZmqQ2Er5a2jux71Dcw5fKXj/0I2cTW7t5pCBoD6A4WbWXKIb9hB8bDa5oplYGsN +rXECzxXrzkW0aEiKZ7dkr6Dmwwif/Ll7lIzZWpHLvfywmUSldxJcACN/2PP5XIem +hnk4ause87KYIcVsf1QXVg0sVYTTId0L3EaQbrDbChz4s5X5/sq1aCwf7xtH1sb1 +VbzbWv8CAwEAAaMhMB8wHQYDVR0OBBYEFEGUVuWZbyydxi1h1NJQuRrW2IyYMA0G +CSqGSIb3DQEBCwUAA4IBAQDFLhJgi+dnvjw7PdJ3Ud/XJF+pKQvHIXH4hEVbpUQu +e+bAnG0XhRa1P5sJifUhjNPhzXHrylzhqy+YJa0ISDgnVuFZsMow5WRW5II+He0B +OINZsXv/nouTLhhxCcgolsR3yCpn/noYJizKFahvbc7s2uvVP4E/doz3XU0EnRvk +CxdUrl/8bxazC6KI/dHKXEUtTJes+yLZkG0874OMsEK2Vxo+8TYBi0yhSjJlf76m +jBw3eVT0aPatn7u74GRL2o0z/xc1bU4pV+ZxE8NqHt+dNVLo4xrbO8GfiBMhofok +IjaxWaJTc/IRXOHtKwC3XcUOfhCS1UPfsmI3qlBW+2js +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem b/certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem new file mode 100644 index 00000000000..c33bfd6e574 --- /dev/null +++ b/certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMTCCAhmgAwIBAgICEAkwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAwwdd29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEJDIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkx +MjMxMjM1OTU5WjAtMSswKQYDVQQDDCJ3b2xmU1NMIENoYWluIFRlc3QgTm8gQkMg +Um9vdCBMZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0OjOYH05 +espel9MIkVAzkQq3VWVbtaQXJdpauK7Lmx4FM/azkUgbEvfwnmvGDrfZFavoI37Y +lR7EnT4iglTeDLtSrTyGgDxJYzKjmV/cECulT2BY2+NOIJMQCpoo6i3mFRyEzDej +cXBugwWqb5QCasibW8LU/JcVTeD95lHZ+GNnjRTmITk0qLudkF1bMbktFIwqkzb7 +1eT9M9yvp5yhBglDRK3FhJl/s6eI+GjUy+VI6Q+FDeT5Fu9YkARL1888K/vm2j3O +3hlJQmZyLY0JLL/4mpuh8OzPxnJ3NntoR3Zcd587MHfIAekqNyssT/XSPfye9vL/ +bLaCYokvnd/VDQIDAQABo2AwXjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIH +gDAdBgNVHQ4EFgQUw+VhltynT6AWBA81KtbB0BiSvuQwHwYDVR0jBBgwFoAUQZRW +5ZlvLJ3GLWHU0lC5GtbYjJgwDQYJKoZIhvcNAQELBQADggEBAIurJthtxaVvX5cl +7F1plvy3WXaprsx9mxWl7w7H4eOJJbxde6c6Dys4SU0Xm7E9G/b4F8Kd/hin9o5M +w09wCNUoV4+IQKdmc7D652FVFT/K4f2tMJc49anOzZFDHX51oFObw45KtEHrE1HU +6xKe4A6zitNCfhYE37OC4cqp5qcl/pAZLqjDtQe+7ZPWKTqhuqxfyu8caiZd9l/a +W+TZr7T+w/dMkg2WpDJ2rb8zAMC1zH3UaExwvnqeQlVYoImU32n9bcRwouxBiCoK +1uIU7C3hil1cKG6ha3MkYbnx+21yHDnMofp3pJo32dOydP4qkqnwmMgO8Hx935JK +VwEHhlk= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/nokid-root-cert.pem b/certs/intermediate/trust_anchor/nokid-root-cert.pem new file mode 100644 index 00000000000..3d3eeabaf29 --- /dev/null +++ b/certs/intermediate/trust_anchor/nokid-root-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgICEBQwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhd29s +ZlNTTCBDaGFpbiBUZXN0IE5vIEtleSBJZCBSb290MB4XDTAwMDEwMTAwMDAwMFoX +DTQ5MTIzMTIzNTk1OVowLDEqMCgGA1UEAwwhd29sZlNTTCBDaGFpbiBUZXN0IE5v +IEtleSBJZCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAppIa +B0paJPNf1sSBAwsjVc/hxxR+BRbmLUwgOsrb9nBWNtCVHxrW1NaMCUtOrj3YFjdq +kuluB6gU46gHsemCayoYhahDtS8YMAoqFATnRowzGpjZ8ukRTd7G0WAd3d8asJXw +/ppi60L/PN7qHe6nyIQROU45SvxL4TcCx+iVnpvQdJQNMVCRzX7hGUo3Kox5niz7 +EdtYEvO6aXfBYtJ769psWVgIaY4YwTN5BNpGSYzvo0T20ChzyXiAmYnwlFvcIQej +ZpXx31Ci4iZs1I/v5UnbNNNoYu3H+27rZB0+Qo7fz8jHfC39xtk1cfuKiAm3zBzI +uaLcbiQjbEdtnp9AzwIDAQABoyMwITAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAUSfAboAOB2/ns6+vSTwdCLoOMIRL +dk+oyBry9nVMdlsWstvnqyc1jO8+efC+aY3FtdFuIxz74WAAGPMFJfE7dD8hpJCI +1mtg8uMXxA/ddHos7CE0Pt4sKwKxWRDRteR3IEEDj9zluZwFCr0zCyTmIKDBWg/r +8uGQM/H+kgNYT0DoUX8BCqvpm8bbKK3Cr5HJZ/YvotgbbWFa6PcpClLYs4Zx6QZM +2LfAXmwUgvPZ5JtjOAMV94C2UVj6XO+Ka+4UOo/+1W4YLCcLGlyySWCc+KSQ8UGJ +ud49fdRQUqQaSFNISCf6TFnArCOD5Yi+sbmPA4SVUqvdSFy3rkTSLEUOMA== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/notca-root-cert.pem b/certs/intermediate/trust_anchor/notca-root-cert.pem new file mode 100644 index 00000000000..f576fb20ca4 --- /dev/null +++ b/certs/intermediate/trust_anchor/notca-root-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHjCCAgagAwIBAgICEAYwDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAwwed29s +ZlNTTCBDaGFpbiBUZXN0IE5vdCBDQSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5 +MTIzMTIzNTk1OVowKTEnMCUGA1UEAwwed29sZlNTTCBDaGFpbiBUZXN0IE5vdCBD +QSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs5iYZgCCO9nh +ZcdXSfvfFGf+9O/EughyiSpFcOCNXx8WBQ885G50HCWWWlY4Niylo8cx1CZbMKme +FD64uIUXoWIIBe3JZqeQhg2ZvZfv2v0KrqN8YF2X/5jCBWMPKNeBezIfEtekJfNj +N8s++T7jqmbcQNylSMq5OrBfo07DXg4FzYMcHscbjPMVcLivVAA6BvtiuGEBhfTa +oYIRlSWmaSt0h+Arjlqqv1HGbTsQ3mb7D9Wy+LNou0HOTzahJ07wlyF/IajvYcdF +mp69UzIzW9iCzqKjIBoxn5wI/bA90GEHcd8WrJ32LUjNIkbkx3W5eCUlaueiP1Go +7g4lgWv92wIDAQABo1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRaBc3GJMSK +ipWlEFGRK7450eknVjAfBgNVHSMEGDAWgBRaBc3GJMSKipWlEFGRK7450eknVjAN +BgkqhkiG9w0BAQsFAAOCAQEAPVN0cQXZE1kaocsCaKusLIW5ZNk/cDmMtzyvYA+R +pYgUHhlZ4kOvY7Pq7ay1KyAiRsXvRiZ6J64kLUReb7muPNR2HBYlOeZpV9NQn38h +hOxVc0YHHt3Nr3HL4EK7tOPidMeQXYR2/r8BAO9MlPxdN8EE5JaTinUL9DGYOCCz +TSTsYeXiJ1g33r/vwswtEnAS3bI4ZMpZ6VerPfKb5QZaWsizAqbYH/fu0Uv08PjM +1lXd4VJd/ljgd81taf83ediDCtJ/1x0iyf2R/SWd4Ts5q74npKJZKtH8/8hnPLF2 +JStg0tR0DEKTdXWGNpLiZz4p6zyrwCQ2/wCAJ2Hg8r9D+A== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/notca-root-leaf-cert.pem b/certs/intermediate/trust_anchor/notca-root-leaf-cert.pem new file mode 100644 index 00000000000..ebec0c8a6e4 --- /dev/null +++ b/certs/intermediate/trust_anchor/notca-root-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEAcwDQYJKoZIhvcNAQELBQAwKTEnMCUGA1UEAwwed29s +ZlNTTCBDaGFpbiBUZXN0IE5vdCBDQSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5 +MTIzMTIzNTk1OVowLjEsMCoGA1UEAwwjd29sZlNTTCBDaGFpbiBUZXN0IE5vdCBD +QSBSb290IExlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA3idX +OiKA8cPeDYbow0+DJrJ+T08L3Td+mM8yHqVKoH++RFenQI+j4sMXXy0yYn0mRSTo +zBNZwrWTpJFpOOqwJ12bMsARoZmJI1Rg3mgo4XWQfIz6MuMLAfy2uWfw4dX6l1Vl +brWtzmBo+++kY0Yu0Q//3aQTTDVX4PXjXNfkjJKtCU7GZTobJcub2A2geVhZ+O8B +lNmOEylnvEYh5rQ2pkPhAnZQCX9DkX9wX3kj9nE1s5+8MsPdiTcJowlywkw3OuF0 +6ZuvpLVjL9XxmQ5Naju0tvlPdRIYqIFMdKW/6Vj4I/5S0p1t6nfEu5VzqG9E6B38 +hmVWgswza6vjZx4LAgMBAAGjYDBeMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD +AgeAMB0GA1UdDgQWBBRqgHxv9d/A7yClcJsTIeaFLW5ezDAfBgNVHSMEGDAWgBRa +Bc3GJMSKipWlEFGRK7450eknVjANBgkqhkiG9w0BAQsFAAOCAQEAdGW9W6iiRYQo +vCK5AsBSj/r6QESUb07RGyuhWhZVxfuNYJ4QBpTZFS45dp+JC8rHSAaHlU48eJmZ +hvNAMCYTkAmSvnrdliUpJGF72/7V3pKSOqPzLzA6JwAVCInJc5oxAjOiyrFGAUTU +KIvQBNzQ7EArKb56CCA/NnG2Yl9LO4bGCmCC8jsk3tRPsDaPqn2rSNVioO8JODZI +c5K49EQ6S0L1P5hcp4l1RQ1waVlyzkMf8rFQ58FCdfkalVOCmlQrX7sE92UF8ijb +DodWseMtvfieJKBpAPm9KNDxpQete8vzgEon6jD9zkehqBoucVUS0qGbfP3oR2fx ++XRp9MCGBA== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/notca-twin-cert.pem b/certs/intermediate/trust_anchor/notca-twin-cert.pem new file mode 100644 index 00000000000..a96e65a358a --- /dev/null +++ b/certs/intermediate/trust_anchor/notca-twin-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIBBTANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB53b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDIzMTgwMTA3WhgPMjA1 +NjEwMjIxODAxMDdaMCkxJzAlBgNVBAMMHndvbGZTU0wgQ2hhaW4gVGVzdCBOb3Qg +Q0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKcyOCJ5zCTQ +h+pEebPAzTbxb168tT9kDu6BcI6ZY5j/qBiIdHBKAwKnUzYvxXl1oWUshz9lSN60 +pT+Xi4M8BJT7hp1ZeYNCKP+hHBDqoqjlBJZa38YzjWCBWDHfl7LhexKO6PsXPSA8 +IzEcpc798rF5VGT37yY6QJwiuL3JqYGWvys3mJlFV9Q9WD5aCZBJpF8huPgzwdKI +xVZOMEvsyRINyBqx8ixijAOGjZV/AFM1gAUR49AGOBPY/QYiC0LlUm7c1ZvvqQ9b +SsoKU8kqhucYM6EHTk9+VTnPnk+3hSkHjGzs+DcfP55rcZivEN6+LzlIBe8RxW6D +ZeixglU0jjUCAwEAAaNEMEIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHwYDVR0jBBgwFoAUjA5HGpT1gTWX1Oixuirkg6EdXicwDQYJKoZIhvcNAQEL +BQADggEBAH4/tD/gV6HCNRfDnO4czp0C8z9K5d/NbtlzmeCnrPImn6WOBluTKWtD +3XBV/bs1yBxFaEql8Z6hm2Bl2TCn/ScMOMLpN4cNeu5zsd6FNP47lJKcdfR6W6wB +3Xy/Vwj6PyORZ6oeTgJxRl+ijguw8Jon2d6Ajg3cCD4wM+RY/c/ITPDz7CRyV6lj +wqhg5aKT7j+hXmdhgMyPuHGE6A4dsdwCXuxP8EyeHRXkbEEUVOIkgumXTEyCoYCN +P8NSoj2nh+MwjDk96Ct/A/63d9ZLe1HB+6n4cCOCVs0AJgz6C9ACu245OVscr1xg +WT5gCjwoL2z7va6GcXYw9eXUNb3c8Zk= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/renewed-root-cert.pem b/certs/intermediate/trust_anchor/renewed-root-cert.pem new file mode 100644 index 00000000000..1bd385436a6 --- /dev/null +++ b/certs/intermediate/trust_anchor/renewed-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhugAwIBAgICEAswDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0yMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMCoxKDAmBgNVBAMMH3dvbGZTU0wgQ2hhaW4gVGVzdCBFeHBp +cmVkIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCay6BcEtD2 +TInZEcKzAIuvQFaSU8VI/j3ffB3q6XyQSPC4OVS882W8JpA42FuAXyI+ssecDqF0 +RpqjsN6rgb/sZutN4LPLYd/dBSV9tyut1C7ZD+zFE1tFYAhmiahJGL4UZphG7d4a +GcGEcYp9OOLAJhLUSSd4eclDN+Io7WHh+kiP5eio2oZKCJ217t0FdsY2FszZiqym +RXBY+7nGZUI2cnfNiqTri4RhWWbnyXpMWnWlo6ml7fV8/3m5gkQ1sBemEizXRdEN +HUdBd73k+aRIO9sJmVvaSmC3OOqV3gc7QUqQxe+Sh18vragzXxBTTIjQDyt4BpVq +szuZIYuqkIzTAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQbaf6hJhMp1O/HIDUGOSoZ/hPFizAfBgNVHSMEGDAWgBQb +af6hJhMp1O/HIDUGOSoZ/hPFizANBgkqhkiG9w0BAQsFAAOCAQEAB0DI8UYHv/NA +vbhJy1j2g3fyJRQk8Jy2/h6rNVwGc23ZpiRRpsucuV5oUjtayZpoxCCOiXy5PRbG +M9ZSKrF4FMRWJYeFg47yeNNmnFXgLM8u96f6gJmTId5g/ETx5du1C7FzGYvBttGb +GnpxvdpYX6gBiC73uyl3KmdnMOwSvo6kYR5CMT6AhGp1s651v3EVoxIQ7ReSDTk+ +SvYSErzR4OMhFO9d9ZDZhQnRsWPTzusgvrVppSl0yvRZnrr2NtjNpGYdIZlofZqQ +H54LMGhx5Q02GboKMKbIVY3hC5VnUrHlVkprkwoQvna30N0/F9XMSWiibh4A6cbe +PvIcpJLX3Q== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem b/certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem new file mode 100644 index 00000000000..d790d1f4ffe --- /dev/null +++ b/certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFDCCAfygAwIBAgICEAwwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IEV4cGlyZWQgUm9vdDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMC8xLTArBgNVBAMMJHdvbGZTU0wgQ2hhaW4gVGVzdCBSZW5l +d2VkIFJvb3QgTGVhZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMDe +J1c6IoDxw94NhujDT4Mmsn5PTwvdN36YzzIepUqgf75EV6dAj6PiwxdfLTJifSZF +JOjME1nCtZOkkWk46rAnXZsywBGhmYkjVGDeaCjhdZB8jPoy4wsB/La5Z/Dh1fqX +VWVuta3OYGj776RjRi7RD//dpBNMNVfg9eNc1+SMkq0JTsZlOhsly5vYDaB5WFn4 +7wGU2Y4TKWe8RiHmtDamQ+ECdlAJf0ORf3BfeSP2cTWzn7wyw92JNwmjCXLCTDc6 +4XTpm6+ktWMv1fGZDk1qO7S2+U91EhiogUx0pb/pWPgj/lLSnW3qd8S7lXOob0To +HfyGZVaCzDNrq+NnHgsCAwEAAaM/MD0wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8E +BAMCB4AwHQYDVR0OBBYEFGqAfG/138DvIKVwmxMh5oUtbl7MMA0GCSqGSIb3DQEB +CwUAA4IBAQAvYOTd7r4jmlcvXBbFY3zZCXqvrylQVTT5bmCEjbtlG0gIIGz/Dpfm +oUmvUIVFOYATfHa+LPdpkynovzFpUXVfbmJc6cydcyYwW2CCYeCSqTiXmkr6jdHB +DtgrrhV21xuiPw7trzman3t+0qS3rpo9Y3tKkKoxxjsJZYzZMC0lMseYymllQiZV +RbEbJxhcvVkxpb++rFdw47f6/0TUI+EMshrILKA+ADOjibVF97Vl1lr5VeWi8Ppo +y3FKMo64wm2m5oSWsTncdPoCTLFa8CYnZDw4uOOpy1XnCrlehQcoWcpLADOIInsh +uJx+3QhmqbWFNMqTM0a0++BukQUAUpco +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/root-cert.pem b/certs/intermediate/trust_anchor/root-cert.pem new file mode 100644 index 00000000000..cd475011703 --- /dev/null +++ b/certs/intermediate/trust_anchor/root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMjCCAhqgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMScwJQYDVQQDDB53b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFJvb3QwIBcNMjYxMDIxMTI0NzAwWhgPMjA1 +NjEwMjAxMjQ3MDBaMCkxJzAlBgNVBAMMHndvbGZTU0wgVHJ1c3QtQW5jaG9yIFRl +c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALpo+V9mn3HN +zFYYbHE2rDeTFizP9MsbpWnOoHWZ51OkGtiB/esUJk41NxEmBSRkY5TuKz6f1QaJ +16iYGZhqMF7DxYMm1x3Kd9i5KHPVjVCpFLMUouJAwLvspk1mUksIrtg35osMwmG+ +skaD/7WRFjjb5aCVkZyrHeWagoxlyO2okHIkYWp10IUrkFpz9Ma5yDRR1cs079bI ++439D98Srm9oedReIUII+9ZA9if0yY5pH4uvkfaJplzhhvAkdnse8RyIkrITTOg0 +QJUNaNPsaLNn2BWWI7x4y5t/tJOVaFcfZLvrIpHqN1lqplCQeRd75uBQLpEa5QHt +GmVrCx3g3BcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIwORxqU9YE1l9Tosboq5IOhHV4nMB8GA1UdIwQYMBaAFIwO +RxqU9YE1l9Tosboq5IOhHV4nMA0GCSqGSIb3DQEBCwUAA4IBAQBukrxYbF56cotF +s+Oam1nU9mUWoCDRsTYkRe6K2745zaS3r5g9pd4T5ilhAQQn8TgR44ysWY4XkKNc +vcoHxzCRqxbcPrjv+p5+Cim5j99hEXvKL00sz38DoK0eEMCUMpEjt7m+OuFyVjqk +/xUol14SwnGlGEnRfsoVLs/4/okxMtZOOa8sfIvqlJAuUZq9SrvKpVkKYwzCWYU8 +hIXc+BzXUcHWAAGAhiHvyIDvY403JnectFPoIku4T+usJdUIQ7WPoqCKc79Gcfv0 +OcnfyO5VOMFH96JdPfbVcfstrDx7Qe5Xkuv8SPvtF5tzn0Ks94rPFrPPsBkra9l+ +61OiJsHN +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/root-key.pem b/certs/intermediate/trust_anchor/root-key.pem new file mode 100644 index 00000000000..b55bebfab19 --- /dev/null +++ b/certs/intermediate/trust_anchor/root-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6aPlfZp9xzcxW +GGxxNqw3kxYsz/TLG6VpzqB1medTpBrYgf3rFCZONTcRJgUkZGOU7is+n9UGideo +mBmYajBew8WDJtcdynfYuShz1Y1QqRSzFKLiQMC77KZNZlJLCK7YN+aLDMJhvrJG +g/+1kRY42+WglZGcqx3lmoKMZcjtqJByJGFqddCFK5Bac/TGucg0UdXLNO/WyPuN +/Q/fEq5vaHnUXiFCCPvWQPYn9MmOaR+Lr5H2iaZc4YbwJHZ7HvEciJKyE0zoNECV +DWjT7GizZ9gVliO8eMubf7STlWhXH2S76yKR6jdZaqZQkHkXe+bgUC6RGuUB7Rpl +awsd4NwXAgMBAAECggEAAKQy03a1br1QCi3MVblj708EwQO4Kmi+cXZCDBWK+7kx +VHRiDMiFdzhsp5TzaJQl2ZHWOUFwiLS5tr1LPUCESRcwW0qra9FSBz9f2cIu2cuz +JAiapN+nKh5e1h5XgyWVAG50zx6snox67Y4vt463vltXV22cUicuk6cr8tK41cSA +tYQSEWGLf9PmuFFKmyNtsrGn2tCS0/5slWVCgsHcVZ4bmOL3Sn8nSP5Q1Du4oyda +8pVeI3F0chi1trByoELgaLRPpkP5Hq+sc0WbAGoJRR0mTU9RfAuFDD4upaFKbeoL +0Nt5lRARHH/VFLGGteNexdTEgV3LWeAtMCV9y4mlkQKBgQDkN2sPwYYku/qGVXqa +F0G02kF2AmX0uKRNTWv6VG8Bk/rZEPZ1CDT3xWrKLzr19kuOszX73HZCDgtept1R +c6CpX+IjlUlnoMoAh3zgIuIaBL/IaB6oGr1OfrTCxDRspDq7QwNebYGhGZr6MpAw +sdBZh/Qy9fEE5mBA2ET2oRJbpwKBgQDRGp6LnONRrX2dWqw+YjuGcO9lfofdhkzk +8vrE60gu3lT54x7QmK9eJtwMskhyq/2p5AmDBGZkP+bVhvdRonPeokoToy1cTMIT +1wJRCq610fDQJjwdj5A+YBRLJAapukzYIJfMnSTXc4d+HC08Aa0SglFiMc/xgtO7 +Gbd1hNHKEQKBgCZm/OkF06DS0PFR6POM/k2EfIdzF9z6RQuM/6J8GsHVONzV8eL3 +opIe8j1bfTxuWYCFv4vGKVAsNNWUlqFiJ6LYs6DxOgzqDZPROL4uoIVoTy1V9/Kp +rTlMMPalNX0ilY91U1fMbYfCW0XGxGStRzYJnpoEsK32ZjQDMHef2wr5AoGAJho5 +pasS6QE6EIBDPqLbweW2WcIys7UTMezssRFGd/PffCFcrhoEap2eH1olRW/9qwV4 +jj0xAzd5spMDYga+ImVkJivnyhfajY7yRx4xM8RroyI0dhaA7DzJtd98r1TZR9C4 +mHpSCkhs+hfcTkDn7qV8eu4kvBdgyYkoGuv25mECgYEAilDjJxqvBhQ5nrSquPKk +4gzjTcDH/j38MuTjSmUFNuQQDr3nRKIGHV408e2yW460v0nSLAcBrvnk7aexlZ2y +PZP6FoZsdZpU1nPP6s6eL3i4L47uQPnMVK08DAGZLEDx4oHRk9Xq6j7ySQEU2EMC +R78EOLZWxnb5j+aDrheRie4= +-----END PRIVATE KEY----- diff --git a/certs/intermediate/trust_anchor/self-cert.pem b/certs/intermediate/trust_anchor/self-cert.pem new file mode 100644 index 00000000000..7b630cb942a --- /dev/null +++ b/certs/intermediate/trust_anchor/self-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQDCCAiigAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDDCV3b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFNlbGYtU2lnbmVkMCAXDTI2MTAyMTEyMzM1 +NVoYDzIwNTYxMDIwMTIzMzU1WjAwMS4wLAYDVQQDDCV3b2xmU1NMIFRydXN0LUFu +Y2hvciBUZXN0IFNlbGYtU2lnbmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA0UQi54OPhzLzaD4/mrQuTfsn6SflV5PRm2ENweGp7R3ud5g1+aDrm38J +SvZL7tHERgXqTXsYeUbZbH1HiZ8681NufUTPWKQTf/MU8CUduprQCOmrOXK2z0/j +YRW1KpfOLdLZyZO03QOR2dLKSa3F4JA+V7o9ad+sehAWNiGcyTZbkrfe1sv/M1M1 +nx0mGccB28mPnojLzKE64R06JC0c7/0chyfDCSHSHp/qG9rWx8TYQqcQh6sz3a6k +dJINAMeaSH9dWONRvVI9QvGVOQJiAZi6hmOKVYn1HGwJR9v/aTXonlIdTLDQmIGH +avUA6Ti6SHTLHTGOvzNesDJGQSgQkwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUutrRDd3QT9a6mPVfMxhiI92Sz84w +HwYDVR0jBBgwFoAUutrRDd3QT9a6mPVfMxhiI92Sz84wDQYJKoZIhvcNAQELBQAD +ggEBAAG5cfrgJf+2JSLyF8jez3UaKSxbeyAsBqNBBwaPH5TRr0/0G6DMuJFwf9Qv +MJE+bGo4fkltDzq3jEfHxWa8D4PlLbi/QP2QxUXt8wWZc+rKuKsqgpY1udd53ipe +9Qb8EbpC5yPytVNhgPI0KY6GmrpYuUY3gcuuKhJIQf8Ph4xtZSO7tEgajrgOmvvf +bfDOhKeo0+KjomKUTDzUq/ChYXayM5hKlre1d1qFXrPZNbJ2VV5+CnCa2epzKG6h +4VXc9iUD2nYnlPPfPNAab0eJwf3qwB+TkTOpsBtEpXumbeNP4+axfUJFniEMoBPb +I2u7HV3MVq5OBOI29AWT76iqxkg= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/self-copy-cert.pem b/certs/intermediate/trust_anchor/self-copy-cert.pem new file mode 100644 index 00000000000..923328b02fb --- /dev/null +++ b/certs/intermediate/trust_anchor/self-copy-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQDCCAiigAwIBAgIBAjANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDDCV3b2xm +U1NMIFRydXN0LUFuY2hvciBUZXN0IFNlbGYtU2lnbmVkMCAXDTI2MTAyMTEyMzM1 +NVoYDzIwNTYxMDIwMTIzMzU1WjAwMS4wLAYDVQQDDCV3b2xmU1NMIFRydXN0LUFu +Y2hvciBUZXN0IFNlbGYtU2lnbmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA0UQi54OPhzLzaD4/mrQuTfsn6SflV5PRm2ENweGp7R3ud5g1+aDrm38J +SvZL7tHERgXqTXsYeUbZbH1HiZ8681NufUTPWKQTf/MU8CUduprQCOmrOXK2z0/j +YRW1KpfOLdLZyZO03QOR2dLKSa3F4JA+V7o9ad+sehAWNiGcyTZbkrfe1sv/M1M1 +nx0mGccB28mPnojLzKE64R06JC0c7/0chyfDCSHSHp/qG9rWx8TYQqcQh6sz3a6k +dJINAMeaSH9dWONRvVI9QvGVOQJiAZi6hmOKVYn1HGwJR9v/aTXonlIdTLDQmIGH +avUA6Ti6SHTLHTGOvzNesDJGQSgQkwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUutrRDd3QT9a6mPVfMxhiI92Sz84w +HwYDVR0jBBgwFoAUutrRDd3QT9a6mPVfMxhiI92Sz84wDQYJKoZIhvcNAQELBQAD +ggEBAIqT5m/1O8O6TsCaAcbTizkeg/2ephuAPVee8bHWhEEWm0WAtinVdARYqIzv +wJq5OamnNdvcG/wr2fs2IsDnudnswipcr3Cm0GpX2nCCkOoo18kPnnCHUyKZOWYP +6u/0RJohX4rm78NnoYU4nM4HU9KyGBfI8rHrtFEjBnRcjipjqYXW69a47v+T+2eU +AYzLUWVxJhYtjUtXEIQkYP6a8J2BHKWjt0peiUUJduiwYmH5ysEp8aP12ukhuHvd +LFnC9BbGWlCMAKVfgcpEWnU0/a6Su92gKI87b71QoLifati8OTNmL7m8B5iyfUZ/ +xrslT9Fon/AxReSWWGif0CYcfdc= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/self-key.pem b/certs/intermediate/trust_anchor/self-key.pem new file mode 100644 index 00000000000..c42276be1ca --- /dev/null +++ b/certs/intermediate/trust_anchor/self-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRRCLng4+HMvNo +Pj+atC5N+yfpJ+VXk9GbYQ3B4antHe53mDX5oOubfwlK9kvu0cRGBepNexh5Rtls +fUeJnzrzU259RM9YpBN/8xTwJR26mtAI6as5crbPT+NhFbUql84t0tnJk7TdA5HZ +0spJrcXgkD5Xuj1p36x6EBY2IZzJNluSt97Wy/8zUzWfHSYZxwHbyY+eiMvMoTrh +HTokLRzv/RyHJ8MJIdIen+ob2tbHxNhCpxCHqzPdrqR0kg0Ax5pIf11Y41G9Uj1C +8ZU5AmIBmLqGY4pVifUcbAlH2/9pNeieUh1MsNCYgYdq9QDpOLpIdMsdMY6/M16w +MkZBKBCTAgMBAAECggEAONjEjpWbiMObgabfSlxF8+MVPW4cDAVYvFfKD/AIZCRs +YkqT5wMC3dPNp3JuwCp1ofv7YR7eUBuh9qm1pDoAHpd8fUUS+mZf8bML7gx3Hfju +KG2pmMqDxJ9UCkyGUVK5rJMJnIumZx+QIFb1orw088LbXKN4CWm8hdAW6I+K8tF2 +IqLgUnb9j7Vf7DTZPJ+v3SRuGCTHe48PGoDfVSu6//q6Me7WeIhSBi/jsvslFFB6 +kwGOTQRkZjKaW+Mwl1OO2XzprzizbClcOOi0/bDPB9+9LNcDqagk3wU2K5kYIZhc +P0i6aAmlpqZYih0wLA9GU939E1i2S6seW7SL5rv+sQKBgQD82DOZ/cPVroFrRjFz +ExTAmbQ0qZl1mG8Bs0Y7grdtQvNb5QmHSGxSTrBInkQXEpS5EhUZTF6+yzj0Ehqz +oIlEeeLWiQKd39GcSqoT4vVpqDkF2c1wAXewXAg/zenoBY/fgbSAsVTZn1Z9G4uV +ZxfLmCWYAXGZ/lSYnwSKCs0KKQKBgQDT4LVu8kN6R2tmCpHOWuAlx/tlj4gjxRAX +TpjOJF7kYZ77P9jyWp3oodnGhRjl0kxzhOPpIaKvj1FYACUepGo/eZLrJqOCFcXg +G1Z5z1TBz9YXoFFcc1XcBcUptRXMv8zkO7RoaYwrbQKd0urKAenpFCDEzDR2+DYZ +wkd3SXBUWwKBgBcq5EeG5gvNSpR+wQcvR2aCbu2oYKRSzFEWkSfgyVfcm5ne+amI +rEw6Y5Kz1b9bd38htgc0ZTYMPvK2OfYdjpZUqMFJmpDGslkkykK6OAxvWpI7q8RI +jiP9us/F2VnmMJGlO9nS5dhu7ijWfkq5QIfqL/+mmjcnA69cWrkfVjxJAoGBAMk/ +cfXsxO0bc0acGXwg0zDvr7j6yTv4jN0meXusDjntA53wOjeQnNZ/DSRD/a8lvhon +indAzI7mmUP190wQrR6XclZLABmqZqYEMXJH3WCt8dPFIiKnJlFzZZ/E8ezl7U6H +60cVod8Jjuerk/nGMZCC3tEuiVLLVk/HOixyo/BrAoGACqCmwGpNk3XuTihD4j18 +ivJZ5OQm9wvbYkOKjWr+Ly1BiG1fiDfNtQRRj/SYAMzkEJ+V9pWt5B9fvnW2TIFz +HWOfvCs14ppHRhn+VDSfcaUeR17u5reX8/4Dw0ITFlGUPwdEdPhbUSfZqq45UU6W +0rhh21oYeJEIXS3p+TT8uxA= +-----END PRIVATE KEY----- diff --git a/certs/intermediate/trust_anchor/v1-root-cert.pem b/certs/intermediate/trust_anchor/v1-root-cert.pem new file mode 100644 index 00000000000..c9131654acb --- /dev/null +++ b/certs/intermediate/trust_anchor/v1-root-cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICvzCCAacCAhAIMA0GCSqGSIb3DQEBCwUAMCUxIzAhBgNVBAMMGndvbGZTU0wg +Q2hhaW4gVGVzdCBWMSBSb290MB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIzMTIzNTk1 +OVowJTEjMCEGA1UEAwwad29sZlNTTCBDaGFpbiBUZXN0IFYxIFJvb3QwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsluAbe0ORlgwunEt+rNn9bqc/HBos +6JK6/AF/ykztPrqgSeLhKioSqiX5682XDUF/Pv8EdA6q6yuq3PGT0Ltvqu0aI1ag +Y1ZX+8pQc24Mvn7lauyv61iqaY2z/Jc8Jf9UtKS/iq0py0kzCAoh6VupN4hZHw2G +/yMO5bWfwatpv6+yCD8e0+OYEKQKrMtULxhCH6KgGP+YMstj6UHuXif7CZbelESq +L6oiUGatUJy1gimBNdhVTsF8rkWsAtUcknPb65fn1zvgBKI+80SfA/AQsaeJQY0/ +hgXCDzw3s9X5cfM/nLCIClRn6JNzB5H28C/JAlRoFup5j/DV9t91SfEvAgMBAAEw +DQYJKoZIhvcNAQELBQADggEBAGM06Ej/u7wdfQQPnn5DN5CdShIwBtAJSyzCn23a +U4h3ON0e0ZPoYLB4foyP3otvLhaWeAzaUo04a3PsEalyPMtWsd/lv21v2Fg3iPmX +4PuxKsN6mrW1zo+bclBRlHEd8zaRubZmL+4mct+eIJJV1Z8li/hBuA1vKdeWequM +3f0e572BobsVEwR5jtXLaO/U6FtLSlqhf6XzqJ4BM/z5reHee+ZVd/MVqJeL0+RG +XRFGFyhwrzbK9UszCNRpQdB6FeI5QaFDaN4PJglC1irapJrNrIh/Uo0C4akk3go5 +wgmhBse4UNq5g6BV7iejLyeKVB3Ze6R6gfbilBBDUT2V5K0= +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/v1-root-leaf-cert.pem b/certs/intermediate/trust_anchor/v1-root-leaf-cert.pem new file mode 100644 index 00000000000..5d5f32c59d5 --- /dev/null +++ b/certs/intermediate/trust_anchor/v1-root-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDRDCCAiygAwIBAgICEAkwDQYJKoZIhvcNAQELBQAwJTEjMCEGA1UEAwwad29s +ZlNTTCBDaGFpbiBUZXN0IFYxIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcNNDkxMjMx +MjM1OTU5WjAqMSgwJgYDVQQDDB93b2xmU1NMIENoYWluIFRlc3QgVjEgUm9vdCBM +ZWFmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwN4nVzoigPHD3g2G +6MNPgyayfk9PC903fpjPMh6lSqB/vkRXp0CPo+LDF18tMmJ9JkUk6MwTWcK1k6SR +aTjqsCddmzLAEaGZiSNUYN5oKOF1kHyM+jLjCwH8trln8OHV+pdVZW61rc5gaPvv +pGNGLtEP/92kE0w1V+D141zX5IySrQlOxmU6GyXLm9gNoHlYWfjvAZTZjhMpZ7xG +Iea0NqZD4QJ2UAl/Q5F/cF95I/ZxNbOfvDLD3Yk3CaMJcsJMNzrhdOmbr6S1Yy/V +8ZkOTWo7tLb5T3USGKiBTHSlv+lY+CP+UtKdbep3xLuVc6hvROgd/IZlVoLMM2ur +42ceCwIDAQABo3kwdzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAdBgNV +HQ4EFgQUaoB8b/XfwO8gpXCbEyHmhS1uXswwOAYDVR0jBDEwL6EppCcwJTEjMCEG +A1UEAwwad29sZlNTTCBDaGFpbiBUZXN0IFYxIFJvb3SCAhAIMA0GCSqGSIb3DQEB +CwUAA4IBAQDB3VDjOqmLduuIIWymNLX2ZievqiDixzjNBMzBsn7BjJsduq9KS8VN +FmO/Ym1wJzbtB7e2g3n5fSG/mir4oCOyG0j+esSagC3GIYM8GSLa16dwQf+JTeQq +tFSxN5cIOJaCllowqL56rvI9K6h3DAYnD+wy4MZy6MOJzWENAtFtmQ53PeaSAp0Z +eyj6Oz8SOU1ZQ9W2o9cFgKWkdKl+w6sw2qM4FAx0OqDgmgOEndq7CJJeDoekwUfp +Z9FLRRHs7e1di2I/lHnvmUvxVmZfjr3x7xVE1qgklVBq1dMC0NpGYlq4yjvBDrh4 +IcjXiPPxoWnTHYwvgYzWn5rGq+ePBVXv +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/wrongca-cert.pem b/certs/intermediate/trust_anchor/wrongca-cert.pem new file mode 100644 index 00000000000..5e72dd90367 --- /dev/null +++ b/certs/intermediate/trust_anchor/wrongca-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgd29s +ZlNTTCBDaGFpbiBUZXN0IFdyb25nIENBIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcN +NDkxMjMxMjM1OTU5WjAmMSQwIgYDVQQDDBt3b2xmU1NMIENoYWluIFRlc3QgV3Jv +bmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDc9Xts5F+9Gr7m +/N0QahL7nm3XId2lRCO4ACqvO03lziWfhAITRShxdDtOZ7neAxamGqoXnFHqrJ7O +EqvOEFvfbUDTXKrxdkDa9eiBgMDSeKXjZFW+OUUYsX7Gx/PivKR3PO87NpPSX3bK +UUuEzMo4dfZuas+nWf46ryM7gV8KNTM2bEVt/AKH4XrlLZMLpv7isgabGteK3TMm +hAVvf94u18DqI38Qomzf7oYOzs3njhpMQmSq5L9dcIu4zuoW5xw9KVQxqrbUD5uQ +k5/jEs0Xc6dDBW4g2d5LIa1wsVoFtGDKDnFqIT79ozRuOYWq7f91FEM3CBRoQumi +ovZ+QNMVAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBTf6lmk6NGYrel1Ew0NXhK+v7oGpDAfBgNVHSMEGDAWgBQl8Gzu +vnTplgMSjPn2HH+ilNGkwzANBgkqhkiG9w0BAQsFAAOCAQEACeCPILf7FxXIH8CE +Ar22uJ7s7U1F0KnOrcgYUAjL9LjSgr2JyOFgwPFDNutO7RH+//l/menfxi4KVek0 +jKoNey6fcS4T5U3zQhG6+hYbH2y4i5FZhS6cbOcOE0jiiJbh9/JELsljSYWNJto8 +KAD7fIqaZEOkWQQVpniopmt/mixM26CgGBzzaALLjJJXgSEMAMOA7d1w+piAv3UM +w2IIOSi6+WolwgRhqrgWX1a/tUEKjPnVTCYn5CfB/BUBhFTJDlMLK6FhF6ilOxcz +ZoyKtYIykzHA8a4HUUPKzqiqGTg2TQklqQwdIfbnaBlzpB6cTAiAaNAzC79MOXrS +qD3NAw== +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/wrongca-int-cert.pem b/certs/intermediate/trust_anchor/wrongca-int-cert.pem new file mode 100644 index 00000000000..b907f1ac7bf --- /dev/null +++ b/certs/intermediate/trust_anchor/wrongca-int-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAfagAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwJjEkMCIGA1UEAwwbd29s +ZlNTTCBDaGFpbiBUZXN0IFdyb25nIENBMB4XDTAwMDEwMTAwMDAwMFoXDTQ5MTIz +MTIzNTk1OVowKjEoMCYGA1UEAwwfd29sZlNTTCBDaGFpbiBUZXN0IFdyb25nIENB +IEludDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKX9EAB9l9T9U65A +zerurYSe977jPQjLCJ6D81QNDL9BCPWftSnjiyQBp7eJ2vLB5OZkT78NCxzT2DLc +yEaF6GlQPih38qtbAjyZgtr+yhTQ3H7xN9gPoeZS6nZdJAM3GOXMm9jJxszBYsDj +5GJ9vz4xjMePe6HDPnT5Wr8ECRDyHX9Tdx4px6s1cHQRsQzA+coU+6RnrHqlYjmD +E+joqoJ/WfEck3bNaF+3pBmwlzD+FfYdDfqPbsWylomkljrWORBIn8tWjrpixIaF +xsVd5bMXhGwcsla0SSUY7FcLhpNvvhWJlVY5tppmON2Ca09A367NdchozmB3LvOU +bY2UCrcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFL0NeSssy7n5Kv+lPlXFe1pULThtMA0GCSqGSIb3DQEBCwUAA4IB +AQCQNrwxWXrsw3hlSz5OfwbSn399k/BknFUBQpcZkCpHvXg1WEGtIkolysO8aSPp +Yr21S9ukVnGzGXF7HrY/1aXW/hFD7El4obok8jYUOOC7F/HKfE0qYL0O/74DtkVD +6tMwTn02PJsPznwmgKnxwB26N5YXzWy70ZiWjbr9o7IJYG0fZELQQy0YyvygK49Y +pghskr7GoJpNE55kzK98HUXA87zVDMHFZ17WarOBoXyqGSsGcQwbsHvnfGt/BrLb +QRbPq0m8O7s2015oy4PWiMgqjiL3wiM4T2GscVuOsD1Vj2Iy2KMbhw4n1ULa8b1J +2f1sy6g9Zpyb3pcC8c9IL7dv +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem b/certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem new file mode 100644 index 00000000000..a7962f206bf --- /dev/null +++ b/certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwKjEoMCYGA1UEAwwfd29s +ZlNTTCBDaGFpbiBUZXN0IFdyb25nIENBIEludDAeFw0wMDAxMDEwMDAwMDBaFw00 +OTEyMzEyMzU5NTlaMC8xLTArBgNVBAMMJHdvbGZTU0wgQ2hhaW4gVGVzdCBXcm9u +ZyBDQSBJbnQgTGVhZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMF6 +VJMg98OODcrfcfhASghxfkZaT9PVEQmsCXcBdcQdkyQ9wZ70ynjjp53b3muRFtit +4LXM3/ageEJTN/Tam/rFQQh4VdRZlOHbTBVal4sPfufx+Rg97APEfAN61WIrh4G9 +aaPxmhaCxL0G3KXUg/7/BbxJl8xlLImUnoO6LeCSuP6MzoMMxya9BeLpPEo4vs5p +MnsgMdVHbb5YuTBtVenqsbW+0wlYocZwRDxEz9y+NiJdwb7aslh8PJDVcXTK0hCh +1Efq4ajjxG8q3gzokbxEWJNRyjzM0yc7cPsFsMd1i7ZkGc8hhxf5n6Om8ECbhOvV +56n2eNSYGfJ5hksC8zsCAwEAAaNgMF4wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8E +BAMCB4AwHQYDVR0OBBYEFBajykWsEqqIXFRNfBoaQPDugZcKMB8GA1UdIwQYMBaA +FL0NeSssy7n5Kv+lPlXFe1pULThtMA0GCSqGSIb3DQEBCwUAA4IBAQA7q34kp+sm +kawfVuaVODiAWG8gQMdBk0zYe8CRyUWS6A/EbHXwB++HafGQSiwtP6ZMJ8Dq12hK +ocxOX6HIG3d03qoaXt1fQqaasCwR0IuLSwEcPjX6EkIRGRLGBYqtfY9CyuAjZmQT +8Dh9moAaTtnwMNrFBpcdX/mUbLtotrG/I8BGZIRlv9pwmfSpiXYWLGnjK4hDcdmn +9mC08SERJqtEdQkzuvG4qc4I/RqxGy2lYHkjy7qkXLdiQRgTB1zlFHkrD8yXlwWF +fSP861GPCM/xBUVq+sOlySbsQCG2b977OZlxVO7Jh4NM7Xd0jQdFmlLHZ1gwle+B +PNnxZ5yxVJfh +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/wrongca-old-int-cert.pem b/certs/intermediate/trust_anchor/wrongca-old-int-cert.pem new file mode 100644 index 00000000000..468a9ef6103 --- /dev/null +++ b/certs/intermediate/trust_anchor/wrongca-old-int-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwJjEkMCIGA1UEAwwbd29s +ZlNTTCBDaGFpbiBUZXN0IFdyb25nIENBMB4XDTEwMDEwMTAwMDAwMFoXDTE5MTIz +MTIzNTk1OVowKjEoMCYGA1UEAwwfd29sZlNTTCBDaGFpbiBUZXN0IFdyb25nIENB +IEludDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKX9EAB9l9T9U65A +zerurYSe977jPQjLCJ6D81QNDL9BCPWftSnjiyQBp7eJ2vLB5OZkT78NCxzT2DLc +yEaF6GlQPih38qtbAjyZgtr+yhTQ3H7xN9gPoeZS6nZdJAM3GOXMm9jJxszBYsDj +5GJ9vz4xjMePe6HDPnT5Wr8ECRDyHX9Tdx4px6s1cHQRsQzA+coU+6RnrHqlYjmD +E+joqoJ/WfEck3bNaF+3pBmwlzD+FfYdDfqPbsWylomkljrWORBIn8tWjrpixIaF +xsVd5bMXhGwcsla0SSUY7FcLhpNvvhWJlVY5tppmON2Ca09A367NdchozmB3LvOU +bY2UCrcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFL0NeSssy7n5Kv+lPlXFe1pULThtMB8GA1UdIwQYMBaAFM7WAysI +FUnUI71Br4FWNbOoZPkYMA0GCSqGSIb3DQEBCwUAA4IBAQAyxCQ4Qiv93ayBF4zO +Env1kLll0ixjyOzt6sbsWoD/Vtm6SmemDioP7b48zR68rp0WeSVOekoiCu+Q+KYP +psBzEGfpQLfziXHJ8VPBURp0ltkqgDDOZyCdBftKRcv25hHhC+AyLUJB47tyxJfX +3Fb77kOZNtwvO5N6bC0/6kffl2yK3jCM5MA9MxFKkJF7wlwYuZbFy8SwcOaEbNpO +t/a1vrqQMGmB0llhSgTSzKYwDGPyI/ivA9FF+ACsenQnxzgCPphWWk0FEMxGbEj/ +71nO3H8ngJ1lp+Q9f2amWd8cr3cgR4BXdih54tBFdiBycKpiFQV7A9OulSE7GiM7 +bNCO +-----END CERTIFICATE----- diff --git a/certs/intermediate/trust_anchor/wrongca-root-cert.pem b/certs/intermediate/trust_anchor/wrongca-root-cert.pem new file mode 100644 index 00000000000..45fa43f9824 --- /dev/null +++ b/certs/intermediate/trust_anchor/wrongca-root-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNTCCAh2gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwKzEpMCcGA1UEAwwgd29s +ZlNTTCBDaGFpbiBUZXN0IFdyb25nIENBIFJvb3QwHhcNMDAwMTAxMDAwMDAwWhcN +NDkxMjMxMjM1OTU5WjArMSkwJwYDVQQDDCB3b2xmU1NMIENoYWluIFRlc3QgV3Jv +bmcgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKStDm7T +Jpr7MTRvTqcSHgAWLnaCnDLHm8UzX0lbdadfToLtOJ8IoRiauofU6sQoID5dk35i +JyyKUl/8ONLVB06wIX2HUgoA4uhyo5xXSyUiPQKh9zifctewnDzij1mXCo9iwKES +meyBqPcftv6GjJHF/ywDT2/Da6YmJI9DGGWS0kP/HCJ0TNM5Nw0aghdh20cxznqc +4mC46/mIlC0TLIdagYwNVTBQUF/lDaCzZydfOZ6exg9ARjeZi7Wpiob1q2kShP80 +x/0EfVa/ydLLTW3bWOCXGsB0IVErTUu1wRidcyU5nIR4FF2o49WXKmy5FjRha3bO +yhZL8KADlYTNOwUCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwHQYDVR0OBBYEFCXwbO6+dOmWAxKM+fYcf6KU0aTDMB8GA1UdIwQYMBaA +FCXwbO6+dOmWAxKM+fYcf6KU0aTDMA0GCSqGSIb3DQEBCwUAA4IBAQApJzCFu2Ds +084NjFnyv8yNNIpPPrcNDgezCh9yULCUXmIMVtYy8gQU+uW66r0StdlDXB0caI2B +1nL7O1BQFCTYnYPTGmURaCec8hShw51fvnH7LMkJWL8spHFH91F0Z078wLyQmCTE +kavU0E1e3kueNG0vLyW8wwYiFar7OOmBvxEcsTgFAARbX/slVoGyDQhqOmLqGkGf +UVEbkXcy/55OQhaOAcrqzwa63qfJl8AMDNdPzlz5BWMnuxRUrVtTkDWzYrNhAPR/ +DvE9MruGdNrFarRdCwHVyRMRBRKoZGVDYnEzHnBjl0ky/BkcueNYO6+KhdSvVw9w +WTtAhrFnupPH +-----END CERTIFICATE----- From aeec8e7d5b9113899422bb08e60a0df7642e92fe Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:32:30 +0000 Subject: [PATCH 20/31] Check CA key sizes in CheckCAKeySize by modulus and curve size AddCA() checks the key of a CA against the cert manager's minimum key sizes for each key type. Move that switch into a function of its own, CheckCAKeySize(). The check compared the DER length of the public key with the minimums, which are in bytes of key size. For ECC that length is of the whole SubjectPublicKeyInfo, 91 bytes for P-256, so no ECC minimum up to 521 bits was enforced. For RSA it is the RSAPublicKey DER, so keys about 100 bits short of the minimum passed. Compare the modulus size for RSA and the curve size for ECC. OpenSSL has no CA-only minimum; its auth level checks the real key strength (EVP_PKEY_get_security_bits()), as this now does for the wolfSSL minimums. This changes AddCA(): a CA whose curve is below the minimum ECC key size, such as P-192 with the default of 224 bits, no longer loads. test_wolfSSL_CertManagerCAKeySize loads CAs into an SSL_CTX that verifies peers: a CA with a 2048-bit modulus loads with a minimum of 2048 bits but not of 2056, and with a minimum of 384 bits a P-256 CA does not load and a P-384 CA does. --- src/ssl_certman.c | 288 ++++++++++++++++++++++++--------------- tests/api/test_certman.c | 57 ++++++++ tests/api/test_certman.h | 2 + wolfssl/internal.h | 2 + 4 files changed, 236 insertions(+), 113 deletions(-) diff --git a/src/ssl_certman.c b/src/ssl_certman.c index 0946251320b..e1942278e07 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -3221,6 +3221,179 @@ int AddSigner(WOLFSSL_CERT_MANAGER* cm, Signer *s) return 0; } +#ifndef NO_RSA +/* Returns the size in bytes of the modulus of an RSA public key, 0 when it + * does not decode. */ +static word32 CAKeyRsaSize(const byte* pubKey, word32 pubKeySz) +{ + const byte* n = NULL; + word32 nSz = 0; + word32 idx = 0; + + if (wc_RsaPublicKeyDecode_ex(pubKey, &idx, pubKeySz, &n, &nSz, NULL, + NULL) != 0) { + return 0; + } + while (nSz > 0 && *n == 0) { + n++; + nSz--; + } + return nSz; +} +#endif /* !NO_RSA */ + +#ifdef HAVE_ECC +/* Returns the size in bytes of the curve of an ECC public key, 0 when it + * does not decode. */ +static word32 CAKeyEccSize(const byte* pubKey, word32 pubKeySz, void* heap) +{ + word32 sz = 0; + word32 idx = 0; + WC_DECLARE_VAR(key, ecc_key, 1, 0); + + WC_ALLOC_VAR_EX(key, ecc_key, 1, heap, DYNAMIC_TYPE_ECC, return 0); + if (wc_ecc_init_ex(key, heap, INVALID_DEVID) == 0) { + if (wc_EccPublicKeyDecode(pubKey, &idx, key, pubKeySz) == 0) { + sz = (word32)wc_ecc_size(key); + } + wc_ecc_free(key); + } + WC_FREE_VAR_EX(key, heap, DYNAMIC_TYPE_ECC); + return sz; +} +#endif /* HAVE_ECC */ + +/* Checks a CA's key against the cert manager's minimum key sizes, in bytes: + * of the modulus for RSA, of the curve for ECC. pubKey is the key as a + * DecodedCert stores it. + * Returns 0, or a key size error when the key is too small. */ +int CheckCAKeySize(WOLFSSL_CERT_MANAGER* cm, word32 keyOID, + const byte* pubKey, word32 pubKeySize) +{ + int ret = 0; + word32 keySz; + + (void)pubKey; + (void)pubKeySize; + (void)keySz; + + switch (keyOID) { + #ifndef NO_RSA + #ifdef WC_RSA_PSS + case RSAPSSk: + #endif + case RSAk: + keySz = CAKeyRsaSize(pubKey, pubKeySize); + if (cm->minRsaKeySz < 0 || keySz < (word32)cm->minRsaKeySz) { + ret = RSA_KEY_SIZE_E; + WOLFSSL_MSG_CERT_LOG("\tCA RSA key size error"); + WOLFSSL_MSG_CERT_EX("\tCA RSA key size = %d; " + "minRsaKeySz = %d", keySz, cm->minRsaKeySz); + } + break; + #endif /* !NO_RSA */ + #ifdef HAVE_ECC + case ECDSAk: + keySz = CAKeyEccSize(pubKey, pubKeySize, cm->heap); + if (cm->minEccKeySz < 0 || keySz < (word32)cm->minEccKeySz) { + ret = ECC_KEY_SIZE_E; + WOLFSSL_MSG_CERT_LOG("\tCA ECC key size error"); + WOLFSSL_MSG_CERT_EX("\tCA ECC key size = %d; " + "minEccKeySz = %d", keySz, cm->minEccKeySz); + } + break; + #endif /* HAVE_ECC */ + #ifdef HAVE_ED25519 + case ED25519k: + if (cm->minEccKeySz < 0 || + ED25519_KEY_SIZE < (word16)cm->minEccKeySz) { + ret = ECC_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ECC key size error"); + } + break; + #endif /* HAVE_ED25519 */ + #ifdef HAVE_ED448 + case ED448k: + if (cm->minEccKeySz < 0 || + ED448_KEY_SIZE < (word16)cm->minEccKeySz) { + ret = ECC_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ECC key size error"); + } + break; + #endif /* HAVE_ED448 */ + #if defined(HAVE_FALCON) + case FALCON_LEVEL1k: + if (cm->minFalconKeySz < 0 || + FALCON_LEVEL1_KEY_SIZE < (word16)cm->minFalconKeySz) { + ret = FALCON_KEY_SIZE_E; + WOLFSSL_MSG("\tCA Falcon level 1 key size error"); + } + break; + case FALCON_LEVEL5k: + if (cm->minFalconKeySz < 0 || + FALCON_LEVEL5_KEY_SIZE < (word16)cm->minFalconKeySz) { + ret = FALCON_KEY_SIZE_E; + WOLFSSL_MSG("\tCA Falcon level 5 key size error"); + } + break; + #endif /* HAVE_FALCON */ + #if defined(WOLFSSL_HAVE_MLDSA) + #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT + case DILITHIUM_LEVEL2k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_44_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 2 key size error"); + } + break; + case DILITHIUM_LEVEL3k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_65_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 3 key size error"); + } + break; + case DILITHIUM_LEVEL5k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_87_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 5 key size error"); + } + break; + #endif /* WOLFSSL_MLDSA_FIPS204_DRAFT */ + case ML_DSA_44k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_44_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 2 key size error"); + } + break; + case ML_DSA_65k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_65_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 3 key size error"); + } + break; + case ML_DSA_87k: + if (cm->minMlDsaKeySz < 0 || + WC_MLDSA_87_KEY_SIZE < (word16)cm->minMlDsaKeySz) { + ret = MLDSA_KEY_SIZE_E; + WOLFSSL_MSG("\tCA ML-DSA level 5 key size error"); + } + break; + #endif /* WOLFSSL_HAVE_MLDSA */ + + default: + WOLFSSL_MSG("\tNo key size check done on CA"); + break; /* no size check if key type is not in switch */ + } + + (void)cm; + (void)pubKeySize; + return ret; +} + /* owns der, internal now uses too */ /* type flag ids from user or from chain received during verify don't allow chain ones to be added w/o isCA extension */ @@ -3294,119 +3467,8 @@ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify) /* check CA key size */ if (verify && (ret == 0 )) { - switch (cert->keyOID) { - #ifndef NO_RSA - #ifdef WC_RSA_PSS - case RSAPSSk: - #endif - case RSAk: - if (cm->minRsaKeySz < 0 || - cert->pubKeySize < (word16)cm->minRsaKeySz) { - ret = RSA_KEY_SIZE_E; - WOLFSSL_MSG_CERT_LOG("\tCA RSA key size error"); - WOLFSSL_MSG_CERT_EX("\tCA RSA pubKeySize = %d; " - "minRsaKeySz = %d", - cert->pubKeySize, cm->minRsaKeySz); - } - break; - #endif /* !NO_RSA */ - #ifdef HAVE_ECC - case ECDSAk: - if (cm->minEccKeySz < 0 || - cert->pubKeySize < (word16)cm->minEccKeySz) { - ret = ECC_KEY_SIZE_E; - WOLFSSL_MSG_CERT_LOG("\tCA ECC key size error"); - WOLFSSL_MSG_CERT_EX("\tCA ECC pubKeySize = %d; " - "minEccKeySz = %d", - cert->pubKeySize, cm->minEccKeySz); - } - break; - #endif /* HAVE_ECC */ - #ifdef HAVE_ED25519 - case ED25519k: - if (cm->minEccKeySz < 0 || - ED25519_KEY_SIZE < (word16)cm->minEccKeySz) { - ret = ECC_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ECC key size error"); - } - break; - #endif /* HAVE_ED25519 */ - #ifdef HAVE_ED448 - case ED448k: - if (cm->minEccKeySz < 0 || - ED448_KEY_SIZE < (word16)cm->minEccKeySz) { - ret = ECC_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ECC key size error"); - } - break; - #endif /* HAVE_ED448 */ - #if defined(HAVE_FALCON) - case FALCON_LEVEL1k: - if (cm->minFalconKeySz < 0 || - FALCON_LEVEL1_KEY_SIZE < (word16)cm->minFalconKeySz) { - ret = FALCON_KEY_SIZE_E; - WOLFSSL_MSG("\tCA Falcon level 1 key size error"); - } - break; - case FALCON_LEVEL5k: - if (cm->minFalconKeySz < 0 || - FALCON_LEVEL5_KEY_SIZE < (word16)cm->minFalconKeySz) { - ret = FALCON_KEY_SIZE_E; - WOLFSSL_MSG("\tCA Falcon level 5 key size error"); - } - break; - #endif /* HAVE_FALCON */ - #if defined(WOLFSSL_HAVE_MLDSA) - #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT - case DILITHIUM_LEVEL2k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_44_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 2 key size error"); - } - break; - case DILITHIUM_LEVEL3k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_65_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 3 key size error"); - } - break; - case DILITHIUM_LEVEL5k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_87_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 5 key size error"); - } - break; - #endif /* WOLFSSL_MLDSA_FIPS204_DRAFT */ - case ML_DSA_44k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_44_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 2 key size error"); - } - break; - case ML_DSA_65k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_65_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 3 key size error"); - } - break; - case ML_DSA_87k: - if (cm->minMlDsaKeySz < 0 || - WC_MLDSA_87_KEY_SIZE < (word16)cm->minMlDsaKeySz) { - ret = MLDSA_KEY_SIZE_E; - WOLFSSL_MSG("\tCA ML-DSA level 5 key size error"); - } - break; - #endif /* WOLFSSL_HAVE_MLDSA */ - - default: - WOLFSSL_MSG("\tNo key size check done on CA"); - break; /* no size check if key type is not in switch */ - } + ret = CheckCAKeySize(cm, cert->keyOID, cert->publicKey, + cert->pubKeySize); } if (ret == 0 && cert->isCA == 0 && type != WOLFSSL_USER_CA && diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index 81c8b3573f7..c0d68f1140f 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -626,6 +626,63 @@ int test_wolfSSL_CertManagerLoadCABufferType(void) return EXPECT_RESULT(); } +/* AddCA() checks the RSA modulus and ECC curve size of a CA against the + * minimum key sizes when the CTX verifies peers. */ +int test_wolfSSL_CertManagerCAKeySize(void) +{ + EXPECT_DECLS; +#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX* ctx = NULL; + byte* buf = NULL; + size_t sz = 0; + +#if !defined(NO_RSA) && !defined(NO_SHA256) + /* A 2048-bit modulus. */ + ExpectIntEQ(load_file("./certs/ca-cert.der", &buf, &sz), 0); + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_CTX_SetMinRsaKey_Sz(ctx, 2048), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx, buf, (long)sz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + wolfSSL_CTX_free(ctx); + ctx = NULL; + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_CTX_SetMinRsaKey_Sz(ctx, 2056), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx, buf, (long)sz, + WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(RSA_KEY_SIZE_E)); + wolfSSL_CTX_free(ctx); + ctx = NULL; + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + buf = NULL; +#endif +#if defined(HAVE_ECC) && !defined(NO_SHA256) && defined(WOLFSSL_SHA384) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + /* P-256 is below a minimum of 384 bits, P-384 is not. */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); + wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_CTX_SetMinEccKey_Sz(ctx, 384), WOLFSSL_SUCCESS); + ExpectIntEQ(load_file("./certs/ca-ecc-cert.der", &buf, &sz), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx, buf, (long)sz, + WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ECC_KEY_SIZE_E)); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + buf = NULL; + ExpectIntEQ(load_file("./certs/ca-ecc384-cert.der", &buf, &sz), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx, buf, (long)sz, + WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + buf = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; +#endif + (void)ctx; + (void)buf; + (void)sz; +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_CertManagerGetCerts(void) { EXPECT_DECLS; diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h index feb9cb687fa..f6a0bcf1979 100644 --- a/tests/api/test_certman.h +++ b/tests/api/test_certman.h @@ -29,6 +29,7 @@ int test_certman_decision_coverage(void); int test_wolfSSL_CertManagerLoadCABuffer(void); int test_wolfSSL_CertManagerLoadCABuffer_ex(void); int test_wolfSSL_CertManagerLoadCABufferType(void); +int test_wolfSSL_CertManagerCAKeySize(void); int test_wolfSSL_CertManagerGetCerts(void); int test_wolfSSL_CertManagerSetVerify(void); int test_wolfSSL_CertManagerNameConstraint(void); @@ -75,6 +76,7 @@ int test_wolfSSL_trust_peer_cert_dates(void); TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerLoadCABuffer), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerLoadCABuffer_ex), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerLoadCABufferType), \ + TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCAKeySize), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerGetCerts), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerSetVerify), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 29307857ef0..da7ef319412 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5037,6 +5037,8 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_LOCAL int AddSigner(WOLFSSL_CERT_MANAGER* cm, Signer *s); WOLFSSL_LOCAL int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify); + WOLFSSL_LOCAL int CheckCAKeySize(WOLFSSL_CERT_MANAGER* cm, word32 keyOID, + const byte* pubKey, word32 pubKeySize); WOLFSSL_LOCAL int RemoveCA(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type); WOLFSSL_LOCAL int SetCAType(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type); WOLFSSL_LOCAL From 1f056912c90746211005667c480acee154a3a19c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:33:12 +0000 Subject: [PATCH 21/31] Build the X509_verify_cert chain as OpenSSL does, failing closed X509_verify_cert() verified each cert with the cert manager and picked issuers through lookups, retries and alternate chains on top of it. The cert manager finds signers by name and key id only, so it accepted chains OpenSSL rejects and rejected chains OpenSSL accepts. Build the chain from X509 objects, as OpenSSL 3.0's build_chain() does, and fail closed where OpenSSL would try another candidate: - The issuer is OpenSSL's pick: among the certs whose names and key ids match and whose key type fits the signature, the first one valid at the verification time, else the one that expires last. Its key is not checked when picked; a signature it did not make is reported later (error 7). Trusted certs are searched in the order they were added, as OpenSSL's store keeps them (the new store->added). Failing closed, a cert without its DER, made in memory, is never picked, trusted or not, so every cert on the chain has its DER and is checked. - Without X509_V_FLAG_TRUSTED_FIRST the untrusted certs are searched first, in their order, else the trusted ones. X509_STORE_CTX_init() sets the flag, as OpenSSL's default param does. Above a trusted cert only trusted certs issue. A trusted stack replaces the store's trusted certs and its cert manager CAs. - A cert is self-signed by its names and key ids. It is trusted when it was found among the trusted certs, or when it is self-signed and is the trusted cert picked as its own issuer: a copy is a mimic. - The first issuer found is final: no other candidate is tried when a later check fails. Where OpenSSL would take an alternate chain, wolfSSL takes it only when its trusted cert is the same cert as the untrusted one above; otherwise the missing issuer is final, also when the verify callback accepts it. - A trusted self-signed cert ends the chain, and any trusted cert with X509_V_FLAG_PARTIAL_CHAIN. As OpenSSL's last resort, a trusted leaf ends a partial chain only when no issuer is found, also above an untrusted self-signed cert. A trusted issuer already on the chain gives X509_V_ERR_CERT_CHAIN_TOO_LONG, as OpenSSL takes it again until the chain is too long. An untrusted self-signed top gives 18 or 19, a missing issuer 2 or 20. - X509_V_FLAG_PARTIAL_CHAIN is read from the ctx param only, which init inherits from the store and X509_STORE_CTX_set0_param() replaces. - The CAs of the cert manager, as in an SSL_CTX's store, are trusted certs searched after the others: only those the application loaded, not CAs that TLS cached from a peer's chain or temporary ones. Such a CA goes on the chain from its DER and the chain goes on to its issuer there, so its path length, name constraints, CA flag, dates and revocation by its root are checked. As in wolfSSL's handshake, a CA without an issuer there ends the chain. Without its DER, or when its key did not sign the cert, the cert has no issuer. A cert that breaks the name constraints of such a CA gets 47 or 48 on the chain. The comment where settings.h sets WOLFSSL_SIGNER_DER_CERT names this use. - The depth comes from the ctx param, as in OpenSSL; the leaf and the trust anchor do not count, also a CA of the cert manager that ends the chain, as the anchor of a partial chain in OpenSSL. A negative depth, unset included, rejects every chain. As OpenSSL, the verify callback is told of a chain too long; failing closed, the error is final. X509_STORE_CTX_set_depth() sets the param depth. The depth member of X509_STORE_CTX is removed, as OpenSSL's X509_STORE_CTX is opaque: only the setter sets the depth, and the ctx of the TLS verify callback no longer carries it; test.h's callback uses error_depth instead. This is an API change for code that read or wrote the member. So is the removal of the owned member, which only the old verification filled. - No temporary CAs are loaded into the cert manager, so none are unloaded after the verification. xmlsec sets the verify depth on a param and gives the param to the X509_STORE_CTX: add X509_VERIFY_PARAM_set_depth and X509_STORE_CTX_set0_param. The param depth is the maximum number of intermediate CAs, -1 when unset. X509_VERIFY_PARAM_set1 and inherit copy it only when set, so a store depth reaches the context at init. X509_STORE_CTX_init() sets the param depth from the store, or to 100 when the store has none, as OpenSSL does. X509_STORE_CTX_set0_param frees the context's param and takes the new one, replacing what came from the store. A NULL param is ignored, and so is the context's own param, which OpenSSL frees and keeps a dangling pointer to. TLS verification does not use the param depth. The context allocates and frees its param as X509_VERIFY_PARAM_new() and X509_VERIFY_PARAM_free() do, without the context's heap, as OpenSSL's context uses X509_VERIFY_PARAM_new(). So a param of X509_VERIFY_PARAM_new() given to set0_param is freed with the heap it was allocated with, also with static memory. Then, as OpenSSL, each error reported at its cert and depth to the verify callback: - Every cert above the leaf is a CA as X509_check_ca() decides (79). Without basicConstraints only the top cert can be one, and not with X509_V_FLAG_X509_STRICT. WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE still lets an untrusted intermediate without CA:TRUE through. - Path lengths as check_extensions(): every non-self-issued cert above the leaf counts, CA or not, and 25 is reported at the CA whose pathLenConstraint is exceeded. - The minimum CA key size of every issuer, the anchor included (67). - Host and IP of the leaf. - Revocation, below. - Top down as internal_verify(): the issuer's keyUsage allows keyCertSign (32), the signature (7) and the dates of each cert at the verification time: the param time with X509_V_FLAG_USE_CHECK_TIME, none with X509_V_FLAG_NO_CHECK_TIME, else now. As X509_cmp_time(), a cert has expired at its notAfter. Then the callback is called for the cert with ok set. - Name constraints as check_name_constraints(): the names of each cert, but a self-issued one above the leaf, against every cert above it, the anchor included (47, 48, 51). The CN of the leaf, CA or not, is a DNS name when it has no dNSName SAN. CheckCertNameConstraints() runs wolfCrypt's checker on the DER of a CA and of a cert. Revocation: - CRLs are checked when the cert manager has CRL checking on, or when X509_V_FLAG_CRL_CHECK(_ALL) is on the ctx param, as OpenSSL reads it. X509_STORE_set_flags() puts them on the store's param. Without HAVE_CRL the flags are not ignored: no CRL is found, so each cert they check gives 3, as in OpenSSL without a CRL. - As OpenSSL, the loaded CRLs are checked also for a cert without a CRL distribution point: WOLFSSL_CRL_ALLOW_MISSING_CDP now skips the check only in the handshake (CheckCertCRL_ex()). - The CRL of a cert must be signed by the cert's issuer on the chain: CheckCertCRLFromCm() takes that issuer and, as crl_akid_check(), skips a CRL whose authority key id is not the issuer's subject key id. A CRL verified when it was loaded, after its CA, keeps no signature: it is used when the CA that verified it has the issuer's key. - As in OpenSSL, a self-issued top cert has its own CRLs: as the leaf, or with X509_V_FLAG_CRL_CHECK_ALL, a trust anchor is checked against them too. Another top cert has no CRL without its issuer, so it gives 3 where it is checked. The CRL checks of the cert manager alone leave the trust anchor out, self-issued or not, as in TLS. - As OpenSSL's get_crl_sk(), one CRL is checked per cert, among those of the issuer's name and key: a current one over one out of date, then the latest thisUpdate, the first on a tie. An older CRL of another key, or a revocation that only an older CRL lists, is not looked at. - A CRL that does not verify gives X509_V_ERR_CRL_SIGNATURE_FAILURE (8), final also when the callback accepts it, as its entries are not verified. A missing CRL gives 3. - As OpenSSL's check_crl_time(), a CRL is current from its thisUpdate until its nextUpdate, if any, at the time the cert dates are checked at: the param time with X509_V_FLAG_USE_CHECK_TIME, none with X509_V_FLAG_NO_CHECK_TIME, else now. CheckCertCRLFromCm() takes that time; the handshake keeps its nextUpdate check at the current time. Without a current CRL, one not yet valid gives 11, else an expired one 12, at the cert's depth: OpenSSL takes the CRL of the latest thisUpdate. Once accepted, its entries are still checked. As in OpenSSL, the dates of a CRL are checked before its signature. Loading a CRL into an X509_STORE does not check its dates. - As in OpenSSL, the CRLs of X509_STORE_CTX_set0_crls() are used over the store's, also when they are out of date: the store's only when none of them is of the cert's issuer. - An issuer whose keyUsage lacks cRLSign gives 35, before the CRL dates as OpenSSL's check_crl(); once accepted, the CRL is used. As OpenSSL, an internal error is not a verify error: the verify callback is not asked, so it cannot accept it, and X509_verify_cert() fails with X509_V_ERR_OUT_OF_MEM when memory ran out, else X509_V_ERR_UNSPECIFIED. Such are a failed allocation, a cert that does not decode again, a mutex error, and any result of the CRL checks but theirs. An error of the signature check, by the cert manager or with the key of an X509, other than a verification result is one: the cert decoded before. A bad signature, or a signature algorithm not compiled in, stays a verification result. VerifyCRL_Signature() returns MEMORY_E instead of ASN_CRL_CONFIRM_E when memory ran out. After a failure the X509_STORE_CTX error is never X509_V_OK, also for a leaf without its DER (-1). Add the X509_V_ERR values used here. The store follows OpenSSL too: - X509_STORE_add_cert() of a store of its own takes a self-signed cert that is expired or not yet valid, also with X509_V_FLAG_NO_CHECK_TIME. It stays out of the cert manager, so TLS never trusts it, and is dropped when the store goes to an SSL_CTX. X509_STORE_get0_objects(), X509_STORE_get1_certs(), X509_STORE_CTX_get_by_subject() and X509_STORE_CTX_get1_issuer() find it. Adding a cert already there is a no-op. - X509_STORE_CTX_set_flags() sets the param flags. X509_STORE_set_flags() sets X509_V_FLAG_X509_STRICT there too, and the CRL flags, but not X509_V_FLAG_NO_CHECK_TIME: on the store of an SSL_CTX it would let CAs out of date into the cert manager, which TLS trusts. The flags of a connection that are copied to the store of its SSL_CTX leave X509_STRICT out. SSL_get0_verified_chain() returns the chain that X509_verify_cert() builds, from the peer's cert to the trust anchor, as OpenSSL returns the chain its handshake verified, instead of the peer's certs followed by the CA chain. After a full handshake that verified the peer (verification on and no error, also none that a verify callback accepted), the dates of a CA the application loaded into the cert manager, and whether it is a CA that may sign certs, do not fail it where that CA, the same cert by its DER, verifies the cert below it: the handshake trusts such a CA without these checks. So a CA that expired after it was loaded, or was loaded with its date errors allowed, still gives the chain, also below a root of the cert manager. Every other check applies. A cert that only a store of its own trusts, outside its cert manager, gets every check, and so does every cert after a resumed handshake, which verifies no cert, or after one that did not verify the peer. Without WOLFSSL_SIGNER_DER_CERT a CA of the cert manager cannot go on the chain, so there is none for a peer that only such a CA verifies. As OpenSSL keeps the chain of its handshake, the chain is built once per peer cert and kept: later calls return the same stack without verifying again. A new peer chain and wolfSSL_clear() drop it, and it is built again when its first cert is not the peer's. Before, each call verified again and freed the stack the previous one returned. Upstream tests changed, each to what OpenSSL 3.0.13 gives: - X509_STORE_check_time: CRL_CHECK on the ctx param with no CRL gives 3, so it now passes certs/crl/crl.pem, which does not list the cert; without HAVE_CRL it expects 3. - X509_STORE_CTX_ex10: a store PARTIAL_CHAIN flag set after init reaches the ctx only at the next init; before that, 2. - partial_chain_cb_reject: the partial chain ends at the first trusted cert, int2, which the callback rejects. - untrusted_inter_depth_trusted_stack: init drops the ctx flag, so depth 0 is too long again (22). - untrusted_inter tests: a tampered intermediate found first is the issuer and fails the chain (7) instead of wolfSSL retrying the genuine one; with only the tampered cert as trusted stack, 2 at depth 1. The comments say so; the function names are kept. - selfsigned_anchor_no_keycertsign: a self-signed anchor without keyCertSign fails with 79 at depth 1. ca_no_keycertsign also checks 79 at depth 1. - X509_STORE_untrusted: an untrusted root without a loaded CA gives 19, as "openssl verify" does, not 20. - X509_STORE_InvalidCa and _CtxCallback: the callback sees 79, then 32, which is left on the ctx. New tests cover the param flags and depth as xmlsec sets them, the X509_STRICT of a connection not reaching the store, in-memory certs without DER, CRL dates at a verification time, CRLs loaded after their CA, internal errors not reaching the callback, the verified chain of a connection and its caching, and the CRL checks of an SSL_CTX's cert manager. Tests that need WOLFSSL_SIGNER_DER_CERT expect no verified chain or a failure without it. Known differences from OpenSSL 3.0.13: - Failing closed, no alternate chain through another trusted cert: some chains that OpenSSL verifies are rejected. - A CRL that does not verify stays an error once accepted; OpenSSL then checks its entries. So does a chain too long; OpenSSL then reports the missing issuer. - A CA of an SSL_CTX without its issuer there ends the chain, as in the handshake; OpenSSL gives 2. - A CA:TRUE leaf whose CN is not a hostname, below DNS constraints, is rejected, as a CA:FALSE one already was. The TLS path does not check the CN of a CA leaf; it is not changed here. - The serial in an authority key id is not compared, as before, so some chains that OpenSSL rejects verify. - CA key sizes: OpenSSL has no CA-only minimum. - SSL_get0_verified_chain() accepts the dates and CA flag of a CA of the cert manager after a full handshake, which trusts it without them; OpenSSL fails such a handshake. After a resumed handshake it builds the chain from the session's certs and checks all of it; OpenSSL has none. - A CRL is picked by its name, authority key id and dates; OpenSSL also scores its scope, critical extensions and delta CRLs. - wolfSSL cannot load a CRL without nextUpdate; OpenSSL takes it as never expired, as the date check here would. - Running out of memory inside X509_check_host(), X509_check_ip_asc() or the key size checks of CheckCAKeySize() (WOLFSSL_SMALL_STACK) is taken as a mismatch or a small key, and inside the math of a signature check, as with sp_int, as a bad signature, as OpenSSL does. - After a failure X509_STORE_CTX_get_current_cert() is the leaf: wolfSSL keeps the cert to verify there. The verify callback sees the cert of each error. - X509_STORE_set_flags() leaves X509_V_FLAG_NO_CHECK_TIME out, above; X509_VERIFY_PARAM_set_flags() on X509_STORE_get0_param() sets it. - With X509_STORE_add_crl() the cert manager checks every cert below the trust anchor, also without X509_V_FLAG_CRL_CHECK, as before. - A verification time of 0 is the current time, as in wolfCrypt; OpenSSL takes it as 1970. - A CRL date that does not parse, as one with a time zone offset, makes the CRL not yet valid (11); OpenSSL gives 15 or 16. --- doc/dox_comments/header_files/ssl.h | 5 + src/crl.c | 277 +++- src/internal.c | 11 +- src/ssl.c | 13 +- src/ssl_api_cert.c | 101 +- src/ssl_certman.c | 9 - src/x509.c | 30 +- src/x509_str.c | 1973 ++++++++++++++++----------- tests/api/test_ossl_x509_str.c | 1001 ++++++++++++-- tests/api/test_ossl_x509_str.h | 13 + tests/api/test_ssl_cert.c | 436 +++++- tests/api/test_ssl_cert.h | 3 + tests/api/test_tls.c | 42 + tests/api/test_tls.h | 2 + tests/unit-mcdc/test_crl_whitebox.c | 19 +- wolfcrypt/src/asn.c | 74 +- wolfssl/crl.h | 8 +- wolfssl/openssl/ssl.h | 2 + wolfssl/ssl.h | 22 +- wolfssl/test.h | 2 +- wolfssl/wolfcrypt/asn.h | 12 + wolfssl/wolfcrypt/settings.h | 7 +- 22 files changed, 3010 insertions(+), 1052 deletions(-) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 9be9564c897..bac64e65508 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -5945,6 +5945,11 @@ int wolfSSL_X509_get_signature(WOLFSSL_X509* x509, unsigned char* buf, int* bufS \ingroup CertsKeys \brief This function adds a certificate to the WOLFSSL_X509_STRE structure. + As in OpenSSL, a store made with wolfSSL_X509_STORE_new() also takes a + self-signed certificate that is not valid now. + wolfSSL_X509_verify_cert() checks its dates at the verification time. + It is not trusted for TLS, and it is dropped when the store is given to + an SSL_CTX with wolfSSL_CTX_set_cert_store(). \return SSL_SUCCESS If certificate is added successfully. \return SSL_FATAL_ERROR: If certificate is not added successfully. diff --git a/src/crl.c b/src/crl.c index d5350ab608c..bf25923bb3c 100644 --- a/src/crl.c +++ b/src/crl.c @@ -485,7 +485,7 @@ static int FindRevokedSerial(RevokedCert* rc, byte* serial, int serialSz, /* cacheResult should only be set when cm is the owning cm of crl. A cached * result is not valid for other cms because they can trust different CAs. */ static int VerifyCRLE(const WOLFSSL_CRL* crl, CRL_Entry* crle, - WOLFSSL_CERT_MANAGER* cm, int cacheResult) + WOLFSSL_CERT_MANAGER* cm, int cacheResult, Signer* issuer) { Signer* ca = NULL; SignatureCtx sigCtx; @@ -510,6 +510,27 @@ static int VerifyCRLE(const WOLFSSL_CRL* crl, CRL_Entry* crle, #else /* NO_SKID */ ca = GetCA(cm, crle->issuerHash); #endif /* NO_SKID */ + /* Verified when loaded, it has no signature left: the CA that verified + * it must have the key of the chain issuer */ + if (issuer != NULL && crle->toBeSigned == NULL) { + #ifndef IGNORE_KEY_EXTENSIONS + if ((issuer->keyUsage & KEYUSE_CRL_SIGN) == 0) { + return ASN_CRL_NO_SIGNER_E; + } + #endif + if (crle->verified <= 0 || ca == NULL || ca->publicKey == NULL || + ca->pubKeySize != issuer->pubKeySize || + XMEMCMP(ca->publicKey, issuer->publicKey, + issuer->pubKeySize) != 0) { + return ASN_CRL_CONFIRM_E; + } + return 0; + } + /* As OpenSSL, the issuer on the chain must have signed it */ + if (issuer != NULL) { + ca = issuer; + cacheResult = 0; + } if (ca == NULL) { WOLFSSL_MSG("Did NOT find CRL issuer CA"); return ASN_CRL_NO_SIGNER_E; @@ -536,31 +557,216 @@ static int VerifyCRLE(const WOLFSSL_CRL* crl, CRL_Entry* crle, return ret; } +#if defined(OPENSSL_EXTRA) && !defined(NO_SKID) && !defined(NO_ASN) +/* Returns 1 when crle is the CRL of another key than issuer's: as OpenSSL's + * X509_check_akid(), its authority key id is not the issuer's subject key + * id, both being known. A zero subjectKeyIdHash is not known. */ +static int CrlOfOtherKey(const CRL_Entry* crle, const Signer* issuer) +{ + byte known = 0; + int i; + + for (i = 0; i < KEYID_SIZE; i++) { + known |= issuer->subjectKeyIdHash[i]; + } + return crle->extAuthKeyIdSet && known != 0 && + XMEMCMP(crle->extAuthKeyId, issuer->subjectKeyIdHash, + KEYID_SIZE) != 0; +} +#endif + +#if !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_CRL_DATE_CHECK) +/* As XVALIDATE_DATE, at checkTime unless it is 0 */ +static int CrlValidDate(const byte* date, byte format, int dateType, + time_t checkTime) +{ +#ifdef USE_WOLF_VALIDDATE + if (checkTime != 0) { + return wc_ValidateDateWithTime(date, format, dateType, checkTime, + MAX_DATE_SIZE); + } +#else + (void)checkTime; +#endif + return XVALIDATE_DATE(date, format, dateType, MAX_DATE_SIZE); +} +#endif + +/* Returns 0 when crle is current. With issuer, as OpenSSL's + * check_crl_time(): always without checkTime, else from its thisUpdate to + * its nextUpdate, if any, at *checkTime, now when it is 0. Without issuer, + * as the handshake checks it: before its nextUpdate, now. + * Returns ASN_BEFORE_DATE_E when it is not yet valid, CRL_CERT_DATE_ERR when + * it has expired. */ +static int CrlDateErr(const CRL_Entry* crle, const Signer* issuer, + const time_t* checkTime) +{ +#if !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_CRL_DATE_CHECK) + if (issuer != NULL) { + if (checkTime == NULL) { + return 0; + } + if (!CrlValidDate(crle->lastDate, crle->lastDateFormat, ASN_BEFORE, + *checkTime)) { + WOLFSSL_MSG("CRL last date is not yet valid"); + return ASN_BEFORE_DATE_E; + } + /* Expired at its nextUpdate, as OpenSSL's X509_cmp_time() */ + if (crle->nextDateFormat != ASN_OTHER_TYPE && + !CrlValidDate(crle->nextDate, crle->nextDateFormat, ASN_AFTER, + (*checkTime != 0) ? *checkTime + 1 : 0)) { + WOLFSSL_MSG("CRL next date is no longer valid"); + return CRL_CERT_DATE_ERR; + } + return 0; + } +#ifdef WOLFSSL_NO_CRL_NEXT_DATE + if (crle->nextDateFormat == ASN_OTHER_TYPE) { + return 0; + } +#endif + if (!XVALIDATE_DATE(crle->nextDate, crle->nextDateFormat, ASN_AFTER, + MAX_DATE_SIZE)) { + WOLFSSL_MSG("CRL next date is no longer valid"); + return CRL_CERT_DATE_ERR; + } +#else + (void)crle; + (void)issuer; + (void)checkTime; +#endif + return 0; +} + +#ifdef OPENSSL_EXTRA +/* As OpenSSL's get_crl_sk(), of the CRLs in list of the chain issuer: one + * current at *checkTime over one that is not, then a later thisUpdate. + * *cur, 0 for none, and *last are of the best so far. + * Returns the entry that is better, else NULL. */ +static CRL_Entry* CrlPick(CRL_Entry* list, const byte* issuerHash, + const Signer* issuer, const time_t* checkTime, int* cur, + WOLFSSL_ASN1_TIME* last) +{ + CRL_Entry* best = NULL; + int c; + + for (; list != NULL; list = list->next) { + if (XMEMCMP(list->issuerHash, issuerHash, CRL_DIGEST_SIZE) != 0) { + continue; + } + #if !defined(NO_SKID) && !defined(NO_ASN) + if (CrlOfOtherKey(list, issuer)) { + continue; + } + #endif + /* 2 when current, else 1 */ + c = (CrlDateErr(list, issuer, checkTime) == 0) ? 2 : 1; + #ifndef NO_ASN_TIME + if (c < *cur || (c == *cur && + wolfSSL_ASN1_TIME_compare(&list->lastDateAsn1, last) <= 0)) { + continue; + } + #else + if (c <= *cur) { + continue; + } + #endif + best = list; + *cur = c; + *last = list->lastDateAsn1; + } + return best; +} + +/* For X509_verify_cert(): returns 1 when crl has a CRL of cert's issuer, + * the chain issuer, better than *cur and *last, as CrlPick(), else 0 or + * BAD_MUTEX_E. */ +int PickCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert, Signer* issuer, + const time_t* checkTime, int* cur, WOLFSSL_ASN1_TIME* last) +{ + int ret; + + if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { + WOLFSSL_MSG("wc_LockRwLock_Rd failed"); + return BAD_MUTEX_E; + } + ret = CrlPick(crl->crlList, cert->issuerHash, issuer, checkTime, cur, + last) != NULL; + wc_UnLockRwLock(&crl->crlLock); + + return ret; +} +#endif + +/* Without a current CRL, returns the date error of one. With issuer, only + * the CRL CrlPick() picks is checked. */ static int CheckCertCRLList(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int serialSz, byte* serialHash, int *pFoundEntry, - WOLFSSL_CERT_MANAGER* cm) + WOLFSSL_CERT_MANAGER* cm, Signer* issuer, const time_t* checkTime) { CRL_Entry* crle; int foundEntry = 0; int ret = 0; + int dateErr = 0; +#ifdef OPENSSL_EXTRA + CRL_Entry* best = NULL; + int cur = 0; + WOLFSSL_ASN1_TIME last; +#endif if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { WOLFSSL_MSG("wc_LockRwLock_Rd failed"); return BAD_MUTEX_E; } +#ifdef OPENSSL_EXTRA + if (issuer != NULL) { + XMEMSET(&last, 0, sizeof(last)); + best = CrlPick(crl->crlList, issuerHash, issuer, checkTime, &cur, + &last); + } +#endif for (crle = crl->crlList; crle != NULL; crle = crle->next) { if (XMEMCMP(crle->issuerHash, issuerHash, CRL_DIGEST_SIZE) == 0) { - int nextDateValid = 1; + int curDateErr; + #ifdef OPENSSL_EXTRA + if (issuer != NULL && crle != best) { + continue; + } + #endif WOLFSSL_MSG("Found CRL Entry on list"); - if (cm != crl->cm) { + #ifndef IGNORE_KEY_EXTENSIONS + /* As OpenSSL's check_crl(), cRLSign before the dates */ + if (issuer != NULL && (issuer->keyUsage & KEYUSE_CRL_SIGN) == 0) { + WOLFSSL_MSG("CA cannot sign CRLs"); + ret = ASN_CRL_NO_SIGNER_E; + foundEntry = 1; + break; + } + #endif + + WOLFSSL_MSG("Checking next date validity"); + curDateErr = CrlDateErr(crle, issuer, checkTime); + /* As OpenSSL, the dates of a CRL of the chain issuer are checked + * before its signature */ + if (curDateErr != 0 && issuer != NULL) { + if (dateErr != WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) + dateErr = curDateErr; + continue; + } + + if (cm != crl->cm || issuer != NULL) { /* cm is not the owning cm of crl. The cached result is only * valid for the owning cm. Verify again without caching. */ - ret = VerifyCRLE(crl, crle, cm, 0); + ret = VerifyCRLE(crl, crle, cm, 0, issuer); if (ret != 0) { WOLFSSL_MSG("Cannot use CRL as it didn't verify"); + /* With a chain issuer it is a signature failure, as + * in OpenSSL, not a missing CRL */ + if (issuer != NULL) + foundEntry = 1; break; } } @@ -574,7 +780,7 @@ static int CheckCertCRLList(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, /* A different thread may have verified the entry while we * were waiting for the mutex. */ if (crle->verified == 0) - ret = VerifyCRLE(crl, crle, cm, 1); + ret = VerifyCRLE(crl, crle, cm, 1, NULL); wc_UnLockMutex(&crle->verifyMutex); @@ -589,35 +795,24 @@ static int CheckCertCRLList(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, } } - WOLFSSL_MSG("Checking next date validity"); - - #ifdef WOLFSSL_NO_CRL_NEXT_DATE - if (crle->nextDateFormat != ASN_OTHER_TYPE) - #endif - { - #if !defined(NO_ASN_TIME) && !defined(WOLFSSL_NO_CRL_DATE_CHECK) - if (!XVALIDATE_DATE(crle->nextDate, crle->nextDateFormat, - ASN_AFTER, MAX_DATE_SIZE)) { - WOLFSSL_MSG("CRL next date is no longer valid"); - nextDateValid = 0; - } - #endif - } - if (nextDateValid) { + if (curDateErr == 0) { foundEntry = 1; ret = FindRevokedSerial(crle->certs, serial, serialSz, serialHash, crle->totalCerts); if (ret != 0) break; } - else if (foundEntry == 0) { - ret = CRL_CERT_DATE_ERR; + else if (dateErr != WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) { + dateErr = curDateErr; } } } wc_UnLockRwLock(&crl->crlLock); + if (foundEntry == 0 && ret == 0) { + ret = dateErr; + } *pFoundEntry = foundEntry; return ret; @@ -629,7 +824,7 @@ static int CheckCertCRLList(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, static int CheckCertCRLCm(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int serialSz, byte* serialHash, const byte* extCrlInfo, int extCrlInfoSz, void* issuerName, WOLFSSL_CERT_MANAGER* cm, - int canRetry) + int canRetry, Signer* issuer, const time_t* checkTime) { int foundEntry = 0; int ret = 0; @@ -644,14 +839,15 @@ static int CheckCertCRLCm(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, } #ifdef WOLFSSL_CRL_ALLOW_MISSING_CDP - /* Skip CRL verification in case no CDP in peer cert */ - if (!extCrlInfo) { + /* Skip CRL verification in case no CDP in peer cert. As OpenSSL, a chain + * issuer's CRLs are checked. */ + if (!extCrlInfo && issuer == NULL) { return ret; } #endif ret = CheckCertCRLList(crl, issuerHash, serial, serialSz, serialHash, - &foundEntry, cm); + &foundEntry, cm, issuer, checkTime); #ifdef HAVE_CRL_IO if (foundEntry == 0) { @@ -665,7 +861,7 @@ static int CheckCertCRLCm(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, else if (cbRet >= 0) { /* try again */ ret = CheckCertCRLList(crl, issuerHash, serial, serialSz, - serialHash, &foundEntry, cm); + serialHash, &foundEntry, cm, issuer, checkTime); } } } @@ -684,7 +880,7 @@ static int CheckCertCRLCm(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, if (loadRet == WOLFSSL_SUCCESS) { /* try again */ ret = CheckCertCRLList(crl, issuerHash, serial, serialSz, - serialHash, &foundEntry, cm); + serialHash, &foundEntry, cm, issuer, checkTime); } } } @@ -696,7 +892,8 @@ static int CheckCertCRLCm(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, #endif ) { WOLFSSL_MSG("Couldn't find CRL for status check"); - if (ret != WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR)) { + if (ret != WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR) && + ret != WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) { ret = CRL_MISSING; } @@ -734,7 +931,7 @@ int CheckCertCRL_ex(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int extCrlInfoSz, void* issuerName) { return CheckCertCRLCm(crl, issuerHash, serial, serialSz, serialHash, - extCrlInfo, extCrlInfoSz, issuerName, crl->cm, 1); + extCrlInfo, extCrlInfoSz, issuerName, crl->cm, 1, NULL, NULL); } /* Get the issuer X509_NAME of cert to pass to CheckCertCRLCm(). */ @@ -754,28 +951,34 @@ static void* CertCrlIssuerName(DecodedCert* cert) } static int CheckDecodedCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert, - WOLFSSL_CERT_MANAGER* cm, int canRetry) + WOLFSSL_CERT_MANAGER* cm, int canRetry, Signer* issuer, + const time_t* checkTime) { void* issuerName = CertCrlIssuerName(cert); return CheckCertCRLCm(crl, cert->issuerHash, cert->serial, cert->serialSz, NULL, cert->extCrlInfo, cert->extCrlInfoSz, issuerName, cm, - canRetry); + canRetry, issuer, checkTime); } /* Is the cert ok with CRL, return 0 on success */ int CheckCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert) { - return CheckDecodedCertCRL(crl, cert, crl->cm, 1); + return CheckDecodedCertCRL(crl, cert, crl->cm, 1, NULL, NULL); } /* Check cert against crl using cm for CRL signature verification. Does not * modify crl, so crl can be a caller-owned object shared between threads. - * Return 0 on success. */ + * As in OpenSSL, issuer, the cert's issuer on the chain, must have signed + * the CRL, and a CRL is current from its thisUpdate to its nextUpdate, if + * any, at *checkTime, now when it is 0. Without checkTime CRL dates are not + * checked. + * Return 0 on success, ASN_BEFORE_DATE_E or CRL_CERT_DATE_ERR when no CRL is + * current. */ int CheckCertCRLFromCm(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_CRL* crl, - DecodedCert* cert) + DecodedCert* cert, Signer* issuer, const time_t* checkTime) { - return CheckDecodedCertCRL(crl, cert, cm, 0); + return CheckDecodedCertCRL(crl, cert, cm, 0, issuer, checkTime); } #ifdef HAVE_CRL_UPDATE_CB diff --git a/src/internal.c b/src/internal.c index b1283fb3c8f..94a86daecb3 100644 --- a/src/internal.c +++ b/src/internal.c @@ -3357,6 +3357,7 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap) return MEMORY_E; } XMEMSET(ctx->param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM)); + ctx->param->depth = -1; /* WOLFSSL_X509_LOOKUP */ if ((ctx->x509_store.lookup.dirs = (WOLFSSL_BY_DIR*)XMALLOC( @@ -3375,6 +3376,7 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap) return MEMORY_E; } XMEMSET(ctx->x509_store.param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM)); + ctx->x509_store.param->depth = -1; if (wc_InitMutex(&ctx->x509_store.lookup.dirs->lock) != 0) { WOLFSSL_MSG("Bad mutex init"); @@ -9570,6 +9572,7 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) return MEMORY_E; } XMEMSET(ssl->param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM)); + ssl->param->depth = -1; /* pass on PARAM values from ctx to ssl, including the expected * hostname / IP */ @@ -15774,6 +15777,8 @@ WC_MAYBE_UNUSED static void ResetSessionCertChain(WOLFSSL* ssl) ssl->session->peer = NULL; wolfSSL_sk_X509_pop_free(ssl->peerCertChain, NULL); ssl->peerCertChain = NULL; + wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); + ssl->verifiedChain = NULL; #endif } #endif @@ -17277,7 +17282,6 @@ int SetupStoreCtxCallback(WOLFSSL_X509_STORE_CTX** store_pt, #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) store->store = SSL_STORE(ssl); #if defined(OPENSSL_EXTRA) - store->depth = args->count; /* Overwrite with non-default param values in SSL */ if (ssl->param) { if (ssl->param->check_time) @@ -18385,8 +18389,9 @@ static int AdjustCMForParams(WOLFSSL* ssl) param = wolfSSL_get0_param(ssl); flags = wolfSSL_X509_VERIFY_PARAM_get_flags(param); /* 0x2 in a param is X509_V_FLAG_USE_CHECK_TIME, but - * wolfSSL_X509_STORE_set_flags() reads it as WOLFSSL_CRL_CHECK. */ - flags &= ~WOLFSSL_USE_CHECK_TIME; + * wolfSSL_X509_STORE_set_flags() reads it as WOLFSSL_CRL_CHECK. The + * X509_STRICT flag of the connection is not for the store. */ + flags &= ~(WOLFSSL_USE_CHECK_TIME | WOLFSSL_X509_STRICT); /* For now there is a possible contradiction of PARAM flags and store flags. * Do not disable CRL support if it has already been enabled with store. */ diff --git a/src/ssl.c b/src/ssl.c index 1b5071405ee..cb8aae381ba 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -433,11 +433,15 @@ WC_RNG* wolfssl_make_rng(WC_RNG* rng, int* local) * them here, next to the includes, rather than inside the files that need * them. * - * x509GetIssuerFromCM() is defined in src/x509_str.c, which also requires - * NO_CERTS to be undefined. */ + * x509GetIssuerFromCM() and X509StoreCmIsIssuer() are defined in + * src/x509_str.c, which also requires NO_CERTS to be undefined. */ #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_CERTS) static int x509GetIssuerFromCM(WOLFSSL_X509 **issuer, WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509 *x); +#ifdef KEEP_PEER_CERT +static int X509StoreCmIsIssuer(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509* ca, + WOLFSSL_X509* x509); +#endif #endif #define WOLFSSL_SSL_API_CERT_INCLUDED @@ -6147,6 +6151,11 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #ifdef KEEP_PEER_CERT FreePeerCert(ssl); #endif +#ifdef OPENSSL_EXTRA + /* The next connection builds its own verified chain. */ + wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); + ssl->verifiedChain = NULL; +#endif #ifdef WOLFSSL_QUIC wolfSSL_quic_clear(ssl); diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 15781db1732..d2895e0e281 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -3395,21 +3395,64 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_set_peer_cert_chain(WOLFSSL* ssl) } #ifdef KEEP_PEER_CERT -/* Get the peer's certificate chain, verified against the store. +/* Verify callback for a chain the handshake verified. As the handshake, it + * accepts the date and CA errors of a cert manager CA where that CA verifies + * the cert below it. Any other error fails. + * + * @param [in] ok 1 when the cert at the error depth passed its checks. + * @param [in] ctx X509 store context. + * @return 1 when verification is to continue, 0 to fail. + */ +static int VerifiedChainCb(int ok, WOLFSSL_X509_STORE_CTX* ctx) +{ + if ((!ok) && (ctx->error_depth > 0)) { + switch (ctx->error) { + case WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID: + case WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED: + case WOLFSSL_X509_V_ERR_INVALID_CA: + case WOLFSSL_X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + ok = X509StoreCmIsIssuer(ctx->store->cm, + wolfSSL_sk_X509_value(ctx->chain, ctx->error_depth), + wolfSSL_sk_X509_value(ctx->chain, ctx->error_depth - 1)); + break; + default: + break; + } + } + + return ok; +} + +/* Returns 1 when the verified chain kept on ssl is of the peer's cert. */ +static int VerifiedChainIsPeer(const WOLFSSL* ssl) +{ + WOLFSSL_X509* x = wolfSSL_sk_X509_value(ssl->verifiedChain, 0); + const WOLFSSL_X509_CHAIN* peer = &ssl->session->chain; + + return (x != NULL) && (x->derCert != NULL) && (peer->count > 0) && + ((int)x->derCert->length == peer->certs[0].length) && + (XMEMCMP(x->derCert->buffer, peer->certs[0].buffer, + x->derCert->length) == 0); +} + +/* Get the peer's chain as verified, from its cert to the trust anchor. * - * Implemented in a similar way to ngx_ssl_ocsp_validate() when - * SSL_get0_verified_chain is not available. The chain is stored on the SSL/TLS - * object and disposed of with it, so the caller must not free it. + * Built with wolfSSL_X509_verify_cert() once per peer cert and kept on ssl + * until a new peer chain or wolfSSL_clear(), as OpenSSL keeps the chain of the + * handshake. After a full handshake that verified the peer, VerifiedChainCb() + * applies; else every check does. Without WOLFSSL_SIGNER_DER_CERT a CA of the + * cert manager cannot go on the chain. * * @param [in, out] ssl SSL/TLS object. Declared const, but the verified * chain is stored into it. - * @return Stack of verified certificates on success. + * @return Stack of verified certificates on success. Owned by ssl. * @return NULL when ssl or its context is NULL, no peer certificate was kept, * the chain cannot be built, or verification fails. */ WOLF_STACK_OF(WOLFSSL_X509) *wolfSSL_get0_verified_chain(const WOLFSSL *ssl) { WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL; + WOLF_STACK_OF(WOLFSSL_X509)* peerChain = NULL; WOLFSSL_X509_STORE_CTX* storeCtx = NULL; WOLFSSL_X509* peerCert = NULL; int err = 0; @@ -3421,6 +3464,9 @@ WOLF_STACK_OF(WOLFSSL_X509) *wolfSSL_get0_verified_chain(const WOLFSSL *ssl) WOLFSSL_MSG("Bad parameter"); err = 1; } + else if (VerifiedChainIsPeer(ssl)) { + return ssl->verifiedChain; + } if (!err) { /* Owned by ssl, so not freed here. */ @@ -3432,19 +3478,12 @@ WOLF_STACK_OF(WOLFSSL_X509) *wolfSSL_get0_verified_chain(const WOLFSSL *ssl) } if (!err) { - chain = CreatePeerCertChain((WOLFSSL*)ssl, 1); - if (chain == NULL) { + /* The peer's certificate first, then its chain and the CA chain. */ + peerChain = CreatePeerCertChain((WOLFSSL*)ssl, 1); + if (peerChain == NULL) { WOLFSSL_MSG("wolfSSL_get_peer_cert_chain error"); err = 1; } - else { - /* Replace any chain kept from a previous call. */ - if (ssl->verifiedChain != NULL) { - wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); - } - /* This is Free'd when ssl is Free'd */ - ((WOLFSSL*)ssl)->verifiedChain = chain; - } } if (!err) { @@ -3456,23 +3495,41 @@ WOLF_STACK_OF(WOLFSSL_X509) *wolfSSL_get0_verified_chain(const WOLFSSL *ssl) } if (!err) { - if (wolfSSL_X509_STORE_CTX_init(storeCtx, SSL_STORE(ssl), peerCert, - chain) != WOLFSSL_SUCCESS) { + if (wolfSSL_X509_STORE_CTX_init(storeCtx, SSL_STORE(ssl), + wolfSSL_sk_X509_value(peerChain, 0), peerChain) != + WOLFSSL_SUCCESS) { WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_init error"); err = 1; } - else if (wolfSSL_X509_verify_cert(storeCtx) <= 0) { + } + + if (!err) { + /* A full handshake verified the peer: no error, not even one a + * verify callback accepted. */ + if ((!ssl->options.verifyNone) && + (ssl->peerVerifyRet == WOLFSSL_X509_V_OK) && + (!wolfSSL_session_reused((WOLFSSL*)ssl))) { + wolfSSL_X509_STORE_CTX_set_verify_cb(storeCtx, VerifiedChainCb); + } + if (wolfSSL_X509_verify_cert(storeCtx) != WOLFSSL_SUCCESS) { WOLFSSL_MSG("wolfSSL_X509_verify_cert error"); err = 1; } } - wolfSSL_X509_STORE_CTX_free(storeCtx); - if (err) { - /* The chain stays owned by the object; report failure only. */ - chain = NULL; + if (!err) { + /* Take the chain built, leaf to trust anchor. */ + chain = storeCtx->chain; + storeCtx->chain = NULL; + /* Replace a chain of an earlier peer. */ + wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL); + /* This is Free'd when ssl is Free'd */ + ((WOLFSSL*)ssl)->verifiedChain = chain; } + wolfSSL_X509_STORE_CTX_free(storeCtx); + wolfSSL_sk_X509_pop_free(peerChain, NULL); + return chain; } #endif /* KEEP_PEER_CERT */ diff --git a/src/ssl_certman.c b/src/ssl_certman.c index e1942278e07..98249fe60f3 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -518,15 +518,6 @@ int wolfSSL_CertManagerUnloadTypeCerts( return ret; } -#if defined(OPENSSL_EXTRA) -static int wolfSSL_CertManagerUnloadTempIntermediateCerts( - WOLFSSL_CERT_MANAGER* cm) -{ - WOLFSSL_ENTER("wolfSSL_CertManagerUnloadTempIntermediateCerts"); - return wolfSSL_CertManagerUnloadTypeCerts(cm, WOLFSSL_TEMP_CA); -} -#endif - int wolfSSL_CertManagerUnloadIntermediateCerts( WOLFSSL_CERT_MANAGER* cm) { diff --git a/src/x509.c b/src/x509.c index 5db523e3f2c..b780a3c3b62 100644 --- a/src/x509.c +++ b/src/x509.c @@ -11659,8 +11659,10 @@ WOLFSSL_X509_VERIFY_PARAM* wolfSSL_X509_VERIFY_PARAM_new(void) WOLFSSL_X509_VERIFY_PARAM *param = NULL; param = (WOLFSSL_X509_VERIFY_PARAM*)XMALLOC( sizeof(WOLFSSL_X509_VERIFY_PARAM), NULL, DYNAMIC_TYPE_OPENSSL); - if (param != NULL) + if (param != NULL) { XMEMSET(param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM )); + param->depth = -1; + } return(param); } @@ -11698,6 +11700,18 @@ void wolfSSL_X509_VERIFY_PARAM_set_time(WOLFSSL_X509_VERIFY_PARAM *param, } +/* Sets the maximum number of intermediate CAs that X509_verify_cert() allows. + * -1 means unset. TLS verification does not use it. */ +void wolfSSL_X509_VERIFY_PARAM_set_depth(WOLFSSL_X509_VERIFY_PARAM *param, + int depth) +{ + WOLFSSL_ENTER("wolfSSL_X509_VERIFY_PARAM_set_depth"); + if (param != NULL) { + param->depth = depth; + } +} + + int wolfSSL_X509_VERIFY_PARAM_get_flags(WOLFSSL_X509_VERIFY_PARAM *param) { int ret = 0; @@ -11723,7 +11737,7 @@ int wolfSSL_X509_VERIFY_PARAM_clear_flags(WOLFSSL_X509_VERIFY_PARAM *param, return ret; } -/* note WOLFSSL_X509_VERIFY_PARAM does not record purpose, trust, depth, or +/* note WOLFSSL_X509_VERIFY_PARAM does not record purpose, trust, or * auth_level. */ static const WOLFSSL_X509_VERIFY_PARAM x509_verify_param_builtins[] = { @@ -11734,7 +11748,8 @@ static const WOLFSSL_X509_VERIFY_PARAM x509_verify_param_builtins[] = { 0, /* flags */ "", /* hostname */ 0, /* hostFlags */ - "" /* ipasc */ + "", /* ipasc */ + -1 /* depth */ }, { "ssl_server", /* name */ @@ -11743,7 +11758,8 @@ static const WOLFSSL_X509_VERIFY_PARAM x509_verify_param_builtins[] = { 0, /* flags */ "", /* hostname */ 0, /* hostFlags */ - "" /* ipasc */ + "", /* ipasc */ + -1 /* depth */ } }; @@ -11800,6 +11816,12 @@ int wolfSSL_X509_VERIFY_PARAM_inherit(WOLFSSL_X509_VERIFY_PARAM *to, isOverWrite = (flags & WOLFSSL_VPARAM_OVERWRITE); isDefault = (flags & WOLFSSL_VPARAM_DEFAULT); + /* copy depth if set, -1 is unset */ + if (isOverWrite || + (from->depth != -1 && (to->depth == -1 || isDefault))) { + to->depth = from->depth; + } + /* copy check_time if check time is not set */ if ((to->flags & WOLFSSL_USE_CHECK_TIME) == 0 || isOverWrite) { to->check_time = from->check_time; diff --git a/src/x509_str.c b/src/x509_str.c index 5c81055c366..f4c9ff128ce 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -34,11 +34,13 @@ #ifdef OPENSSL_EXTRA static int X509StoreGetIssuerEx(WOLFSSL_X509 **issuer, WOLFSSL_STACK *certs, WOLFSSL_X509 *x); -static int X509StoreGetIssuerSkip(WOLFSSL_X509 **issuer, - WOLFSSL_STACK *certs, WOLFSSL_X509 *x, - WOLF_STACK_OF(WOLFSSL_X509)* skip); static int X509StoreAddCa(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509, int type); +static int X509StoreCertInStack(WOLF_STACK_OF(WOLFSSL_X509)* stack, + WOLFSSL_X509* cert); +static int X509StoreCertPartialChainEnabled(WOLFSSL_X509_STORE_CTX* ctx); +static int X509StoreKeySigned(WOLFSSL_X509* ca, WOLFSSL_X509* x, int* err); +static int X509StoreIssued(WOLFSSL_X509* ca, WOLFSSL_X509* x); #endif /* Based on OpenSSL default max depth */ @@ -86,15 +88,12 @@ void wolfSSL_X509_STORE_CTX_free(WOLFSSL_X509_STORE_CTX* ctx) #endif #ifdef OPENSSL_EXTRA - XFREE(ctx->param, ctx->heap, DYNAMIC_TYPE_OPENSSL); + wolfSSL_X509_VERIFY_PARAM_free(ctx->param); ctx->param = NULL; if (ctx->chain != NULL) { wolfSSL_sk_X509_pop_free(ctx->chain, NULL); } - if (ctx->owned != NULL) { - wolfSSL_sk_X509_pop_free(ctx->owned, NULL); - } if (ctx->current_issuer != NULL) { wolfSSL_X509_free(ctx->current_issuer); @@ -215,15 +214,6 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, wolfSSL_sk_X509_pop_free(ctx->chain, NULL); ctx->chain = NULL; } - /* Release the issuers retained by the previous verification and start - * a fresh stack. X509StoreVerifyCert() drops the issuer it decodes - * when this is NULL, so the context must always carry one. */ - wolfSSL_sk_X509_pop_free(ctx->owned, NULL); - ctx->owned = wolfSSL_sk_X509_new_null(); - if (ctx->owned == NULL) { - WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_init failed"); - return WOLFSSL_FAILURE; - } #ifdef SESSION_CERTS ctx->sesChain = NULL; #endif @@ -231,9 +221,6 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, #ifdef HAVE_EX_DATA XMEMSET(&ctx->ex_data, 0, sizeof(ctx->ex_data)); #endif -#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_EXTRA) - ctx->depth = WOLFSSL_X509_STORE_DEFAULT_MAX_DEPTH; -#endif ctx->userCtx = NULL; ctx->verify_cb = NULL; @@ -241,16 +228,18 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, ctx->error_depth = 0; ctx->discardSessionCerts = 0; + /* As X509_VERIFY_PARAM_new(), so set0_param() can replace it */ if (ctx->param == NULL) { ctx->param = (WOLFSSL_X509_VERIFY_PARAM*)XMALLOC( sizeof(WOLFSSL_X509_VERIFY_PARAM), - ctx->heap, DYNAMIC_TYPE_OPENSSL); + NULL, DYNAMIC_TYPE_OPENSSL); if (ctx->param == NULL){ WOLFSSL_MSG("wolfSSL_X509_STORE_CTX_init failed"); return WOLFSSL_FAILURE; } } XMEMSET(ctx->param, 0, sizeof(*ctx->param)); + ctx->param->depth = -1; /* Inherit the store's parameters, including the hostname / IP the * caller expects the peer to present. */ @@ -261,6 +250,12 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, return WOLFSSL_FAILURE; } } + /* No depth from the store: use the default, as OpenSSL does */ + if (ctx->param->depth == -1) { + ctx->param->depth = WOLFSSL_X509_STORE_DEFAULT_MAX_DEPTH; + } + /* As OpenSSL's default param */ + ctx->param->flags |= WOLFSSL_TRUSTED_FIRST; return WOLFSSL_SUCCESS; } @@ -274,7 +269,7 @@ void wolfSSL_X509_STORE_CTX_cleanup(WOLFSSL_X509_STORE_CTX* ctx) { if (ctx != NULL) { - XFREE(ctx->param, ctx->heap, DYNAMIC_TYPE_OPENSSL); + wolfSSL_X509_VERIFY_PARAM_free(ctx->param); ctx->param = NULL; wolfSSL_X509_STORE_CTX_init(ctx, NULL, NULL, NULL); @@ -346,7 +341,10 @@ int GetX509Error(int e) case WC_NO_ERR_TRACE(CRL_CERT_REVOKED): return WOLFSSL_X509_V_ERR_CERT_REVOKED; case WC_NO_ERR_TRACE(CRL_MISSING): + case WC_NO_ERR_TRACE(ASN_CRL_NO_SIGNER_E): return WOLFSSL_X509_V_ERR_UNABLE_TO_GET_CRL; + case WC_NO_ERR_TRACE(ASN_CRL_CONFIRM_E): + return WOLFSSL_X509_V_ERR_CRL_SIGNATURE_FAILURE; case WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E): return WOLFSSL_X509_V_ERR_INVALID_PURPOSE; /* is an internal wolfSSL return code, not an X509_V_* code, so 1 @@ -383,161 +381,146 @@ static void SetupStoreCtxError_ex(WOLFSSL_X509_STORE_CTX* ctx, int ret, wolfSSL_X509_STORE_CTX_set_error_depth(ctx, depth); } -static void SetupStoreCtxError(WOLFSSL_X509_STORE_CTX* ctx, int ret) +#ifndef NO_ASN_TIME +/* The verification time, as OpenSSL's: the param time with + * X509_V_FLAG_USE_CHECK_TIME, none with X509_V_FLAG_NO_CHECK_TIME, else the + * current time, 0. As in wolfCrypt's date checks, a param time of 0 is the + * current time. A custom XVALIDATE_DATE always checks against its own time. + * Returns 0 when dates are not checked, else 1 and the time in *t. */ +static int X509StoreCheckTime(WOLFSSL_X509_STORE_CTX* ctx, time_t* t) { - int depth = 0; - - /* Set error depth */ - if (ctx->chain) - depth = (int)ctx->chain->num; +#ifdef USE_WOLF_VALIDDATE + WOLFSSL_X509_VERIFY_PARAM* param = ctx->param; - SetupStoreCtxError_ex(ctx, ret, depth); + if (param == NULL && ctx->store != NULL) { + param = ctx->store->param; + } + if (param != NULL && (param->flags & WOLFSSL_USE_CHECK_TIME) != 0) { + *t = param->check_time; + return 1; + } + if (param != NULL && (param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { + return 0; + } +#else + (void)ctx; +#endif + *t = 0; + return 1; } -#ifndef NO_ASN_TIME -/* Post certificate validation date handling. This function is called after the - * certificate has been verified by the certificate manager. It then checks if - * X509 store parameters are set for date validation override. - * @param ctx The certificate store context - * @param ret The return value from the certificate manager verify - * @return The return value for the certificate date validation after override - */ -static int X509StoreVerifyCertDate(WOLFSSL_X509_STORE_CTX* ctx, int ret) +/* Checks the validity period of x509 at the verification time. + * Returns WOLFSSL_SUCCESS, ASN_BEFORE_DATE_E or ASN_AFTER_DATE_E. */ +static int X509StoreCheckCertDate(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509* x509) { - byte *afterDate = ctx->current_cert->notAfter.data; - byte *beforeDate = ctx->current_cert->notBefore.data; + time_t t; - /* Only override existing date errors or WOLFSSL_SUCCESS. */ - if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) || - ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E) || - ret == WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) { + if (!X509StoreCheckTime(ctx, &t)) { + return WOLFSSL_SUCCESS; + } #ifdef USE_WOLF_VALIDDATE - WOLFSSL_X509_VERIFY_PARAM* param = NULL; - - /* If no external XVALIDATE_DATE was defined then use param for date - validation overrides. */ - if (ctx->param != NULL) { - param = ctx->param; + if (t != 0) { + if (wc_ValidateDateWithTime(x509->notBefore.data, + (byte)x509->notBefore.type, ASN_BEFORE, t, + x509->notBefore.length) < 1) { + return ASN_BEFORE_DATE_E; + } + /* Expired at its notAfter, as OpenSSL's X509_cmp_time() */ + if (wc_ValidateDateWithTime(x509->notAfter.data, + (byte)x509->notAfter.type, ASN_AFTER, t + 1, + x509->notAfter.length) < 1) { + return ASN_AFTER_DATE_E; } - else if (ctx->store != NULL && ctx->store->param != NULL) { - param = ctx->store->param; - } - - if (param != NULL) { - if ((param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { - WOLFSSL_MSG("Overriding date validation WOLFSSL_NO_CHECK_TIME"); - ret = WOLFSSL_SUCCESS; - } - else if ((param->flags & WOLFSSL_USE_CHECK_TIME) != 0 && - (param->check_time != 0)) { - time_t checkTime = param->check_time; - ret = WOLFSSL_SUCCESS; /* override date error and use custom set - time for validating certificate dates */ - WOLFSSL_MSG("Override date validation, WOLFSSL_USE_CHECK_TIME"); - if (wc_ValidateDateWithTime(afterDate, - (byte)ctx->current_cert->notAfter.type, ASN_AFTER, - checkTime, ctx->current_cert->notAfter.length) < 1) { - ret = ASN_AFTER_DATE_E; - } - else if (wc_ValidateDateWithTime(beforeDate, - (byte)ctx->current_cert->notBefore.type, ASN_BEFORE, - checkTime, ctx->current_cert->notBefore.length) < 1) { - ret = ASN_BEFORE_DATE_E; - } - } - #if defined(OPENSSL_ALL) - else { - WOLFSSL_MSG("Using system time for date validation"); - /* use system time for date validation */ - if (wc_ValidateDate(afterDate, - (byte)ctx->current_cert->notAfter.type, ASN_AFTER, - ctx->current_cert->notAfter.length) < 1) { - ret = ASN_AFTER_DATE_E; - } - else if (wc_ValidateDate(beforeDate, - (byte)ctx->current_cert->notBefore.type, ASN_BEFORE, - ctx->current_cert->notBefore.length) < 1) { - ret = ASN_BEFORE_DATE_E; - } - } - #endif - } -#else - if (XVALIDATE_DATE(afterDate, - (byte)ctx->current_cert->notAfter.type, ASN_AFTER, - ctx->current_cert->notAfter.length) < 1) { - ret = ASN_AFTER_DATE_E; - } - else if (XVALIDATE_DATE(beforeDate, - (byte)ctx->current_cert->notBefore.type, ASN_BEFORE, - ctx->current_cert->notBefore.length) < 1) { - ret = ASN_BEFORE_DATE_E; - } -#endif /* USE_WOLF_VALIDDATE */ + return WOLFSSL_SUCCESS; } - - return ret; +#endif + if (XVALIDATE_DATE(x509->notBefore.data, (byte)x509->notBefore.type, + ASN_BEFORE, x509->notBefore.length) < 1) { + return ASN_BEFORE_DATE_E; + } + if (XVALIDATE_DATE(x509->notAfter.data, (byte)x509->notAfter.type, + ASN_AFTER, x509->notAfter.length) < 1) { + return ASN_AFTER_DATE_E; + } + return WOLFSSL_SUCCESS; } + #endif /* NO_ASN_TIME */ #ifdef HAVE_CRL -/* Check ctx->current_cert against the CRLs set with - * X509_STORE_CTX_set0_crls. - * Returns WOLFSSL_SUCCESS if a CRL for the cert's issuer is in the stack and - * the cert is not revoked. Returns CRL_MISSING if the stack has no CRL for - * the issuer. Returns a negative error on revocation or CRL failure. */ -static int X509StoreCheckCtxCrls(WOLFSSL_X509_STORE_CTX* ctx) +/* Check ctx->current_cert against the store's CRLs, signed by issuer and + * current at *checkTime, as CheckCertCRLFromCm() checks them. + * Returns WOLFSSL_SUCCESS or the CRL error. */ +static int X509StoreCheckStoreCrl(WOLFSSL_X509_STORE_CTX* ctx, Signer* issuer, + const time_t* checkTime) { - int ret = WC_NO_ERR_TRACE(CRL_MISSING); - int found = 0; - int dateErr = 0; + int ret; + WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + + WC_ALLOC_VAR_EX(cert, DecodedCert, 1, ctx->heap, DYNAMIC_TYPE_DCERT, + return MEMORY_E); + + InitDecodedCert(cert, ctx->current_cert->derCert->buffer, + ctx->current_cert->derCert->length, ctx->heap); + ret = ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, ctx->store->cm, NULL); + if (ret == 0) { + ret = CheckCertCRLFromCm(ctx->store->cm, ctx->store->cm->crl, cert, + issuer, checkTime); + } + FreeDecodedCert(cert); + WC_FREE_VAR_EX(cert, ctx->heap, DYNAMIC_TYPE_DCERT); + + return (ret == 0) ? WOLFSSL_SUCCESS : ret; +} + +/* As OpenSSL's get_crl_sk(), checks the cert with only the best CRL set with + * X509_STORE_CTX_set0_crls. Returns WOLFSSL_SUCCESS or the CRL error. */ +static int X509StoreCheckCtxCrls(WOLFSSL_X509_STORE_CTX* ctx, Signer* issuer, + const time_t* checkTime) +{ + int ret; int i; int numCrls; + int cur = 0; + WOLFSSL_ASN1_TIME last; + WOLFSSL_X509_CRL* best = NULL; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); numCrls = wolfSSL_sk_X509_CRL_num(ctx->crls); if (numCrls <= 0) - return ret; + return WC_NO_ERR_TRACE(CRL_MISSING); WC_ALLOC_VAR_EX(cert, DecodedCert, 1, ctx->heap, DYNAMIC_TYPE_DCERT, return MEMORY_E); + XMEMSET(&last, 0, sizeof(last)); InitDecodedCert(cert, ctx->current_cert->derCert->buffer, ctx->current_cert->derCert->length, ctx->heap); /* The cert signature is verified by the CertManager. Only the issuer and - * serial info is needed here. */ - if (ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, ctx->store->cm, NULL) - == 0) { - /* Check all CRLs in the stack. A revocation in any of them wins over - * a CRL that does not list the cert, like in the CertManager. */ - for (i = 0; i < numCrls; i++) { - WOLFSSL_X509_CRL* crl = wolfSSL_sk_X509_CRL_value(ctx->crls, i); - if (crl == NULL) - continue; - /* Use the store's cm to verify the CRL. The caller-owned crl is - * not modified. */ - ret = CheckCertCRLFromCm(ctx->store->cm, crl, cert); - if (ret == 0) - found = 1; - else if (ret == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR)) - dateErr = 1; /* stale CRL, another CRL can still vouch */ - else if (ret != WC_NO_ERR_TRACE(CRL_MISSING)) - break; + * serial info is needed here. A parse error is final, not CRL_MISSING. */ + ret = ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, ctx->store->cm, NULL); + for (i = 0; ret == 0 && i < numCrls; i++) { + WOLFSSL_X509_CRL* crl = wolfSSL_sk_X509_CRL_value(ctx->crls, i); + + if (crl != NULL) { + ret = PickCertCRL(crl, cert, issuer, checkTime, &cur, &last); + if (ret == 1) { + best = crl; + ret = 0; + } } } + if (ret == 0) { + /* The store's cm verifies the CRL. The caller-owned crl is not + * modified. */ + ret = (best == NULL) ? WC_NO_ERR_TRACE(CRL_MISSING) : + CheckCertCRLFromCm(ctx->store->cm, best, cert, issuer, checkTime); + } FreeDecodedCert(cert); WC_FREE_VAR_EX(cert, ctx->heap, DYNAMIC_TYPE_DCERT); - if (ret == 0 || ret == WC_NO_ERR_TRACE(CRL_MISSING) || - ret == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR)) { - if (found) - ret = WOLFSSL_SUCCESS; - else if (dateErr) - ret = WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR); - else - ret = WC_NO_ERR_TRACE(CRL_MISSING); - } - return ret; + return (ret == 0) ? WOLFSSL_SUCCESS : ret; } #endif /* HAVE_CRL */ @@ -565,179 +548,33 @@ static WOLFSSL_X509_STORE_CTX_verify_cb X509StoreGetVerifyCb( return NULL; } -/* Verify ctx->current_cert against the store. - * - * is an out-parameter, never NULL. It is set to 1 when the - * application's per-context verify callback explicitly rejected a certificate - * the verification itself accepted, and to 0 otherwise. The rejection is - * reported out of band rather than as a return value so that it cannot be - * confused with any of the internal error codes this function passes through. - * - * A caller must stop chain building when it is set: a veto must not be turned - * into a retry with another issuer, and must not be cleared by the - * partial-chain fallback in wolfSSL_X509_verify_cert(). */ -static int X509StoreVerifyCert(WOLFSSL_X509_STORE_CTX* ctx, int* cbRejected, - WOLFSSL_X509_STORE_CTX_verify_cb verifyCb) -{ - int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); - int crlRet = 0; - int datesChecked = 0; - WOLFSSL_ENTER("X509StoreVerifyCert"); - - *cbRejected = 0; - - if (ctx->current_cert != NULL && ctx->current_cert->derCert != NULL) { - ret = CM_VerifyBuffer_ex(ctx->store->cm, - ctx->current_cert->derCert->buffer, - ctx->current_cert->derCert->length, - WOLFSSL_FILETYPE_ASN1, 0, &crlRet); - datesChecked = (ret == WOLFSSL_SUCCESS) || - (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) || - (ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); -#ifdef HAVE_CRL - /* The CRLs set with X509_STORE_CTX_set0_crls can revoke a cert the - * CertManager accepted, or supply the CRL its own store lacked. */ - if (ctx->crls != NULL && ctx->store->cm->crlEnabled && datesChecked && - (crlRet == 0 || crlRet == WC_NO_ERR_TRACE(CRL_MISSING))) { - int ctxCrlRet = X509StoreCheckCtxCrls(ctx); - if (ctxCrlRet == WOLFSSL_SUCCESS) { - crlRet = 0; - } - else if (ctxCrlRet != WC_NO_ERR_TRACE(CRL_MISSING)) { - crlRet = ctxCrlRet; - } - } - /* Revocation is reported before the dates, as OpenSSL does. */ - if (crlRet != 0) { - SetupStoreCtxError(ctx, crlRet); - if ((verifyCb == NULL) || (verifyCb(0, ctx) != 1)) { - return crlRet; - } - } -#endif - #ifndef NO_ASN_TIME - /* update return value with any date validation overrides */ - ret = X509StoreVerifyCertDate(ctx, ret); - #endif - SetupStoreCtxError(ctx, ret); - if (verifyCb != NULL) { - /* Snapshot the error so the rejection below can tell one the - * callback recorded itself from one left over from an earlier - * certificate in the chain - SetupStoreCtxError() preserves the - * worst error seen so far, so ctx->error is not necessarily - * WOLFSSL_X509_V_OK on entry even when this certificate - * verified. */ - int preCbError = ctx->error; - - if (verifyCb(ret >= 0 ? 1 : 0, ctx) == 1) { - ret = WOLFSSL_SUCCESS; - } - else if (ret >= 0 && ctx->verify_cb != NULL) { - /* Returning 0 must reject a chain the cert manager accepted. */ - if (ctx->error == preCbError) { - /* Keep an error the callback recorded itself; otherwise - * the rejection has no error to report. */ - wolfSSL_X509_STORE_CTX_set_error(ctx, - WOLFSSL_X509_V_ERR_UNSPECIFIED); - } - *cbRejected = 1; - ret = WOLFSSL_FAILURE; - } - #if !defined(NO_ASN_TIME) && defined(OPENSSL_ALL) - else if (ret >= 0) { - /* A store callback may reject one too, without a veto. */ - ret = WOLFSSL_FATAL_ERROR; - } - #endif - } - } -#if !defined(NO_ASN_TIME) && defined(OPENSSL_ALL) - /* Skipped once the callback has rejected: the decision is already made, - * and re-running the date check would consult the callback a second time, - * which could overturn the rejection. */ - if (*cbRejected == 0 && !datesChecked && ret == WOLFSSL_SUCCESS) { - /* With OpenSSL, we need to check the certificate's date - * after certificate manager verification, - * as it skips date validation when other errors are present. - */ - ret = X509StoreVerifyCertDate(ctx, ret); - SetupStoreCtxError(ctx, ret); - ret = ret == WOLFSSL_SUCCESS ? 1 : 0; - if (verifyCb != NULL) { - if (verifyCb(ret, ctx) == 1) { - ret = WOLFSSL_SUCCESS; - } - else { - ret = -1; - } - } - } -#endif - (void)datesChecked; - return ret; -} - -static int addAllButSelfSigned(WOLF_STACK_OF(WOLFSSL_X509)*to, - WOLF_STACK_OF(WOLFSSL_X509)*from, int *numAdded) -{ - int ret = WOLFSSL_SUCCESS; - int i = 0; - int cnt = 0; - WOLFSSL_X509 *x = NULL; - - for (i = 0; i < wolfSSL_sk_X509_num(from); i++) { - x = wolfSSL_sk_X509_value(from, i); - if (wolfSSL_X509_NAME_cmp(&x->issuer, &x->subject) != 0) { - if (wolfSSL_sk_X509_push(to, x) <= 0) { - ret = WOLFSSL_FAILURE; - goto exit; - } - cnt++; - } - } - -exit: - if (numAdded != NULL) { - *numAdded = cnt; - } - return ret; -} - -static int X509StoreMoveCert(WOLFSSL_STACK *certs_stack, - WOLFSSL_STACK *dest_stack, - WOLFSSL_X509 *cert) { - int i; - - if (certs_stack == NULL || dest_stack == NULL || cert == NULL) - return WOLFSSL_FATAL_ERROR; - - for (i = 0; i < wolfSSL_sk_X509_num(certs_stack); i++) { - if (wolfSSL_sk_X509_value(certs_stack, i) == cert) { - wolfSSL_sk_X509_push(dest_stack, - (WOLFSSL_X509*)wolfSSL_sk_pop_node(certs_stack, i)); - return WOLFSSL_SUCCESS; - } - } - - return WOLFSSL_FAILURE; -} - /* Push x509 onto the ctx chain with its own reference, like OpenSSL. - * The chain owns a reference to each of its certs. */ + * The chain owns a reference to each of its certs. + * Returns WOLFSSL_SUCCESS, MEMORY_E or WOLFSSL_FATAL_ERROR. */ static int X509StoreChainPush(WOLF_STACK_OF(WOLFSSL_X509)* chain, WOLFSSL_X509* x509) { - int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); if (x509 == NULL || wolfSSL_X509_up_ref(x509) != WOLFSSL_SUCCESS) return ret; ret = wolfSSL_sk_X509_push(chain, x509) > 0 ? WOLFSSL_SUCCESS : - WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + WC_NO_ERR_TRACE(MEMORY_E); if (ret != WOLFSSL_SUCCESS) wolfSSL_X509_free(x509); return ret; } +/* Keeps e, when it is an error, in *err unless one is there: an internal + * error of the chain checks. Returns e. */ +static int X509StoreKeepErr(int* err, int e) +{ + if (err != NULL && *err == 0 && e < 0) { + *err = e; + } + return e; +} + /* Returns 1 if `cert` (by pointer identity) is present in `stack`, else 0. * Used to keep a candidate that already failed verification off the reported * chain. */ @@ -761,25 +598,6 @@ static int X509StoreCertInStack(WOLF_STACK_OF(WOLFSSL_X509)* stack, return 0; } -/* Current certificate failed, but it is possible there is an - * alternative cert with the same subject key which will work. - * Retry until all possible candidate certs are exhausted. - * The temporary CAs of the abandoned path are not removed here: the caller - * drops all of them before it verifies the next link. */ -static void X509VerifyCertSetupRetry(WOLFSSL_X509_STORE_CTX* ctx, - WOLF_STACK_OF(WOLFSSL_X509)* certs, WOLF_STACK_OF(WOLFSSL_X509)* failed, - int* depth, int origDepth) { - WOLFSSL_MSG("X509_verify_cert current cert failed, " - "retrying with other certs."); - X509StoreMoveCert(certs, failed, ctx->current_cert); - ctx->current_cert = wolfSSL_sk_X509_pop(ctx->chain); - /* Release the chain's reference. The cert stays valid through its - * original owner. */ - wolfSSL_X509_free(ctx->current_cert); - if (*depth < origDepth) - *depth += 1; -} - /* Returns 1 if cur and x509 have identical DER encodings, 0 otherwise. */ static int X509DerEquals(WOLFSSL_X509* cur, WOLFSSL_X509* x509) { @@ -793,16 +611,11 @@ static int X509DerEquals(WOLFSSL_X509* cur, WOLFSSL_X509* x509) x509->derCert->length) == 0; } -/* Returns 1 if x509's DER matches an entry in either origTrustedSk (an - * immutable snapshot of the caller's trusted set - store->certs or the - * set0_trusted_stack override - captured before any intermediates were - * injected for this verification call) or in store->trusted. Returns 0 - * otherwise. Used by the X509_V_FLAG_PARTIAL_CHAIN fallback to confirm that - * a chain actually terminates at a caller-trusted certificate. - * NOTE: origTrustedSk is a private snapshot, but store->trusted is read live - * and unlocked here (as it is at the terminal issuer lookup); this mitigation - * is deliberately asymmetric, so a single X509_STORE must not be shared across - * threads verifying concurrently. */ +/* Returns 1 if x509's DER matches an entry in either origTrustedSk (the + * trusted certs of the verification: the trusted stack of the ctx or the + * certs added to the store) or in store->trusted. Returns 0 otherwise. + * NOTE: both are read without a lock, as at the issuer lookups, so an + * X509_STORE must not be changed while it is used to verify. */ static int X509StoreCertIsTrusted(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509, WOLF_STACK_OF(WOLFSSL_X509)* origTrustedSk) { @@ -831,527 +644,922 @@ static int X509StoreCertIsTrusted(WOLFSSL_X509_STORE* store, return 0; } -/* Enforce the BasicConstraints pathLenConstraint (RFC 5280 sec. 4.2.1.9 and - * the path validation rules in sec. 6.1.4 (l)/(m)) over the certification path - * assembled in ctx->chain. - * - * wolfSSL_X509_verify_cert() authenticates each certificate individually via - * the CertManager, which parses every certificate as CERT_TYPE. The issuer - * pathLen check in ParseCertRelative() is gated on a non-CERT_TYPE certificate - * type (it is reached on the TLS handshake path via CHAIN_CERT_TYPE), so the - * OpenSSL-compatibility path never enforced it. Re-create that check here over - * the completed path so that a CA asserting pathlen:N cannot issue more than N - * subordinate intermediate CAs. - * - * ctx->chain is ordered leaf first (index 0) up to the trust anchor (highest - * index). Walk from the trust anchor down toward the leaf, tracking the - * remaining number of non-self-issued intermediate certificates permitted. - * The budget is only enforced once some CA in the path actually asserts a - * pathLenConstraint; an explicit "haveConstraint" flag tracks that, so every - * value 0..WOLFSSL_MAX_PATH_LEN (the parser's hard cap on pathLenConstraint) - * is a usable budget rather than overloading the cap as a "no constraint" - * sentinel. The leaf (index 0) issues nothing and is therefore not subject to - * the constraint. - * - * Returns WOLFSSL_SUCCESS if the path satisfies every pathLenConstraint, or - * WOLFSSL_FAILURE (with ctx->error set) on the first violation. */ +/* Returns the store whose roots ctx trusts. As in OpenSSL, a trusted stack + * replaces them. */ +static WOLFSSL_X509_STORE* X509StoreRoots(WOLFSSL_X509_STORE_CTX* ctx) +{ + return (ctx->setTrustedSk != NULL) ? NULL : ctx->store; +} + +/* As OpenSSL's check_extensions(): the pathLenConstraint of a CA bounds the + * non-self-issued certs below it, the leaf not counted (RFC 5280 6.1.4 (l) + * and (m)). The error is reported at that CA. + * Returns WOLFSSL_SUCCESS, or WOLFSSL_FAILURE when the verify callback does + * not accept an error. */ static int X509StoreCheckPathLen(WOLFSSL_X509_STORE_CTX* ctx, WOLFSSL_X509_STORE_CTX_verify_cb verifyCb) { int num; int i; - word32 maxPathLen = 0; - byte haveConstraint = 0; - WOLFSSL_X509* anchor; + word32 plen = 0; if (ctx == NULL || ctx->chain == NULL) return WOLFSSL_SUCCESS; - num = wolfSSL_sk_X509_num(ctx->chain); - /* A pathLen violation requires at least one intermediate between the leaf - * (index 0) and the trust anchor, i.e. a chain of three or more. */ - if (num < 3) - return WOLFSSL_SUCCESS; + num = wolfSSL_sk_X509_num(ctx->chain); + for (i = 1; i < num; i++) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + + if (x509 == NULL) + continue; + if (i > 1 && x509->isCa && x509->basicConstPlSet && + plen > x509->pathLength) { + ctx->current_cert = x509; + SetupStoreCtxError_ex(ctx, + WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED, i); + if (verifyCb == NULL || verifyCb(0, ctx) != 1) + return WOLFSSL_FAILURE; + } + if (wolfSSL_X509_NAME_cmp(&x509->issuer, &x509->subject) != 0) + plen++; + } + + return WOLFSSL_SUCCESS; +} + +/* Returns 1 if X509_V_FLAG_PARTIAL_CHAIN is set on the param of ctx, as + * OpenSSL reads it, otherwise 0. Init inherits the store's flags and + * X509_STORE_CTX_set0_param() replaces them. ctx and ctx->store must be + * non-NULL. */ +static int X509StoreCertPartialChainEnabled(WOLFSSL_X509_STORE_CTX* ctx) +{ + WOLFSSL_X509_VERIFY_PARAM* param = ctx->param; + + /* Without a param, the store's, as init would inherit */ + if (param == NULL) { + param = ctx->store->param; + } + return param != NULL && (param->flags & WOLFSSL_PARTIAL_CHAIN) != 0; +} + +/* Returns 1 if x509 can issue certs as OpenSSL's X509_check_ca() decides, + * with keyCertSign, as for a cert above the leaf. keyUsage, if present, must + * allow cert signing and basicConstraints, if present, must say CA. Without + * basicConstraints only the top cert of the chain can be a CA: a v1 + * self-signed cert or one with keyUsage. X509_V_FLAG_X509_STRICT drops that + * exception, as OpenSSL does. */ +static int X509StoreCertIsCa(WOLFSSL_X509_STORE_CTX* ctx, WOLFSSL_X509* x509, + int top) +{ + if (x509->keyUsageSet && (x509->keyUsage & KEYUSE_KEY_CERT_SIGN) == 0) { + return 0; + } + if (x509->basicConstSet) { + return x509->isCa; + } + if (!top || (ctx->param != NULL && + (ctx->param->flags & WOLFSSL_X509_STRICT) != 0)) { + return 0; + } + return x509->keyUsageSet || (x509->version == 1 && + wolfSSL_X509_check_issued(x509, x509) == WOLFSSL_X509_V_OK); +} + +/* Returns 1 if x509, an issuer on the chain, need not be a CA: with + * WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE, an untrusted cert without + * CA:TRUE. Otherwise 0. */ +static int X509StoreNonCaAllowed(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509* x509, WOLF_STACK_OF(WOLFSSL_X509)* trustedSk) +{ +#ifdef WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE + return !x509->isCa && X509StoreCertInStack(ctx->ctxIntermediates, x509) && + !X509StoreCertIsTrusted(X509StoreRoots(ctx), x509, trustedSk); +#else + (void)ctx; + (void)x509; + (void)trustedSk; + return 0; +#endif +} + +/* Returns 1 if ca was loaded by the application. As in OpenSSL, whose store + * holds only the certs added to it, a CA cached from a TLS peer's chain is + * not a trust anchor. */ +static int X509StoreCmCaLoaded(const Signer* ca) +{ + return ca != NULL && ca->type != WOLFSSL_CHAIN_CA && + ca->type != WOLFSSL_TEMP_CA; +} + +/* Returns 1 when ret, of ParseCertRelative() with a cert manager or of + * CheckCertSignaturePubKey(), is the result of verifying a cert, a + * signature algorithm not supported included. The cert decoded before, so + * another error is internal, as running out of memory. */ +static int X509StoreVerifyResult(int ret) +{ + return ret == 0 || ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) || + ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E) || + ret == WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E) || + ret == WC_NO_ERR_TRACE(ASN_SIG_OID_E) || + ret == WC_NO_ERR_TRACE(ASN_SIG_KEY_E) || + ret == WC_NO_ERR_TRACE(ASN_SIG_HASH_E) || + ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E) || + ret == WC_NO_ERR_TRACE(HASH_TYPE_E) || + ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN) || + ret == WC_NO_ERR_TRACE(BUFFER_E) || + ret == WC_NO_ERR_TRACE(ASN_NAME_INVALID_E) || + ret == WC_NO_ERR_TRACE(ASN_PATHLEN_SIZE_E); +} + +/* Verifies x509, dates aside, with a CA the application loaded into the cert + * manager, which goes in *issuer when it has its DER and its key signed + * x509. Returns 0 or the verify error. An internal error is kept in *err. */ +static int X509StoreCmVerify(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509* x509, + WOLFSSL_X509** issuer, int* err) +{ + int ret; + WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + + WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT, + return X509StoreKeepErr(err, MEMORY_E)); + InitDecodedCert(cert, x509->derCert->buffer, x509->derCert->length, + cm->heap); + ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_SKIP_DATE, cm, NULL); + if (!X509StoreVerifyResult(ret)) { + (void)X509StoreKeepErr(err, ret); + } + /* The CA verified it: as OpenSSL, its name constraints are checked on + * the chain */ + if (ret == WC_NO_ERR_TRACE(ASN_NAME_INVALID_E)) { + ret = 0; + } + if (ret == 0 && !X509StoreCmCaLoaded(cert->ca)) { + ret = WC_NO_ERR_TRACE(ASN_NO_SIGNER_E); + } +#ifdef WOLFSSL_SIGNER_DER_CERT + /* A signer restored from a cert cache has no DER */ + if (ret == 0 && issuer != NULL && cert->ca->derCert != NULL) { + WOLFSSL_X509* ca = NULL; + + if (wolfSSL_X509_d2i_ex(&ca, cert->ca->derCert->buffer, + (int)cert->ca->derCert->length, cm->heap) == NULL) { + (void)X509StoreKeepErr(err, WOLFSSL_FATAL_ERROR); + } + else if (X509StoreKeySigned(ca, x509, err)) { + *issuer = ca; + ca = NULL; + } + wolfSSL_X509_free(ca); + } +#else + (void)issuer; +#endif + FreeDecodedCert(cert); + WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT); + + return ret; +} + +/* Returns 1 if a CA the application loaded into the cert manager verifies + * x509, dates aside, otherwise 0. An internal error is kept in *err. */ +static int X509StoreCmHasIssuer(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509* x509, + int* err) +{ + return X509StoreCmVerify(cm, x509, NULL, err) == 0; +} + +/* Returns 1 if x509 is a self-signed CA the application loaded into the cert + * manager of a store that keeps its CAs only there, as an SSL_CTX's store: the + * CA has its key and, when the CA keeps its DER, the same DER. Otherwise 0. + * An internal error is kept in *err. */ +static int X509StoreCmHasCert(WOLFSSL_X509_STORE_CTX* ctx, WOLFSSL_X509* x509, + int* err) +{ + WOLFSSL_CERT_MANAGER* cm = ctx->store->cm; + int ret; + WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + + if (ctx->setTrustedSk != NULL || ctx->store->trusted != NULL || + x509->derCert == NULL) { + return 0; + } + WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT, + { (void)X509StoreKeepErr(err, MEMORY_E); return 0; }); + InitDecodedCert(cert, x509->derCert->buffer, x509->derCert->length, + cm->heap); + ret = ParseCertRelative(cert, CERT_TYPE, VERIFY_SKIP_DATE, cm, NULL); + if (!X509StoreVerifyResult(ret)) { + (void)X509StoreKeepErr(err, ret); + } + ret = ret == 0 && X509StoreCmCaLoaded(cert->ca); +#ifdef WOLFSSL_SIGNER_DER_CERT + if (ret && cert->ca != NULL && cert->ca->derCert != NULL) { + ret = cert->ca->derCert->length == x509->derCert->length && + XMEMCMP(cert->ca->derCert->buffer, x509->derCert->buffer, + x509->derCert->length) == 0; + } +#endif + FreeDecodedCert(cert); + WC_FREE_VAR_EX(cert, cm->heap, DYNAMIC_TYPE_DCERT); + + return ret; +} + +#if defined(SESSION_CERTS) && defined(KEEP_PEER_CERT) +/* Returns 1 if ca, above x509 on a chain, is the CA the application loaded + * into the cert manager that verifies x509, dates aside: the same cert, by + * its DER. Otherwise 0. */ +static int X509StoreCmIsIssuer(WOLFSSL_CERT_MANAGER* cm, WOLFSSL_X509* ca, + WOLFSSL_X509* x509) +{ + int err = 0; + int ret = 0; + WOLFSSL_X509* issuer = NULL; + + if (cm == NULL || ca == NULL || x509 == NULL || x509->derCert == NULL) { + return 0; + } + if (X509StoreCmVerify(cm, x509, &issuer, &err) == 0 && err == 0) { + ret = X509DerEquals(issuer, ca); + } + wolfSSL_X509_free(issuer); + + return ret; +} +#endif + +/* As OpenSSL's check_chain_extensions(): every cert above the leaf issues + * the one below it, so it must be a CA that may sign certs. num is the + * number of chain certs to check. + * Returns WOLFSSL_SUCCESS, or WOLFSSL_FAILURE when the verify callback does + * not accept an error. */ +static int X509StoreCheckChainCa(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, + WOLF_STACK_OF(WOLFSSL_X509)* trustedSk, int num) +{ + int i; + + for (i = 1; i < num; i++) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + + if (x509 == NULL || X509StoreNonCaAllowed(ctx, x509, trustedSk) || + X509StoreCertIsCa(ctx, x509, i == num - 1)) { + continue; + } + ctx->current_cert = x509; + SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_INVALID_CA, i); + if (verifyCb == NULL || verifyCb(0, ctx) != 1) { + return WOLFSSL_FAILURE; + } + } + return WOLFSSL_SUCCESS; +} + +/* As OpenSSL's find_issuer(), picks from certs a cert that issued x, its + * names and key ids matching: the first one valid at the verification time, + * else the one that expires last, as *best when it expires later. Its key + * is not checked. With skipChain, certs on the chain are not picked. + * Failing closed, a cert without its DER is not picked, so that every cert + * on the chain has it and is checked. + * Returns 1 when *best is valid at the verification time, else 0. */ +static int X509StorePickIssuer(WOLFSSL_X509_STORE_CTX* ctx, + WOLF_STACK_OF(WOLFSSL_X509)* certs, WOLFSSL_X509* x, int skipChain, + WOLFSSL_X509** best) +{ + int i; + + for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) { + WOLFSSL_X509* ca = wolfSSL_sk_X509_value(certs, i); + + if (ca == NULL || ca->derCert == NULL || (skipChain && + X509StoreCertInStack(ctx->chain, ca)) || + !X509StoreIssued(ca, x)) { + continue; + } + #ifndef NO_ASN_TIME + if (X509StoreCheckCertDate(ctx, ca) != WOLFSSL_SUCCESS) { + if (*best == NULL || wolfSSL_ASN1_TIME_compare(&ca->notAfter, + &(*best)->notAfter) > 0) { + *best = ca; + } + continue; + } + #endif + *best = ca; + return 1; + } + return 0; +} + +/* Returns the trusted cert that issued x, as OpenSSL's + * X509_STORE_CTX_get1_issuer() picks it: certs on the chain included. As in + * OpenSSL, a trusted stack replaces the store's trusted certs. */ +static WOLFSSL_X509* X509StoreFindTrusted(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509* x) +{ + WOLFSSL_X509* ca = NULL; + + (void)X509StorePickIssuer(ctx, (ctx->setTrustedSk != NULL) ? + ctx->setTrustedSk : ctx->store->added, x, 0, &ca); + return ca; +} + +/* As OpenSSL looks for an alternate chain: the highest untrusted cert below + * the top untrusted one, the numUntrusted certs from the leaf being + * untrusted, that a trusted cert or a CA of the cert manager issued. + * Returns its depth, or -1 when there is none. *alt is that trusted cert, + * NULL for a CA of the cert manager. An internal error is kept in *err. */ +static int X509StoreTrustedAlt(WOLFSSL_X509_STORE_CTX* ctx, int numUntrusted, + WOLFSSL_X509** alt, int* err) +{ + int i; + + for (i = numUntrusted - 2; i >= 0; i--) { + WOLFSSL_X509* x = wolfSSL_sk_X509_value(ctx->chain, i); + + *alt = X509StoreFindTrusted(ctx, x); + if (*alt != NULL || (ctx->setTrustedSk == NULL && + X509StoreCmHasIssuer(ctx->store->cm, x, err))) { + return i; + } + } + *alt = NULL; + return -1; +} + +/* As OpenSSL, an internal error is not a verify error: the verify callback + * is not asked and the verification fails, with X509_V_ERR_OUT_OF_MEM when + * memory ran out, else X509_V_ERR_UNSPECIFIED. + * Returns WOLFSSL_FAILURE. */ +static int X509StoreFatal(WOLFSSL_X509_STORE_CTX* ctx, int err) +{ + ctx->error = (err == WC_NO_ERR_TRACE(MEMORY_E) || + err == WC_NO_ERR_TRACE(MEMORY_ERROR)) ? + WOLFSSL_X509_V_ERR_OUT_OF_MEM : WOLFSSL_X509_V_ERR_UNSPECIFIED; + return WOLFSSL_FAILURE; +} + +/* Records err for x at depth and asks the verify callback. + * Returns 1 when the callback accepts the error. */ +static int X509StoreReport(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, WOLFSSL_X509* x, int err, + int depth) +{ + ctx->current_cert = x; + SetupStoreCtxError_ex(ctx, err, depth); + return verifyCb != NULL && verifyCb(0, ctx) == 1; +} + +/* As OpenSSL's check_auth_level(): the key of each issuer, the trust anchor + * included, must meet the cert manager's minimum CA key size. + * Returns WOLFSSL_SUCCESS, or WOLFSSL_FAILURE when the verify callback does + * not accept an error. */ +static int X509StoreCheckChainKeys(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, int num) +{ + int i; + + for (i = 1; i < num; i++) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + + if (x509 != NULL && CheckCAKeySize(ctx->store->cm, + (word32)x509->pubKeyOID, x509->pubKey.buffer, + x509->pubKey.length) != 0 && + !X509StoreReport(ctx, verifyCb, x509, + WOLFSSL_X509_V_ERR_CA_KEY_TOO_SMALL, i)) { + return WOLFSSL_FAILURE; + } + } + return WOLFSSL_SUCCESS; +} + +/* As OpenSSL's internal_verify(), top down. sigFail holds the certs whose + * issuer did not sign them. Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE. */ +static int X509StoreCheckChainCerts(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, + WOLF_STACK_OF(WOLFSSL_X509)* trustedSk, int num, int noIssuer, + WOLF_STACK_OF(WOLFSSL_X509)* sigFail) +{ + int i = num - 1; + + if (noIssuer && !X509StoreCertPartialChainEnabled(ctx)) { + if (i > 0) { + i--; + } + else if (!X509StoreReport(ctx, verifyCb, + wolfSSL_sk_X509_value(ctx->chain, 0), + WOLFSSL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, 0)) { + return WOLFSSL_FAILURE; + } + } + for (; i >= 0; i--) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + WOLFSSL_X509* up = (i + 1 < num) ? + wolfSSL_sk_X509_value(ctx->chain, i + 1) : NULL; + #ifndef NO_ASN_TIME + int ret; + #endif + + /* RFC 5280 6.1.4 (n): the issuer's keyUsage must allow keyCertSign */ + if (up != NULL && up->keyUsageSet && + (up->keyUsage & KEYUSE_KEY_CERT_SIGN) == 0 && + !X509StoreNonCaAllowed(ctx, up, trustedSk) && + !X509StoreReport(ctx, verifyCb, up, + WOLFSSL_X509_V_ERR_KEYUSAGE_NO_CERTSIGN, i + 1)) { + return WOLFSSL_FAILURE; + } + if (X509StoreCertInStack(sigFail, x509) && + !X509StoreReport(ctx, verifyCb, x509, + WOLFSSL_X509_V_ERR_CERT_SIGNATURE_FAILURE, i)) { + return WOLFSSL_FAILURE; + } + #ifndef NO_ASN_TIME + + if (x509 != NULL) { + ret = X509StoreCheckCertDate(ctx, x509); + if (ret != WOLFSSL_SUCCESS && + !X509StoreReport(ctx, verifyCb, x509, ret, i)) { + return WOLFSSL_FAILURE; + } + } + #endif + ctx->current_cert = x509; + ctx->error_depth = i; + if (verifyCb != NULL && verifyCb(1, ctx) != 1) { + return WOLFSSL_FAILURE; + } + } + return WOLFSSL_SUCCESS; +} + +#ifndef IGNORE_NAME_CONSTRAINTS +/* As OpenSSL's check_name_constraints(): the names of each cert, but a + * self-issued one above the leaf, meet the name constraints of every cert + * above it on the chain, the anchor included. + * Returns WOLFSSL_SUCCESS, or WOLFSSL_FAILURE when the verify callback does + * not accept an error. */ +static int X509StoreCheckChainNames(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, int num) +{ + int i; + int j; + int ret; + + for (i = num - 1; i >= 0; i--) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + + if (x509 == NULL || (i > 0 && + wolfSSL_X509_NAME_cmp(&x509->issuer, &x509->subject) == 0)) { + continue; + } + for (j = num - 1; j > i; j--) { + WOLFSSL_X509* ca = wolfSSL_sk_X509_value(ctx->chain, j); + int violation = 0; + + if (ca == NULL || (ca->permittedNames == NULL && + ca->excludedNames == NULL && !ca->nameConstraintCrit)) { + continue; + } + ret = CheckCertNameConstraints(ca->derCert->buffer, + ca->derCert->length, x509->derCert->buffer, + x509->derCert->length, i == 0, ctx->heap, &violation); + if (ret != 0) { + return X509StoreFatal(ctx, ret); + } + if (violation != 0 && !X509StoreReport(ctx, verifyCb, x509, + (violation == NC_VIOLATION_EXCLUDED) ? + WOLFSSL_X509_V_ERR_EXCLUDED_VIOLATION : + (violation == NC_VIOLATION_PERMITTED) ? + WOLFSSL_X509_V_ERR_PERMITTED_VIOLATION : + WOLFSSL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, i)) { + return WOLFSSL_FAILURE; + } + } + } + return WOLFSSL_SUCCESS; +} +#endif /* IGNORE_NAME_CONSTRAINTS */ + +#ifdef HAVE_CRL +/* As OpenSSL's get_crl_delta(), checks the cert with a CRL of up, its issuer, + * of the ctx, else of the store. staleOk and anyKeyUsage accept one out of + * date or without cRLSign. Returns WOLFSSL_SUCCESS or the CRL error. */ +static int X509StoreCheckCertCrls(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509* up, int staleOk, int anyKeyUsage) +{ + int ret = WC_NO_ERR_TRACE(CRL_MISSING); + Signer issuer; +#ifndef NO_ASN_TIME + time_t t; + const time_t* checkTime = (!staleOk && X509StoreCheckTime(ctx, &t)) ? + &t : NULL; +#else + const time_t* checkTime = NULL; + + (void)staleOk; +#endif - /* The trust anchor (top of chain) is not part of the prospective - * certification path (RFC 5280 sec. 6.1): it does not consume path-length - * budget, and the loop below runs from num-2 down to 1 so the anchor is - * never processed as an intermediate. A self-signed anchor that asserts its - * own pathLenConstraint does still bound the path, matching - * ParseCertRelative()'s trust-anchor handling, so seed the budget from - * it. */ - anchor = wolfSSL_sk_X509_value(ctx->chain, num - 1); - if (anchor != NULL && anchor->isCa && anchor->basicConstPlSet) { - maxPathLen = (word32)anchor->pathLength; - haveConstraint = 1; + XMEMSET(&issuer, 0, sizeof(issuer)); + issuer.publicKey = up->pubKey.buffer; + issuer.pubKeySize = up->pubKey.length; + issuer.keyOID = (word32)up->pubKeyOID; + /* Without the extension any key usage is allowed */ + issuer.keyUsage = (up->keyUsageSet && !anyKeyUsage) ? + up->keyUsage : 0xFFFF; +#ifndef NO_SKID + /* Its subject key id, if any, picks out its CRLs */ + if (up->subjKeyIdSet && up->subjKeyId != NULL && + GetHashId(up->subjKeyId, (int)up->subjKeyIdSz, + issuer.subjectKeyIdHash, + HashIdAlg((word32)up->sigOID)) != 0) { + XMEMSET(issuer.subjectKeyIdHash, 0, + sizeof(issuer.subjectKeyIdHash)); + } +#endif + if (ctx->crls != NULL) { + ret = X509StoreCheckCtxCrls(ctx, &issuer, checkTime); + } + if (ret == WC_NO_ERR_TRACE(CRL_MISSING) && ctx->store->cm->crl != NULL) { + ret = X509StoreCheckStoreCrl(ctx, &issuer, checkTime); } + return ret; +} +#endif /* HAVE_CRL */ - for (i = num - 2; i >= 1; i--) { - WOLFSSL_X509* cert = wolfSSL_sk_X509_value(ctx->chain, i); - int selfIssued; +/* As OpenSSL's check_revocation(): the leaf, or every cert with check-all. + * Check-all of the cert manager, as TLS, leaves the trust anchor out. + * Returns WOLFSSL_SUCCESS, or WOLFSSL_FAILURE when the verify callback does + * not accept an error. */ +static int X509StoreCheckChainCrls(WOLFSSL_X509_STORE_CTX* ctx, + WOLFSSL_X509_STORE_CTX_verify_cb verifyCb, int num, int noIssuer) +{ + unsigned long flags = (ctx->param != NULL) ? ctx->param->flags : 0; +#ifdef HAVE_CRL + WOLFSSL_CERT_MANAGER* cm = ctx->store->cm; + /* Enabled on the cert manager, or as OpenSSL by the verify param */ + int check = cm->crlEnabled || + (flags & WOLFSSL_X509_V_FLAG_CRL_CHECK) != 0; + int checkAll = (cm->crlEnabled && cm->crlCheckAll) || + (flags & WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL) != 0; +#else + int check = (flags & WOLFSSL_X509_V_FLAG_CRL_CHECK) != 0; + int checkAll = (flags & WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL) != 0; +#endif + int i; + int ret; - if (cert == NULL) - continue; + for (i = 0; check && (i == 0 || checkAll) && i < num; i++) { + WOLFSSL_X509* x509 = wolfSSL_sk_X509_value(ctx->chain, i); + WOLFSSL_X509* up = NULL; - selfIssued = - (wolfSSL_X509_NAME_cmp(&cert->issuer, &cert->subject) == 0); - - /* RFC 5280 sec. 6.1.4 (l): a non-self-issued *CA* certificate consumes - * one unit of the issuer's remaining path length budget. Gate on isCa - * to match ParseCertRelative() (wolfcrypt/src/asn.c) and the (m) step - * below, so a non-CA intermediate tolerated via verify_cb does not - * trigger a false PATH_LENGTH_EXCEEDED. Only meaningful once a CA above - * has asserted a constraint (haveConstraint). */ - if (!selfIssued && cert->isCa && haveConstraint) { - if (maxPathLen == 0) { - SetupStoreCtxError_ex(ctx, - WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED, i); - /* Allow an application verify callback to override, matching - * the INVALID_CA handling in wolfSSL_X509_verify_cert(). */ - if (verifyCb != NULL && verifyCb(0, ctx) == 1) { - /* Overridden: keep walking without decrementing (budget is - * already exhausted). */ - continue; + if (i < num - 1) { + up = wolfSSL_sk_X509_value(ctx->chain, i + 1); + } + else if (!noIssuer && i > 0 && + (flags & WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL) == 0) { + break; + } + else if (!noIssuer && X509StoreIssued(x509, x509)) { + /* As OpenSSL, a self-issued top cert has its own CRLs. Another + * has none. */ + up = x509; + } + ctx->current_cert = x509; + ret = WC_NO_ERR_TRACE(CRL_MISSING); + #ifdef HAVE_CRL + if (up != NULL) { + int anyKeyUsage = 0; + + ret = X509StoreCheckCertCrls(ctx, up, 0, 0); + /* As OpenSSL, a CRL of an issuer without cRLSign is reported. + * Once that is accepted, the CRL is used. */ + if (ret == WC_NO_ERR_TRACE(ASN_CRL_NO_SIGNER_E)) { + if (!X509StoreReport(ctx, verifyCb, ctx->current_cert, + WOLFSSL_X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, i)) { + return WOLFSSL_FAILURE; } - return WOLFSSL_FAILURE; + anyKeyUsage = 1; + ret = X509StoreCheckCertCrls(ctx, up, 0, anyKeyUsage); + } + /* As OpenSSL, once its error is accepted a CRL out of date is + * used */ + if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) || + ret == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR)) { + if (!X509StoreReport(ctx, verifyCb, ctx->current_cert, + (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) ? + WOLFSSL_X509_V_ERR_CRL_NOT_YET_VALID : + WOLFSSL_X509_V_ERR_CRL_HAS_EXPIRED, i)) { + return WOLFSSL_FAILURE; + } + ret = X509StoreCheckCertCrls(ctx, up, 1, anyKeyUsage); } - maxPathLen--; } - - /* RFC 5280 sec. 6.1.4 (m): tighten the budget with this CA's own - * pathLenConstraint, if present. The first constraint encountered seeds - * the budget; subsequent ones only ever lower it. */ - if (cert->isCa && cert->basicConstPlSet && - (!haveConstraint || (word32)cert->pathLength < maxPathLen)) { - maxPathLen = (word32)cert->pathLength; - haveConstraint = 1; + #else + (void)up; + #endif + /* Any other error is internal */ + if (ret != WOLFSSL_SUCCESS && ret != WC_NO_ERR_TRACE(CRL_MISSING) && + ret != WC_NO_ERR_TRACE(CRL_CERT_REVOKED) && + ret != WC_NO_ERR_TRACE(ASN_CRL_CONFIRM_E)) { + return X509StoreFatal(ctx, ret); + } + /* A CRL that does not verify is not used. Its entries are not + * checked, so failing closed its error is final. */ + if (ret != WOLFSSL_SUCCESS && + (!X509StoreReport(ctx, verifyCb, ctx->current_cert, ret, i) || + ret == WC_NO_ERR_TRACE(ASN_CRL_CONFIRM_E))) { + return WOLFSSL_FAILURE; } } - return WOLFSSL_SUCCESS; } -/* Returns 1 if X509_V_FLAG_PARTIAL_CHAIN is set on ctx or on its store, - * otherwise 0. ctx and ctx->store must be non-NULL. */ -static int X509StoreCertPartialChainEnabled(WOLFSSL_X509_STORE_CTX* ctx) -{ - /* PARTIAL_CHAIN lets any trusted cert end the path */ - if (ctx->flags & WOLFSSL_PARTIAL_CHAIN) { - return 1; - } - if (ctx->store->param != NULL && - (ctx->store->param->flags & WOLFSSL_PARTIAL_CHAIN)) { - return 1; - } - return 0; -} - /* Verifies certificate chain using WOLFSSL_X509_STORE_CTX * returns 1 on success or <= 0 on failure. - */ + * Builds and checks the chain as OpenSSL's build_chain() and verify_chain(), + * failing closed: the first issuer found is final, and an alternate chain is + * taken only when its trusted cert is the same cert. A CA of the cert + * manager is trusted and, as in the handshake, needs no issuer there. */ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) { - int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); - int done = 0; - int depth = 0; - int origDepth = 0; - int cbRejected = 0; - WOLFSSL_X509 *issuer = NULL; - WOLFSSL_X509 *orig = NULL; - WOLF_STACK_OF(WOLFSSL_X509)* callerTrusted = NULL; - WOLF_STACK_OF(WOLFSSL_X509)* certsToUse = NULL; - WOLF_STACK_OF(WOLFSSL_X509)* failedCerts = NULL; - WOLF_STACK_OF(WOLFSSL_X509)* origTrustedSk = NULL; + int ret = WOLFSSL_SUCCESS; + int depth; + int num; + int trustedFirst; + int trustedSeen = 0; + int fromTrusted = 0; + int numUntrusted = 1; + int noIssuer = 0; + int cmRet; + int fromCm = 0; + int err = 0; + WOLF_STACK_OF(WOLFSSL_X509)* sigFail = NULL; + WOLFSSL_X509* x; + WOLFSSL_X509* issuer; + WOLFSSL_X509* cmIssuer = NULL; + WOLFSSL_X509* orig; + WOLF_STACK_OF(WOLFSSL_X509)* trustedSk; WOLFSSL_X509_STORE_CTX_verify_cb verifyCb; + WOLFSSL_ENTER("wolfSSL_X509_verify_cert"); - if (ctx == NULL || ctx->store == NULL || ctx->store->cm == NULL - || ctx->current_cert == NULL || ctx->current_cert->derCert == NULL) { + if (ctx == NULL) { + return WOLFSSL_FATAL_ERROR; + } + if (ctx->store == NULL || ctx->store->cm == NULL || + ctx->current_cert == NULL || ctx->current_cert->derCert == NULL) { + ctx->error = WOLFSSL_X509_V_ERR_UNSPECIFIED; return WOLFSSL_FATAL_ERROR; } verifyCb = X509StoreGetVerifyCb(ctx); - - /* Chain building mutates the working stack: caller-supplied intermediates - * are appended and X509VerifyCertSetupRetry moves failed certs out of it. - * store->certs is shared by every connection using this store and - * setTrustedSk is owned by the caller, so build a per-verification shallow - * copy (certsToUse) and leave both untouched. This removes the write-side - * corruption a concurrent verification used to inflict on those stacks, but - * it does NOT make concurrent use of one X509_STORE safe: the dup is itself - * an unlocked read, and store->trusted is still walked live (see the NOTE - * below). A single store must not be shared across threads that verify - * concurrently. - * - * The X509_V_FLAG_PARTIAL_CHAIN fallback needs the set of certs that were - * caller-trusted before any intermediates were injected. Snapshot it from - * certsToUse - the private copy, taken before addAllButSelfSigned() injects - * intermediates - rather than walking the shared stack a second time, so - * the two snapshots are provably identical. Both dups hold borrowed - * references and are shallow-freed at exit. */ - callerTrusted = ctx->store->certs; - if (ctx->setTrustedSk != NULL) { - callerTrusted = ctx->setTrustedSk; - } - - if (callerTrusted != NULL) { - certsToUse = wolfSSL_shallow_sk_dup(callerTrusted); - if (certsToUse != NULL) - origTrustedSk = wolfSSL_shallow_sk_dup(certsToUse); - if (origTrustedSk == NULL) { - ret = WOLFSSL_FAILURE; - goto exit; - } - } - else { - certsToUse = wolfSSL_sk_X509_new_null(); - } - if (certsToUse == NULL) { - ret = WOLFSSL_FAILURE; - goto exit; - } - /* Add the intermediates provided on init to the list of untrusted - * intermediates to be used. */ - ret = addAllButSelfSigned(certsToUse, ctx->ctxIntermediates, NULL); - if (ret != WOLFSSL_SUCCESS) { - goto exit; - } + /* Without a param, the default of init */ + trustedFirst = ctx->param == NULL || + (ctx->param->flags & WOLFSSL_TRUSTED_FIRST) != 0; + trustedSk = (ctx->setTrustedSk != NULL) ? ctx->setTrustedSk : + ctx->store->certs; + depth = (ctx->param != NULL) ? ctx->param->depth : + WOLFSSL_X509_STORE_DEFAULT_MAX_DEPTH; + orig = ctx->current_cert; if (ctx->chain != NULL) { wolfSSL_sk_X509_pop_free(ctx->chain, NULL); } ctx->chain = wolfSSL_sk_X509_new_null(); if (ctx->chain == NULL) { - ret = WOLFSSL_FAILURE; + err = MEMORY_E; goto exit; } - - failedCerts = wolfSSL_sk_X509_new_null(); - if (!failedCerts) { - /* Fail closed: ret is still WOLFSSL_SUCCESS from the checks above, so - * an unset error here would make the function report a verified chain - * after an allocation failure. */ - ret = WOLFSSL_FAILURE; + x = orig; + if (X509StoreKeepErr(&err, X509StoreChainPush(ctx->chain, x)) != + WOLFSSL_SUCCESS) { goto exit; } - /* An explicit negative depth leaves no room for any chain, - * not even a trusted leaf: reject before building one. */ - if (ctx->depth < 0) { - SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, 0); + /* A negative depth, unset included, leaves no room for any chain. As + * OpenSSL, the callback is told; failing closed, the error is final. */ + if (depth < 0) { + (void)X509StoreReport(ctx, verifyCb, x, + WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, 0); ret = WOLFSSL_FAILURE; goto exit; } - /* The struct is public, so also honor a positive depth written directly - * (no setter). Clamp so the + 1 can't overflow. */ - depth = (ctx->depth < INT_MAX) ? ctx->depth + 1 : INT_MAX; + for (;;) { + /* As OpenSSL, self-signed by names and key ids, key not checked */ + int selfSigned = X509StoreIssued(x, x); + /* As in OpenSSL, a cert found among the trusted certs, or a + * self-signed cert that is the trusted cert picked as its issuer */ + int trusted = fromTrusted || (selfSigned && + X509DerEquals(X509StoreFindTrusted(ctx, x), x)); + WOLFSSL_X509* alt = NULL; + int altDepth = -1; - orig = ctx->current_cert; - origDepth = depth; - while(done == 0 && depth > 0) { - issuer = NULL; + num = wolfSSL_sk_X509_num(ctx->chain); + trustedSeen |= trusted; + /* As in OpenSSL, the self-signature of a trusted cert is not + * checked */ + if (trusted && (selfSigned || X509StoreCertPartialChainEnabled(ctx))) { + break; + } + /* A self-signed CA of the cert manager is a trust anchor */ + if (selfSigned && X509StoreCmHasCert(ctx, x, &err)) { + break; + } - /* Try to find an untrusted issuer first. Skip certs already on the - * path so an issuer cycle (A <- B <- A) ends the search instead of - * running until the depth budget is spent. current_cert is not on - * ctx->chain yet, so a self-issued terminus is still found. */ - ret = X509StoreGetIssuerSkip(&issuer, certsToUse, - ctx->current_cert, ctx->chain); - if (ret == WOLFSSL_SUCCESS) { - if (ctx->current_cert == issuer) { - X509StoreChainPush(ctx->chain, ctx->current_cert); - break; + issuer = NULL; + /* 1 until the cert manager is asked, once, for x */ + cmRet = 1; + fromTrusted = 0; + if (!selfSigned) { + /* The cert manager's CAs are trusted, searched after the trusted + * certs */ + if (trustedSeen || trustedFirst) { + issuer = X509StoreFindTrusted(ctx, x); + fromTrusted = issuer != NULL; + if (issuer == NULL && ctx->setTrustedSk == NULL) { + cmRet = X509StoreCmVerify(ctx->store->cm, x, &cmIssuer, + &err); + } } - - /* We found our issuer in the non-trusted cert list, add it - * to the CM and verify the current cert against it */ - #ifndef WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE - /* RFC 5280 4.2.1.9: reject non-CA issuer. verify_cb may - * suppress the INVALID_CA error to keep building the chain, - * but the leaf signature must still be verified against the - * issuer below - never skip X509StoreVerifyCert. */ - if (!issuer->isCa) { - /* error depth is current depth + 1 */ - SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_INVALID_CA, - (ctx->chain) ? (int)(ctx->chain->num + 1) : 1); - if (verifyCb != NULL) { - ret = verifyCb(0, ctx); - if (ret != WOLFSSL_SUCCESS) { - ret = WOLFSSL_FAILURE; - goto exit; - } + if (issuer == NULL && cmRet != 0 && !trustedSeen) { + (void)X509StorePickIssuer(ctx, ctx->ctxIntermediates, x, 1, + &issuer); + if (issuer != NULL) { + numUntrusted++; } - else { - ret = WOLFSSL_FAILURE; - goto exit; + else if (!trustedFirst) { + issuer = X509StoreFindTrusted(ctx, x); + fromTrusted = issuer != NULL; + if (issuer == NULL && ctx->setTrustedSk == NULL) { + cmRet = X509StoreCmVerify(ctx->store->cm, x, + &cmIssuer, &err); + } } } - #endif - /* The CertManager picks the signer itself, by key id or name, from - * everything it holds. Drop the intermediates loaded for the links - * below and for abandoned candidates, so that issuer is the only - * untrusted signer this link can verify against. Otherwise a cert - * signed by one of those would pass here while the path goes on - * with issuer, a same-named cert that never signed it. */ - if (wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm) - != WOLFSSL_SUCCESS) { - ret = WOLFSSL_FATAL_ERROR; - goto exit; - } - /* NOTE: this loads the caller-supplied intermediate into the - * shared ctx->store->cm as a WOLFSSL_TEMP_CA, and the unload paths - * drop *all* WOLFSSL_TEMP_CA signers in that CertManager, not only - * the ones added here. This copy removes the working-stack race, - * but two threads running X509_verify_cert() against the same - * X509_STORE still contend on store->cm. Concurrent verification - * on a single shared store therefore remains unsupported; callers - * needing it must use a store per thread. */ - ret = X509StoreAddCa(ctx->store, issuer, WOLFSSL_TEMP_CA); - if (ret != WOLFSSL_SUCCESS) { - X509VerifyCertSetupRetry(ctx, certsToUse, failedCerts, - &depth, origDepth); - continue; - } - ret = X509StoreVerifyCert(ctx, &cbRejected, verifyCb); - if (cbRejected) { - /* The application vetoed this certificate. Stop instead of - * looking for another issuer: the decision is the - * application's and retrying would only ask it again. */ - ret = WOLFSSL_FAILURE; - goto exit; - } - if (ret != WOLFSSL_SUCCESS) { - X509VerifyCertSetupRetry(ctx, certsToUse, failedCerts, - &depth, origDepth); - continue; + } + /* wolfSSL trusts a CA of the cert manager without an issuer there */ + if (err == 0 && fromCm && issuer == NULL && cmRet != 0) { + if (cmRet == 1) { + cmRet = X509StoreCmVerify(ctx->store->cm, x, NULL, &err); } - /* Add it to the current chain and look at the issuer cert next */ - X509StoreChainPush(ctx->chain, ctx->current_cert); - ctx->current_cert = issuer; - } - else if (ret == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) { - /* Could not find in untrusted list, only place left is - * a trusted CA in the CM. Drop any caller-supplied untrusted - * intermediates that were temporarily loaded into the CertManager - * to authenticate child certificates, so the current certificate - * must verify against a genuinely trusted CA. They must not be - * allowed to anchor the path: otherwise a chain that never reaches - * a configured trust anchor (or whose intermediate signature is - * invalid) would be accepted. */ - if (wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm) - != WOLFSSL_SUCCESS) { - /* Could not guarantee the temporary intermediates were - * dropped; fail closed rather than risk verifying the current - * certificate against one. */ - ret = WOLFSSL_FATAL_ERROR; - goto exit; + if (cmRet == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) || + cmRet == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) { + break; } - ret = X509StoreVerifyCert(ctx, &cbRejected, verifyCb); - if (cbRejected) { - /* An application veto is final. The partial-chain fallback - * below must not accept the chain here and clear ctx->error: - * the certificate verified, the application rejected it. */ - ret = WOLFSSL_FAILURE; + } + if (err != 0) { + goto exit; + } + /* The leaf and the trust anchor do not count against the depth */ + if (!selfSigned && num - 1 > depth) { + (void)X509StoreReport(ctx, verifyCb, x, + WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, num - 1); + ret = WOLFSSL_FAILURE; + goto exit; + } + /* A CA of the cert manager is a trusted cert. Without its DER it + * cannot go on the chain: fail closed as without an issuer. */ + fromCm = cmIssuer != NULL; + if (fromCm) { + x = cmIssuer; + cmIssuer = NULL; + (void)X509StoreKeepErr(&err, X509StoreChainPush(ctx->chain, x)); + /* The chain holds x unless err */ + wolfSSL_X509_free(x); + if (err != 0) { goto exit; } - if (ret != WOLFSSL_SUCCESS) { - /* WOLFSSL_PARTIAL_CHAIN may only terminate the chain at a - * certificate the caller actually trusts, so verify that - * ctx->current_cert is itself in the original trust set. */ - if (X509StoreCertPartialChainEnabled(ctx) && - X509StoreCertIsTrusted(ctx->store, ctx->current_cert, - origTrustedSk)) { - X509StoreChainPush(ctx->chain, ctx->current_cert); - /* Clear error set by the failed X509StoreVerifyCert - * attempt; the partial-chain fallback accepted the - * chain at a caller-trusted certificate. */ - ctx->error = 0; - ret = WOLFSSL_SUCCESS; - /* The caller-trusted certificate terminates the path: - * it is the anchor, so stop here rather than falling - * through to the "finish building the chain" push below, - * which would add ctx->current_cert to ctx->chain a - * second time. Mirrors the self-issued terminus break - * above; the depth>0/done==0 success path accepts it. */ - break; - } else { - X509VerifyCertSetupRetry(ctx, certsToUse, failedCerts, - &depth, origDepth); - continue; - } - } - - /* Cert verified, finish building the chain */ - X509StoreChainPush(ctx->chain, ctx->current_cert); - issuer = NULL; - #ifdef WOLFSSL_SIGNER_DER_CERT - x509GetIssuerFromCM(&issuer, ctx->store->cm, ctx->current_cert); - if (issuer != NULL && ctx->owned != NULL) { - wolfSSL_sk_X509_push(ctx->owned, issuer); - } - #else - /* A candidate that already failed verification (moved to - * failedCerts by the retry path) must not terminate the reported - * chain. setTrustedSk / store->trusted are searched by name+AKID, - * not by signature, and setTrustedSk is no longer pruned during - * chain building, so a same-subject cert that was tried and - * rejected can match here. Skip those entries and keep scanning so - * a genuine anchor further down the stack is still found. */ - if (ctx->setTrustedSk == NULL) { - X509StoreGetIssuerSkip(&issuer, - ctx->store->trusted, ctx->current_cert, failedCerts); - } - else { - X509StoreGetIssuerSkip(&issuer, - ctx->setTrustedSk, ctx->current_cert, failedCerts); - } - #endif - if (issuer != NULL) { - X509StoreChainPush(ctx->chain, issuer); - } - - done = 1; + fromTrusted = 1; + continue; } - else { + /* OpenSSL takes a trusted issuer on the chain again, until the chain + * is too long */ + if (issuer != NULL && X509StoreCertInStack(ctx->chain, issuer)) { + (void)X509StoreReport(ctx, verifyCb, x, + WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, num - 1); + ret = WOLFSSL_FAILURE; goto exit; } - - depth--; - } - - /* Success requires the path to have reached a configured trust anchor - * (done == 1), to have terminated at a caller-trusted self-signed - * certificate via the break above (done == 0 with depth still > 0), or - * to have spent its depth budget exactly on a trust anchor (below). A - * loop that ran out of its depth budget (depth <= 0) without completing - * must fail with CERT_CHAIN_TOO_LONG unless the cert it stopped on is - * caller-trusted and either self-issued or accepted by - * X509_V_FLAG_PARTIAL_CHAIN: the trust anchor never counts against depth. - * ret may still be WOLFSSL_SUCCESS from the last link, so it cannot be - * relied on here. */ - if (ret == WOLFSSL_SUCCESS && done == 0 && depth <= 0) { - int anchorEndsPath = 0; - - /* A trusted anchor from certsToUse is not an intermediate, so accept - * it here if it ends the path instead of counting it against depth. */ - if (X509StoreCertIsTrusted(ctx->store, ctx->current_cert, - origTrustedSk)) { - /* self-issued anchor: ends the path without a verify */ - if (wolfSSL_X509_check_issued(ctx->current_cert, - ctx->current_cert) == WOLFSSL_X509_V_OK) { - anchorEndsPath = 1; - } - else if (X509StoreCertPartialChainEnabled(ctx)) { - /* verify the anchor so the verify callback sees it and - * can veto it. */ - if (wolfSSL_CertManagerUnloadTempIntermediateCerts( - ctx->store->cm) != WOLFSSL_SUCCESS) { - ret = WOLFSSL_FATAL_ERROR; - goto exit; + if (issuer == NULL) { + altDepth = X509StoreTrustedAlt(ctx, numUntrusted, &alt, &err); + /* Failing closed, OpenSSL's alternate chain is only taken when + * its trusted cert is the untrusted cert above, the same cert */ + if (altDepth >= 0 && X509DerEquals(alt, + wolfSSL_sk_X509_value(ctx->chain, altDepth + 1))) { + while (wolfSSL_sk_X509_num(ctx->chain) > altDepth + 1) { + wolfSSL_X509_free(wolfSSL_sk_X509_pop(ctx->chain)); } - ret = X509StoreVerifyCert(ctx, &cbRejected, verifyCb); - if (cbRejected) { - ret = WOLFSSL_FAILURE; - goto exit; + while (wolfSSL_sk_X509_num(sigFail) > 0 && + !X509StoreCertInStack(ctx->chain, + wolfSSL_sk_X509_value(sigFail, + wolfSSL_sk_X509_num(sigFail) - 1))) { + (void)wolfSSL_sk_X509_pop(sigFail); } - if (ret != WOLFSSL_SUCCESS) { - /* the partial chain is accepted at a caller-trusted - * cert; clear the error from the failed verify */ - ctx->error = 0; - } - anchorEndsPath = 1; + numUntrusted = altDepth + 1; + issuer = alt; + fromTrusted = 1; } } - - if (anchorEndsPath) { - ret = X509StoreChainPush(ctx->chain, ctx->current_cert); + if (err != 0) { + goto exit; } - else { - SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, - wolfSSL_sk_X509_num(ctx->chain)); - ret = WOLFSSL_FAILURE; + /* As OpenSSL's last resort, a trusted leaf ends a partial chain + * without a trusted cert */ + if (issuer == NULL && !trustedSeen && altDepth < 0 && + X509StoreCertPartialChainEnabled(ctx) && + X509StoreCertIsTrusted(X509StoreRoots(ctx), orig, + trustedSk)) { + noIssuer = !selfSigned; + break; + } + if (issuer == NULL && selfSigned) { + if (!X509StoreReport(ctx, verifyCb, x, (num == 1) ? + WOLFSSL_X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT : + WOLFSSL_X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, num - 1) || + altDepth >= 0) { + ret = WOLFSSL_FAILURE; + goto exit; + } + break; + } + if (issuer == NULL) { + if (!X509StoreReport(ctx, verifyCb, x, trusted ? + WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT : + WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + num - 1) || altDepth >= 0) { + ret = WOLFSSL_FAILURE; + goto exit; + } + noIssuer = 1; + break; + } + x = wolfSSL_sk_X509_value(ctx->chain, + wolfSSL_sk_X509_num(ctx->chain) - 1); + /* Its signature failure is reported with the chain's signatures */ + if (!X509StoreCertInStack(sigFail, x) && + !X509StoreKeySigned(issuer, x, &err) && err == 0 && + ((sigFail == NULL && + (sigFail = wolfSSL_sk_X509_new_null()) == NULL) || + wolfSSL_sk_X509_push(sigFail, x) <= 0)) { + err = MEMORY_E; + } + if (err != 0 || X509StoreKeepErr(&err, + X509StoreChainPush(ctx->chain, issuer)) != WOLFSSL_SUCCESS) { + goto exit; } + x = issuer; } + num = wolfSSL_sk_X509_num(ctx->chain); - /* RFC 5280 sec. 6.1.4: the per-certificate CertManager verification above - * does not enforce the issuer's BasicConstraints pathLenConstraint on this - * API path, so check it over the assembled path before reporting success. */ + ret = X509StoreCheckChainCa(ctx, verifyCb, trustedSk, num); if (ret == WOLFSSL_SUCCESS) { ret = X509StoreCheckPathLen(ctx, verifyCb); } + if (ret == WOLFSSL_SUCCESS) { + ret = X509StoreCheckChainKeys(ctx, verifyCb, num); + } - /* Enforce hostname / IP verification from X509_VERIFY_PARAM if set. - * Always check against the leaf (end-entity) certificate, captured in - * orig before the chain-building loop modified ctx->current_cert. - * - * A mismatch is reported to the application verify callback the way - * OpenSSL's check_id_error() does: record error, error_depth and - * current_cert, then call it with ok=0, and let a return of 1 override. - * Without that call a callback installed to inspect or override - * verification errors never sees a hostname or IP mismatch. */ - if (ctx->param != NULL) { - if (ret == WOLFSSL_SUCCESS && ctx->param->hostName[0] != '\0') { - if (wolfSSL_X509_check_host(orig, - ctx->param->hostName, - XSTRLEN(ctx->param->hostName), - ctx->param->hostFlags, NULL) != WOLFSSL_SUCCESS) { - ctx->error = WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH; - ctx->error_depth = 0; - ctx->current_cert = orig; - if (verifyCb == NULL || verifyCb(0, ctx) != 1) { - ret = WOLFSSL_FAILURE; - } - } - } - if (ret == WOLFSSL_SUCCESS && ctx->param->ipasc[0] != '\0') { - if (wolfSSL_X509_check_ip_asc(orig, - ctx->param->ipasc, - ctx->param->hostFlags) != WOLFSSL_SUCCESS) { - ctx->error = WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH; - ctx->error_depth = 0; - ctx->current_cert = orig; - if (verifyCb == NULL || verifyCb(0, ctx) != 1) { - ret = WOLFSSL_FAILURE; - } - } - } + /* Host and IP of the leaf, reported as OpenSSL's check_id_error() */ + if (ret == WOLFSSL_SUCCESS && ctx->param != NULL && + ctx->param->hostName[0] != '\0' && + wolfSSL_X509_check_host(orig, ctx->param->hostName, + XSTRLEN(ctx->param->hostName), ctx->param->hostFlags, + NULL) != WOLFSSL_SUCCESS && + !X509StoreReport(ctx, verifyCb, orig, + WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH, 0)) { + ret = WOLFSSL_FAILURE; + } + if (ret == WOLFSSL_SUCCESS && ctx->param != NULL && + ctx->param->ipasc[0] != '\0' && + wolfSSL_X509_check_ip_asc(orig, ctx->param->ipasc, + ctx->param->hostFlags) != WOLFSSL_SUCCESS && + !X509StoreReport(ctx, verifyCb, orig, + WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH, 0)) { + ret = WOLFSSL_FAILURE; } -exit: - /* failedCerts, certsToUse and origTrustedSk hold only borrowed references; - * free the stack nodes, not the certs. All three are per-verification - * stacks (certsToUse/origTrustedSk are shallow dups of the caller's set), - * so none of the caller's own stacks are touched here. */ - wolfSSL_sk_X509_free(failedCerts); - - /* Remove intermediates added to CM. Unconditional: a "did we add one" flag - * is cleared by the same failure paths that leave one resident. Clears all - * TEMP_CAs, so concurrent verifies on one store are unsupported. */ - if (ctx != NULL) { - if (ctx->store != NULL) { - if (wolfSSL_CertManagerUnloadTempIntermediateCerts(ctx->store->cm) - != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("Failed to unload temporary intermediates"); - /* Residue would anchor later verifications. */ - if (ret == WOLFSSL_SUCCESS) - ret = WOLFSSL_FAILURE; - } - } - if (orig != NULL) { - ctx->current_cert = orig; - } + if (ret == WOLFSSL_SUCCESS) { + ret = X509StoreCheckChainCrls(ctx, verifyCb, num, noIssuer); + } + if (ret == WOLFSSL_SUCCESS) { + ret = X509StoreCheckChainCerts(ctx, verifyCb, trustedSk, num, + noIssuer, sigFail); + } +#ifndef IGNORE_NAME_CONSTRAINTS + if (ret == WOLFSSL_SUCCESS) { + ret = X509StoreCheckChainNames(ctx, verifyCb, num); } - wolfSSL_sk_X509_free(certsToUse); - wolfSSL_sk_X509_free(origTrustedSk); +#endif - /* Fail closed on the way out: every failure has to be reportable through - * X509_STORE_CTX_get_error(), or the application is told the chain was - * fine while this function reports failure. Not all of them record one - - * a verify callback that rejects a chain the verification accepted, an - * allocation failure, or a chain-building error that never reached - * SetupStoreCtxError() all leave ctx->error at X509_V_OK. This is a - * deliberate blanket fallback for that whole class; it is applied only - * when nothing more specific was recorded, so an error the callback or - * the verification did set survives untouched. */ +exit: + wolfSSL_X509_free(cmIssuer); + wolfSSL_sk_X509_free(sigFail); + if (err != 0) { + ret = X509StoreFatal(ctx, err); + } + if (orig != NULL) { + ctx->current_cert = orig; + } + /* Every failure must be reportable through X509_STORE_CTX_get_error() */ if (ret != WOLFSSL_SUCCESS && ctx->error == WOLFSSL_X509_V_OK) { ctx->error = WOLFSSL_X509_V_ERR_UNSPECIFIED; } @@ -1470,9 +1678,14 @@ int wolfSSL_X509_STORE_CTX_set_purpose(WOLFSSL_X509_STORE_CTX *ctx, void wolfSSL_X509_STORE_CTX_set_flags(WOLFSSL_X509_STORE_CTX *ctx, unsigned long flags) { - if ((ctx != NULL) && (flags & WOLFSSL_PARTIAL_CHAIN)){ + if (ctx == NULL) { + return; + } + if (flags & WOLFSSL_PARTIAL_CHAIN) { ctx->flags |= WOLFSSL_PARTIAL_CHAIN; } + /* As in OpenSSL, the flags go on the verify param too */ + (void)wolfSSL_X509_VERIFY_PARAM_set_flags(ctx->param, flags); } /* set X509_STORE_CTX ex_data, max idx is MAX_EX_DATA. Return WOLFSSL_SUCCESS @@ -1522,7 +1735,7 @@ void wolfSSL_X509_STORE_CTX_set_depth(WOLFSSL_X509_STORE_CTX* ctx, int depth) { WOLFSSL_ENTER("wolfSSL_X509_STORE_CTX_set_depth"); if (ctx != NULL) { - ctx->depth = depth; + wolfSSL_X509_VERIFY_PARAM_set_depth(ctx->param, depth); } } #endif @@ -1776,6 +1989,19 @@ WOLFSSL_X509_VERIFY_PARAM *wolfSSL_X509_STORE_CTX_get0_param( return ctx->param; } +/* Replace the param of ctx, including the values inherited from the store. + * ctx takes ownership of param. A NULL param or the current one keeps the + * current one. */ +void wolfSSL_X509_STORE_CTX_set0_param(WOLFSSL_X509_STORE_CTX *ctx, + WOLFSSL_X509_VERIFY_PARAM *param) +{ + WOLFSSL_ENTER("wolfSSL_X509_STORE_CTX_set0_param"); + if (ctx != NULL && param != NULL && param != ctx->param) { + wolfSSL_X509_VERIFY_PARAM_free(ctx->param); + ctx->param = param; + } +} + #endif /* OPENSSL_EXTRA */ #if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) @@ -1791,6 +2017,8 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_X509_STORE_get1_certs( WOLFSSL_X509_NAME* certToFilterName = NULL; WOLF_STACK_OF(WOLFSSL_X509)* filteredCerts = NULL; WOLFSSL_X509* filteredCert = NULL; + int i; + int j; WOLFSSL_ENTER("wolfSSL_X509_STORE_get1_certs"); @@ -1814,7 +2042,7 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_X509_STORE_get1_certs( if (err == 0) { sk = wolfSSL_CertManagerGetCerts(store->cm); - if (sk == NULL) { + if (sk == NULL && wolfSSL_sk_X509_num(store->trusted) <= 0) { err = 1; } } @@ -1841,6 +2069,27 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_X509_STORE_get1_certs( } } + /* A trusted cert out of date when added is not in the cert manager */ + for (i = 0; err == 0 && i < wolfSSL_sk_X509_num(store->trusted); i++) { + filteredCert = wolfSSL_sk_X509_value(store->trusted, i); + if (wolfSSL_X509_NAME_cmp(&filteredCert->subject, name) != 0) + continue; + for (j = 0; j < wolfSSL_sk_X509_num(filteredCerts); j++) { + if (X509DerEquals(wolfSSL_sk_X509_value(filteredCerts, j), + filteredCert)) + break; + } + if (j < wolfSSL_sk_X509_num(filteredCerts)) + continue; + if (wolfSSL_X509_up_ref(filteredCert) != WOLFSSL_SUCCESS) { + err = 1; + } + else if (wolfSSL_sk_X509_push(filteredCerts, filteredCert) <= 0) { + wolfSSL_X509_free(filteredCert); + err = 1; + } + } + if (err == 1) { if (filteredCerts != NULL) { wolfSSL_sk_X509_pop_free(filteredCerts, NULL); @@ -1879,12 +2128,15 @@ int wolfSSL_X509_STORE_CTX_get1_issuer(WOLFSSL_X509 **issuer, #ifdef WOLFSSL_SIGNER_DER_CERT ret = x509GetIssuerFromCM(issuer, ctx->store->cm, x); -#else + if (ret == WOLFSSL_SUCCESS) { + return ret; + } +#endif + /* A trusted cert out of date when added is not in the cert manager */ ret = X509StoreGetIssuerEx(issuer, ctx->store->trusted, x); if ((ret == WOLFSSL_SUCCESS) && (*issuer != NULL)) { return wolfSSL_X509_up_ref(*issuer); } -#endif return ret; } @@ -1892,12 +2144,33 @@ int wolfSSL_X509_STORE_CTX_get1_issuer(WOLFSSL_X509 **issuer, #ifdef OPENSSL_EXTRA -/* Like X509StoreGetIssuerEx, but candidates present in `skip` are passed over - * and the scan continues, so a rejected same-subject cert does not hide a - * genuine issuer further down the stack. */ -static int X509StoreGetIssuerSkip(WOLFSSL_X509 **issuer, - WOLFSSL_STACK * certs, WOLFSSL_X509 *x, - WOLF_STACK_OF(WOLFSSL_X509)* skip) +/* Returns 1 if the public key of ca verifies the signature of x, else 0. + * An internal error is kept in *err. */ +static int X509StoreKeySigned(WOLFSSL_X509* ca, WOLFSSL_X509* x, int* err) +{ + int ret; + + if (ca->pubKey.buffer == NULL || x->derCert == NULL) { + return 0; + } + ret = CheckCertSignaturePubKey(x->derCert->buffer, x->derCert->length, + x->heap, ca->pubKey.buffer, ca->pubKey.length, (int)ca->pubKeyOID); + if (!X509StoreVerifyResult(ret)) { + (void)X509StoreKeepErr(err, ret); + } + return ret == 0; +} + +/* Returns 1 if ca may issue x, as in OpenSSL's check_issued(): the names + * and key ids match and the key type of ca fits the signature of x. */ +static int X509StoreIssued(WOLFSSL_X509* ca, WOLFSSL_X509* x) +{ + return wolfSSL_X509_check_issued(ca, x) == WOLFSSL_X509_V_OK && + SigOidMatchesKeyOid((word32)x->sigOID, (word32)ca->pubKeyOID); +} + +static int X509StoreGetIssuerEx(WOLFSSL_X509 **issuer, + WOLFSSL_STACK * certs, WOLFSSL_X509 *x) { int i; @@ -1908,9 +2181,7 @@ static int X509StoreGetIssuerSkip(WOLFSSL_X509 **issuer, for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) { WOLFSSL_X509* cand = wolfSSL_sk_X509_value(certs, i); - if (X509StoreCertInStack(skip, cand)) - continue; - if (wolfSSL_X509_check_issued(cand, x) == WOLFSSL_X509_V_OK) { + if (X509StoreIssued(cand, x)) { *issuer = cand; return WOLFSSL_SUCCESS; } @@ -1920,12 +2191,6 @@ static int X509StoreGetIssuerSkip(WOLFSSL_X509 **issuer, return WOLFSSL_FAILURE; } -static int X509StoreGetIssuerEx(WOLFSSL_X509 **issuer, - WOLFSSL_STACK * certs, WOLFSSL_X509 *x) -{ - return X509StoreGetIssuerSkip(issuer, certs, x, NULL); -} - #endif /****************************************************************************** @@ -1971,6 +2236,9 @@ WOLFSSL_X509_STORE* wolfSSL_X509_STORE_new(void) if ((store->trusted = wolfSSL_sk_X509_new_null()) == NULL) goto err_exit; + + if ((store->added = wolfSSL_sk_X509_new_null()) == NULL) + goto err_exit; #endif #ifdef HAVE_CRL @@ -1988,6 +2256,7 @@ WOLFSSL_X509_STORE* wolfSSL_X509_STORE_new(void) goto err_exit; } XMEMSET(store->param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM)); + store->param->depth = -1; if ((store->lookup.dirs = (WOLFSSL_BY_DIR*)XMALLOC(sizeof(WOLFSSL_BY_DIR), NULL, DYNAMIC_TYPE_OPENSSL)) == NULL) { WOLFSSL_MSG("store->lookup.dir memory allocation error"); @@ -2056,6 +2325,8 @@ void wolfSSL_X509_STORE_free(WOLFSSL_X509_STORE* store) wolfSSL_sk_X509_pop_free(store->trusted, NULL); store->trusted = NULL; } + wolfSSL_sk_X509_free(store->added); + store->added = NULL; #endif #ifdef OPENSSL_ALL if (store->objs != NULL) { @@ -2250,8 +2521,10 @@ static int X509StoreAddCa(WOLFSSL_X509_STORE* store, WOLFSSL_ENTER("X509StoreAddCa"); if (store != NULL && x509 != NULL && x509->derCert != NULL) { - /* Check if NO_CHECK_TIME flag is set - if so, skip date validation */ - if (store->param != NULL && + /* Check if NO_CHECK_TIME flag is set - if so, skip date validation. + * A store of its own keeps a CA out of date out of the cert manager + * also with the flag: TLS would trust it for good. */ + if (store->trusted == NULL && store->param != NULL && (store->param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { verify = VERIFY_SKIP_DATE; } @@ -2290,43 +2563,50 @@ WOLFSSL_LOCAL int X509StorePushCertsToCM(WOLFSSL_X509_STORE* store) if (store == NULL || store->cm == NULL) return WOLFSSL_SUCCESS; - /* Push non-self-signed intermediates from store->certs into the CM. */ - if (store->certs != NULL) { - num = wolfSSL_sk_X509_num(store->certs); + /* Push trusted certs first, while the store is not yet the store of an + * SSL_CTX: their dates are checked. Self-signed CAs are typically already + * in the CM (added during X509_STORE_add_cert), but AddCA handles + * duplicates. */ + if (store->trusted != NULL) { + num = wolfSSL_sk_X509_num(store->trusted); for (i = 0; i < num; i++) { - x509 = wolfSSL_sk_X509_value(store->certs, i); + x509 = wolfSSL_sk_X509_value(store->trusted, i); if (x509 != NULL) { ret = X509StoreAddCa(store, x509, WOLFSSL_USER_CA); - if (ret != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("X509StorePushCertsToCM: failed to add cert"); + /* A cert out of date stays out of the CM and is dropped */ + if (ret != WOLFSSL_SUCCESS && + ret != WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) && + ret != WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) { + WOLFSSL_MSG("X509StorePushCertsToCM: failed to add " + "trusted cert"); anyFail = 1; } } } - /* Free and NULL to mark store as CTX-owned. Future add_cert calls - * will go directly to the CertManager. */ - wolfSSL_sk_X509_pop_free(store->certs, NULL); - store->certs = NULL; + wolfSSL_sk_X509_pop_free(store->trusted, NULL); + store->trusted = NULL; } - /* Push trusted certs too. Self-signed CAs are typically already in the CM - * (added during X509_STORE_add_cert), but AddCA handles duplicates. */ - if (store->trusted != NULL) { - num = wolfSSL_sk_X509_num(store->trusted); + /* Push non-self-signed intermediates from store->certs into the CM. */ + if (store->certs != NULL) { + num = wolfSSL_sk_X509_num(store->certs); for (i = 0; i < num; i++) { - x509 = wolfSSL_sk_X509_value(store->trusted, i); + x509 = wolfSSL_sk_X509_value(store->certs, i); if (x509 != NULL) { ret = X509StoreAddCa(store, x509, WOLFSSL_USER_CA); if (ret != WOLFSSL_SUCCESS) { - WOLFSSL_MSG("X509StorePushCertsToCM: failed to add " - "trusted cert"); + WOLFSSL_MSG("X509StorePushCertsToCM: failed to add cert"); anyFail = 1; } } } - wolfSSL_sk_X509_pop_free(store->trusted, NULL); - store->trusted = NULL; + /* Free and NULL to mark store as CTX-owned. Future add_cert calls + * will go directly to the CertManager. */ + wolfSSL_sk_X509_pop_free(store->certs, NULL); + store->certs = NULL; } + wolfSSL_sk_X509_free(store->added); + store->added = NULL; if (anyFail) { return WOLFSSL_FATAL_ERROR; @@ -2346,12 +2626,25 @@ int wolfSSL_X509_STORE_add_cert(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509) * CA=TRUE */ if (wolfSSL_X509_NAME_cmp(&x509->issuer, &x509->subject) == 0) { result = X509StoreAddCa(store, x509, WOLFSSL_USER_CA); - if (result == WOLFSSL_SUCCESS && store->trusted != NULL) { + /* As OpenSSL, a store of its own takes a cert out of date now. + * It stays out of the cert manager, so TLS does not trust it. + * X509_verify_cert() checks its dates at the verification time. */ + if (store->trusted != NULL && + (result == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) || + result == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E))) { + result = WOLFSSL_SUCCESS; + } + /* As OpenSSL, adding a cert already there is a no-op */ + if (result == WOLFSSL_SUCCESS && store->trusted != NULL && + !X509StoreCertIsTrusted(store, x509, NULL)) { result = wolfSSL_X509_up_ref(x509); if (result == WOLFSSL_SUCCESS) { result = wolfSSL_sk_X509_push(store->trusted, x509); if (result > 0) { - result = WOLFSSL_SUCCESS; + /* OpenSSL picks among certs of a name in the order + * they were added. */ + result = (wolfSSL_sk_X509_push(store->added, x509) > 0) + ? WOLFSSL_SUCCESS : WOLFSSL_FATAL_ERROR; } else { result = WOLFSSL_FATAL_ERROR; @@ -2367,7 +2660,8 @@ int wolfSSL_X509_STORE_add_cert(WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509) if (result == WOLFSSL_SUCCESS) { result = wolfSSL_sk_X509_push(store->certs, x509); if (result > 0) { - result = WOLFSSL_SUCCESS; + result = (wolfSSL_sk_X509_push(store->added, x509) > 0) + ? WOLFSSL_SUCCESS : WOLFSSL_FATAL_ERROR; } else { result = WOLFSSL_FATAL_ERROR; @@ -2424,6 +2718,14 @@ int wolfSSL_X509_STORE_set_flags(WOLFSSL_X509_STORE* store, unsigned long flag) if (flag & WOLFSSL_PARTIAL_CHAIN) { store->param->flags |= WOLFSSL_PARTIAL_CHAIN; } + if (flag & WOLFSSL_X509_STRICT) { + store->param->flags |= WOLFSSL_X509_STRICT; + } + /* As OpenSSL, also without HAVE_CRL. Not X509_V_FLAG_NO_CHECK_TIME: on + * the store of an SSL_CTX it lets CAs out of date into the cert + * manager. */ + store->param->flags |= flag & (WOLFSSL_X509_V_FLAG_CRL_CHECK | + WOLFSSL_X509_V_FLAG_CRL_CHECK_ALL); return ret; } @@ -2804,6 +3106,8 @@ WOLF_STACK_OF(WOLFSSL_X509_OBJECT)* wolfSSL_X509_STORE_get0_objects( #if defined(WOLFSSL_SIGNER_DER_CERT) && !defined(NO_FILESYSTEM) WOLFSSL_X509* x509 = NULL; int i = 0; + int j; + int n; #endif WOLFSSL_ENTER("wolfSSL_X509_STORE_get0_objects"); @@ -2835,7 +3139,8 @@ WOLF_STACK_OF(WOLFSSL_X509_OBJECT)* wolfSSL_X509_STORE_get0_objects( #if defined(WOLFSSL_SIGNER_DER_CERT) && !defined(NO_FILESYSTEM) cert_stack = wolfSSL_CertManagerGetCerts(store->cm); - if (cert_stack == NULL && wolfSSL_sk_X509_num(store->certs) > 0) { + if (cert_stack == NULL && (wolfSSL_sk_X509_num(store->certs) > 0 || + wolfSSL_sk_X509_num(store->trusted) > 0)) { cert_stack = wolfSSL_sk_X509_new_null(); if (cert_stack == NULL) { WOLFSSL_MSG("wolfSSL_sk_X509_OBJECT_new error"); @@ -2855,6 +3160,26 @@ WOLF_STACK_OF(WOLFSSL_X509_OBJECT)* wolfSSL_X509_STORE_get0_objects( goto err_cleanup; } } + /* A trusted cert out of date when added is not in the cert manager */ + n = wolfSSL_sk_X509_num(cert_stack); + for (i = 0; i < wolfSSL_sk_X509_num(store->trusted); i++) { + x509 = wolfSSL_sk_X509_value(store->trusted, i); + for (j = 0; j < n; j++) { + if (X509DerEquals(wolfSSL_sk_X509_value(cert_stack, j), x509)) + break; + } + if (j < n) + continue; + if (wolfSSL_X509_up_ref(x509) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("wolfSSL_X509_up_ref error"); + goto err_cleanup; + } + if (wolfSSL_sk_X509_push(cert_stack, x509) <= 0) { + WOLFSSL_MSG("wolfSSL_sk_X509_push error"); + wolfSSL_X509_free(x509); + goto err_cleanup; + } + } for (i = 0; i < wolfSSL_sk_X509_num(cert_stack); i++) { x509 = (WOLFSSL_X509 *)wolfSSL_sk_value(cert_stack, i); obj = wolfSSL_X509_OBJECT_new(); diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index cf567fbdba3..7d2823248d9 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -29,6 +29,7 @@ #endif #include +#include #ifdef OPENSSL_EXTRA #include #include @@ -87,10 +88,18 @@ int test_wolfSSL_X509_STORE_check_time(void) WOLFSSL_X509_STORE_CTX* ctx = NULL; WOLFSSL_X509* ca = NULL; WOLFSSL_X509* cert = NULL; +#ifdef HAVE_CRL + WOLF_STACK_OF(WOLFSSL_X509_CRL)* crls = NULL; + WOLFSSL_X509_CRL* crl = NULL; + XFILE fp = XBADFILE; +#endif int ret; time_t check_time; const char* srvCertFile = "./certs/server-cert.pem"; const char* expiredCertFile = "./certs/test/expired/expired-cert.pem"; + WOLFSSL_ASN1_TIME* epoch = NULL; + int day = 0; + int sec = 0; /* Set check_time to May 26, 2000 - should fail "not yet valid" check */ ExpectNotNull(store = wolfSSL_X509_STORE_new()); @@ -146,6 +155,26 @@ int test_wolfSSL_X509_STORE_check_time(void) WOLFSSL_SUCCESS); ret = wolfSSL_X509_verify_cert(ctx); ExpectIntEQ(ret, WOLFSSL_SUCCESS); + + /* As OpenSSL's X509_cmp_time(), the CA expires at its notAfter */ + ExpectNotNull(epoch = wolfSSL_ASN1_TIME_new()); + ExpectIntEQ(wolfSSL_ASN1_TIME_set_string(epoch, "19700101000000Z"), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_ASN1_TIME_diff(&day, &sec, epoch, + wolfSSL_X509_get_notAfter(ca)), WOLFSSL_SUCCESS); + check_time = (time_t)day * 86400 + sec; + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_set_time(ctx, 0, check_time - 1); + ExpectIntEQ(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_set_time(ctx, 0, check_time); + ExpectIntNE(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(ctx), + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error_depth(ctx), 1); + wolfSSL_ASN1_TIME_free(epoch); wolfSSL_X509_STORE_CTX_free(ctx); ctx = NULL; } @@ -156,7 +185,8 @@ int test_wolfSSL_X509_STORE_check_time(void) wolfSSL_X509_free(ca); ca = NULL; - /* A CRL flag must not make verification use check_time */ + /* A CRL flag must not make verification use check_time. As in OpenSSL, + * it turns on CRL checks: the CA's CRL does not list the cert. */ ExpectNotNull(store = wolfSSL_X509_STORE_new()); if (store != NULL) { ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, @@ -173,10 +203,32 @@ int test_wolfSSL_X509_STORE_check_time(void) WOLFSSL_X509_V_FLAG_CRL_CHECK), WOLFSSL_SUCCESS); ExpectIntEQ(ctx->param->flags & WOLFSSL_USE_CHECK_TIME, 0); } +#ifdef HAVE_CRL + ExpectTrue((fp = XFOPEN("./certs/crl/crl.pem", "rb")) != XBADFILE); + ExpectNotNull(crl = wolfSSL_PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(crls = wolfSSL_sk_X509_CRL_new_null()); + ExpectIntGT(wolfSSL_sk_X509_CRL_push(crls, crl), 0); + wolfSSL_X509_STORE_CTX_set0_crls(ctx, crls); +#endif ret = wolfSSL_X509_verify_cert(ctx); +#ifdef HAVE_CRL ExpectIntEQ(ret, WOLFSSL_SUCCESS); +#else + /* Without HAVE_CRL there is no CRL */ + ExpectIntNE(ret, WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(ctx), + WOLFSSL_X509_V_ERR_UNABLE_TO_GET_CRL); +#endif wolfSSL_X509_STORE_CTX_free(ctx); ctx = NULL; +#ifdef HAVE_CRL + wolfSSL_sk_X509_CRL_free(crls); + crls = NULL; + wolfSSL_X509_CRL_free(crl); + crl = NULL; +#endif } wolfSSL_X509_STORE_free(store); store = NULL; @@ -207,6 +259,13 @@ int test_wolfSSL_X509_STORE_check_time(void) * because time validation is skipped */ ret = wolfSSL_X509_verify_cert(ctx); ExpectIntEQ(ret, WOLFSSL_SUCCESS); + + /* As OpenSSL, X509_V_FLAG_USE_CHECK_TIME wins, also with time 0, the + * current time in wolfSSL */ + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_set_time(ctx, 0, 0); + ExpectIntNE(wolfSSL_X509_verify_cert(ctx), WOLFSSL_SUCCESS); } wolfSSL_X509_STORE_CTX_free(ctx); ctx = NULL; @@ -1299,6 +1358,9 @@ static int test_wolfSSL_X509_STORE_CTX_ex10(X509_STORE_test_data *testData) /* Fails because chain is incomplete */ ExpectIntNE(X509_verify_cert(ctx), 1); ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN), 1); + /* As in OpenSSL, init takes the store's flags */ + ExpectIntNE(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, testData->x509Leaf, NULL), 1); /* Partial chain now OK */ ExpectIntEQ(X509_verify_cert(ctx), 1); ExpectNotNull(chain = X509_STORE_CTX_get_chain(ctx)); @@ -1479,21 +1541,17 @@ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_cb_reject( STACK_OF(X509)* trusted = NULL; /* A per-context verify callback that rejects a certificate must not be - * overruled by the partial-chain terminus. The store trusts the root, so - * the intermediate the chain ends at verifies successfully - only the - * callback rejects it. The callback's veto is what makes - * X509StoreVerifyCert fail, and the WOLFSSL_PARTIAL_CHAIN fallback would - * then accept the chain at that same (caller-trusted) certificate and - * clear ctx->error, reporting X509_V_OK for a chain the application - * refused. */ + * overruled by the partial-chain terminus. The chain ends at the trusted + * intermediate, which verifies successfully - only the callback rejects + * it, when signalled success at its depth. A WOLFSSL_PARTIAL_CHAIN + * fallback must not accept the chain at that same (caller-trusted) + * certificate and clear ctx->error, reporting X509_V_OK for a chain the + * application refused. */ ExpectNotNull(store = X509_STORE_new()); ExpectIntEQ(X509_STORE_add_cert(store, testData->x509Ca), 1); /* Trust the two intermediates through the ctx override instead of the - * store, so the chain runs out of issuers at x509CaInt while the root - * that signed it is still a trusted CA in the CertManager. That is what - * makes the last X509StoreVerifyCert() succeed, leaving the callback as - * the only reason for the failure. */ + * store. */ ExpectNotNull(trusted = sk_X509_new_null()); ExpectIntGT(sk_X509_push(trusted, testData->x509CaInt2), 0); ExpectIntGT(sk_X509_push(trusted, testData->x509CaInt), 0); @@ -1502,8 +1560,10 @@ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_cb_reject( ExpectIntEQ(X509_STORE_CTX_init(ctx, store, testData->x509Leaf, NULL), 1); X509_STORE_CTX_trusted_stack(ctx, trusted); X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_PARTIAL_CHAIN); - /* After init, which clears any callback already on the context. */ - partialChainRejectCert = testData->x509CaInt; + /* After init, which clears any callback already on the context. + * With X509_V_FLAG_PARTIAL_CHAIN the chain ends at the first trusted + * cert, x509CaInt2, as in OpenSSL: x509CaInt is never seen. */ + partialChainRejectCert = testData->x509CaInt2; partialChainRejectSeen = 0; X509_STORE_CTX_set_verify_cb(ctx, partial_chain_reject_cb); @@ -2457,10 +2517,9 @@ static int test_untrusted_inter_depth_exhaustion(X509* leafDeep, X509* inter, ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leafDeep, untrusted), 1); X509_STORE_CTX_set_depth(ctx, INT_MAX); ExpectIntEQ(X509_verify_cert(ctx), 1); - /* a positive depth written directly (no setter) is still honored */ + /* a positive depth set after init is honored */ ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leafDeep, untrusted), 1); - if (ctx != NULL) - ctx->depth = 1; + X509_STORE_CTX_set_depth(ctx, 1); ExpectIntEQ(X509_verify_cert(ctx), 0); ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); @@ -2568,7 +2627,12 @@ static int test_untrusted_inter_depth_trusted_stack(X509* leaf, X509* inter, X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_PARTIAL_CHAIN); X509_STORE_CTX_set_depth(ctx, 0); ExpectIntEQ(X509_verify_cert(ctx), 1); - /* fresh ctx below: init does not clear the ctx-level flag */ + /* As in OpenSSL, init drops it */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + X509_STORE_CTX_set_depth(ctx, 0); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_CERT_CHAIN_TOO_LONG); X509_STORE_CTX_free(ctx); ctx = NULL; @@ -2691,12 +2755,10 @@ static int test_untrusted_inter_trusted_stack_cleanup(X509* leaf, X509* inter, } /* Trusted-stack counterpart of test_untrusted_inter_store_stack_unchanged: the - * caller's set0_trusted_stack must not be mutated - not even reordered - by the - * retry path. Put the tampered candidate ahead of root in the trusted stack so - * the verifier hits it first and takes X509VerifyCertSetupRetry (which moves - * failed candidates around on the internal copy), supply the genuine int-ca via - * the untrusted stack, then assert the trusted stack's exact contents and order - * after both a succeeding and a failing verification. */ + * caller's set0_trusted_stack must not be mutated - not even reordered - by + * X509_verify_cert(). Put the tampered candidate ahead of root in the trusted + * stack and the genuine int-ca in the untrusted stack, then assert the trusted + * stack's exact contents and order after each verification. */ static int test_untrusted_inter_trusted_stack_unchanged(X509* leaf, X509* inter, X509* tamperedInter, X509* root) { @@ -2708,18 +2770,19 @@ static int test_untrusted_inter_trusted_stack_unchanged(X509* leaf, X509* inter, ExpectNotNull(store = X509_STORE_new()); ExpectNotNull(trusted = sk_X509_new_null()); - /* Tampered candidate ahead of root forces the retry path over the trusted - * stack. */ + /* Tampered candidate ahead of root: it is found first. */ ExpectIntGT(sk_X509_push(trusted, tamperedInter), 0); ExpectIntGT(sk_X509_push(trusted, root), 0); - /* Succeeding verification: genuine int-ca arrives via the untrusted stack. */ + /* Genuine int-ca via the untrusted stack. The tampered cert is found + * first and is final: wolfSSL fails closed. OpenSSL rejects too, root did + * not sign it. */ ExpectNotNull(untrusted = sk_X509_new_null()); ExpectIntGT(sk_X509_push(untrusted, inter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); X509_STORE_CTX_trusted_stack(ctx, trusted); - ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); X509_STORE_CTX_free(ctx); ctx = NULL; @@ -2747,16 +2810,11 @@ static int test_untrusted_inter_trusted_stack_unchanged(X509* leaf, X509* inter, } /* One mixed-candidate verification: leaf with both the tampered intermediate - * (broken outer signature, same subject as int-ca) and the genuine int-ca in - * the untrusted stack, in the given push order. The chain must verify - the - * verifier tries the candidates and recovers via X509VerifyCertSetupRetry when - * it hits the tampered one first. Only the return value is asserted: when the - * tampered candidate is tried first wolfSSL intentionally preserves its error - * code even though the chain recovers (see the "worst-seen error must persist" - * behaviour exercised by test_X509_STORE_InvalidCa), so the error after success - * is order-dependent and not asserted here. */ + * (broken outer signature, same subject and key as int-ca) and the genuine + * int-ca in the untrusted stack, in the given push order. The first one is + * the issuer: the chain verifies as expected only with int-ca first. */ static int untrusted_inter_retry_one(X509_STORE* store, X509* leaf, - X509* first, X509* second) + X509* first, X509* second, int expected) { EXPECT_DECLS; X509_STORE_CTX* ctx = NULL; @@ -2767,21 +2825,18 @@ static int untrusted_inter_retry_one(X509_STORE* store, X509* leaf, ExpectIntGT(sk_X509_push(mixed, second), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, mixed), 1); - ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_verify_cert(ctx), expected); X509_STORE_CTX_free(ctx); sk_X509_free(mixed); return EXPECT_RESULT(); } -/* Retry path: when several caller-supplied candidates share a subject, the - * verifier tries one, fails, and retries with another - * (X509VerifyCertSetupRetry), moving entries through an internal "failed" list. - * Exercise BOTH push orderings so that, whichever order the verifier enumerates - * same-subject candidates, at least one ordering forces it to hit the tampered - * candidate first and recover. Then prove the store is left clean: a later +/* Same-subject candidates in the untrusted stack: the first issuer found is + * final, as in OpenSSL. With the tampered cert first the chain fails; with + * int-ca first it verifies. Then prove the store is left clean: a later * verification on the same store with only the tampered intermediate must fail * (no genuine int-ca left behind), and one with the genuine intermediate must - * still succeed. */ + * still succeed. The name is kept from when another candidate was tried. */ static int test_untrusted_inter_retry(X509* leaf, X509* inter, X509* tamperedInter, X509* root) { @@ -2794,13 +2849,16 @@ static int test_untrusted_inter_retry(X509* leaf, X509* inter, ExpectNotNull(store = X509_STORE_new()); ExpectIntEQ(X509_STORE_add_cert(store, root), 1); - /* Both orderings verify and report X509_V_OK. */ - ExpectIntEQ(untrusted_inter_retry_one(store, leaf, tamperedInter, inter), 1); - ExpectIntEQ(untrusted_inter_retry_one(store, leaf, inter, tamperedInter), 1); + /* The first issuer found is final: with the tampered cert first, + * wolfSSL fails closed. OpenSSL rejects too, root did not sign it. */ + ExpectIntEQ(untrusted_inter_retry_one(store, leaf, tamperedInter, inter, + 0), 1); + ExpectIntEQ(untrusted_inter_retry_one(store, leaf, inter, tamperedInter, + 1), 1); /* Reuse the SAME store with only the tampered intermediate: must fail. If - * a retry above left the genuine int-ca behind in the store, this would - * wrongly succeed. */ + * a verification above left the genuine int-ca behind in the store, this + * would wrongly succeed. */ ExpectNotNull(badOnly = sk_X509_new_null()); ExpectIntGT(sk_X509_push(badOnly, tamperedInter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); @@ -2811,7 +2869,7 @@ static int test_untrusted_inter_retry(X509* leaf, X509* inter, ctx = NULL; /* Reuse the SAME store with the genuine intermediate: must still succeed, - * proving the retry left no stale state that breaks later verifications. */ + * proving the verifications above left no stale state. */ ExpectNotNull(goodOnly = sk_X509_new_null()); ExpectIntGT(sk_X509_push(goodOnly, inter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); @@ -2827,10 +2885,8 @@ static int test_untrusted_inter_retry(X509* leaf, X509* inter, } /* A first-link signature failure must not leave the caller-supplied issuer - * loaded in the store's CertManager. X509_verify_cert() adds it as a - * WOLFSSL_TEMP_CA before checking the child; if the check fails the anchor has - * to go with it. The compat verifier drops TEMP_CAs before its own trust - * check, so residue is only visible through another user of the same + * loaded in the store's CertManager: X509_verify_cert() loads no temporary + * CAs. Residue would only be visible through another user of the same * CertManager - signer lookups there do not filter on type. */ static int test_untrusted_inter_no_temp_ca_residue(X509* leaf, X509* inter, X509* root) @@ -2880,15 +2936,12 @@ static int test_untrusted_inter_no_temp_ca_residue(X509* leaf, X509* inter, return EXPECT_RESULT(); } -/* Retry-path chain integrity: a tampered same-subject candidate tried and - * rejected before the genuine intermediate succeeds must not appear in the - * reported chain. Drive the retry path (tampered candidate ahead of the - * genuine one), then confirm X509_STORE_CTX_get0_chain() contains the genuine - * intermediate and never the rejected sibling. Certs are compared by content - * (X509_cmp) since the chain need not hold the caller's pointers. - * NOTE: this exercises the retry/failedCerts machinery via the untrusted - * stack. The terminal-anchor failedCerts guard is covered separately by - * test_untrusted_inter_terminal_anchor_rejected(). */ +/* Reported chain with the tampered sibling ahead of int-ca in the untrusted + * stack: the tampered cert is the leaf's issuer and is final, so the chain + * holds it and never int-ca, as in OpenSSL (leaf, tampered, root). Certs are + * compared by content (X509_cmp) since the chain need not hold the caller's + * pointers. The name is kept from when the sibling was rejected and int-ca + * tried. */ static int test_untrusted_inter_chain_excludes_rejected(X509* leaf, X509* inter, X509* tamperedInter, X509* root) { @@ -2904,16 +2957,16 @@ static int test_untrusted_inter_chain_excludes_rejected(X509* leaf, X509* inter, ExpectNotNull(store = X509_STORE_new()); ExpectIntEQ(X509_STORE_add_cert(store, root), 1); - /* Tampered candidate first forces the verifier to try and reject it, - * moving it into the internal failedCerts list, before recovering with the - * genuine intermediate. */ + /* Tampered candidate first. */ ExpectNotNull(mixed = sk_X509_new_null()); ExpectIntGT(sk_X509_push(mixed, tamperedInter), 0); ExpectIntGT(sk_X509_push(mixed, inter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, mixed), 1); - ExpectIntEQ(X509_verify_cert(ctx), 1); + /* The tampered cert is found first and is final: wolfSSL fails closed + * and the genuine int-ca is not tried. OpenSSL rejects too. */ + ExpectIntEQ(X509_verify_cert(ctx), 0); ExpectNotNull(chain = X509_STORE_CTX_get0_chain(ctx)); for (i = 0; i < sk_X509_num(chain); i++) { @@ -2923,8 +2976,8 @@ static int test_untrusted_inter_chain_excludes_rejected(X509* leaf, X509* inter, if (c != NULL && X509_cmp(c, tamperedInter) == 0) foundTampered = 1; } - ExpectIntEQ(foundInter, 1); - ExpectIntEQ(foundTampered, 0); + ExpectIntEQ(foundInter, 0); + ExpectIntEQ(foundTampered, 1); X509_STORE_CTX_free(ctx); X509_STORE_free(store); @@ -2933,17 +2986,12 @@ static int test_untrusted_inter_chain_excludes_rejected(X509* leaf, X509* inter, } #ifndef NO_TLS -/* Terminal-anchor variant of the above: drive the failedCerts guard on the - * issuer looked up after the CertManager verification succeeds. - * - * The anchors live in the CertManager (loaded from file) rather than on - * store->certs, so the terminal lookup consults set0_trusted_stack. That - * stack holds only the tampered intermediate, which is a same-subject sibling - * of the genuine one: the verifier tries it as the leaf's issuer, fails to - * verify it against the root, and moves it to failedCerts. The leaf then - * verifies directly against the CertManager copy of int-ca, and the terminal - * X509StoreGetIssuerEx() finds the tampered cert again by name+AKID. Without - * the guard it would be pushed onto the reported chain. */ +/* A trusted stack replaces the store's CAs, as in OpenSSL. root and int-ca + * are only in the CertManager (SSL_CTX_set_cert_store() moves store->certs + * there); the trusted stack holds only the tampered sibling of int-ca. It is + * the leaf's trusted issuer and nothing trusted issues it, so verification + * fails with UNABLE_TO_GET_ISSUER_CERT at depth 1 and the chain holds it. + * The name is kept from when the sibling was rejected. */ static int test_untrusted_inter_terminal_anchor_rejected(X509* leaf, X509* inter, X509* tamperedInter, X509* root) { @@ -2958,8 +3006,8 @@ static int test_untrusted_inter_terminal_anchor_rejected(X509* leaf, int storeOwned = 0; /* SSL_CTX_set_cert_store() pushes store->certs into the CertManager and - * detaches the stack, so int-ca becomes a CM anchor and the terminal - * lookup has to fall back to the caller's trusted stack. */ + * detaches the stack, so int-ca is only a CM CA, which the trusted stack + * replaces. */ #ifndef NO_WOLFSSL_SERVER ExpectNotNull(sslCtx = SSL_CTX_new(wolfSSLv23_server_method())); #else @@ -2980,7 +3028,12 @@ static int test_untrusted_inter_terminal_anchor_rejected(X509* leaf, ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); X509_STORE_CTX_trusted_stack(ctx, trusted); - ExpectIntEQ(X509_verify_cert(ctx), 1); + /* As in OpenSSL, the trusted stack replaces the CAs of the store: the + * tampered cert is the only trusted issuer and root did not sign it. */ + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), 1); ExpectNotNull(chain = X509_STORE_CTX_get0_chain(ctx)); for (i = 0; i < sk_X509_num(chain); i++) { @@ -2988,7 +3041,7 @@ static int test_untrusted_inter_terminal_anchor_rejected(X509* leaf, if (c != NULL && X509_cmp(c, tamperedInter) == 0) foundTampered = 1; } - ExpectIntEQ(foundTampered, 0); + ExpectIntEQ(foundTampered, 1); X509_STORE_CTX_free(ctx); sk_X509_free(trusted); @@ -3001,12 +3054,9 @@ static int test_untrusted_inter_terminal_anchor_rejected(X509* leaf, #endif /* NO_TLS */ /* The store's cert stack is shared by every X509_STORE_CTX (and every SSL - * connection) using the store, so verification must not modify it. Chain - * building appends caller-supplied intermediates and moves failed retry - * candidates around on an internal copy only. Put a tampered candidate on - * store->certs ahead of the genuine one so the verifier takes the retry path - * (which used to reorder the stack), then assert the stack's exact contents - * and order after both a succeeding and a failing verification. */ + * connection) using the store, so verification must not modify it. Put a + * tampered candidate on store->certs ahead of inter2, then assert the stack's + * exact contents and order after each verification. */ static int test_untrusted_inter_store_stack_unchanged(X509* leaf, X509* inter, X509* tamperedInter, X509* inter2, X509* root) { @@ -3022,15 +3072,14 @@ static int test_untrusted_inter_store_stack_unchanged(X509* leaf, X509* inter, ExpectIntEQ(X509_STORE_add_cert(store, inter2), 1); ExpectIntEQ(sk_X509_num(store->certs), 2); - /* Succeeding verification: the tampered candidate is hit first and the - * genuine intermediate arrives via the untrusted stack, forcing a retry. - * Only the return value is asserted; the error code after a recovered - * retry is order-dependent (worst-seen error persists). */ + /* Genuine int-ca via the untrusted stack. The tampered cert of the store + * is found first and is final: wolfSSL fails closed. OpenSSL rejects + * too, root did not sign it. */ ExpectNotNull(untrusted = sk_X509_new_null()); ExpectIntGT(sk_X509_push(untrusted, inter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); - ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); X509_STORE_CTX_free(ctx); ctx = NULL; @@ -3183,6 +3232,661 @@ int test_X509_verify_cert_untrusted_inter(void) return EXPECT_RESULT(); } +/* Depth from X509_VERIFY_PARAM, set as xmlsec does. leaf-deep <- int-ca2 <- + * int-ca <- root has two intermediates: depth 1 is too small, 2 is enough. */ +int test_wolfSSL_X509_VERIFY_PARAM_set_depth(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) + X509* leaf = NULL; + X509* inter = NULL; + X509* inter2 = NULL; + X509* root = NULL; + STACK_OF(X509)* untrusted = NULL; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + X509_VERIFY_PARAM* vpm = NULL; + X509_VERIFY_PARAM* vpm2 = NULL; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX* sslCtx = NULL; + SSL* ssl = NULL; +#endif + + ExpectNotNull(leaf = X509_load_certificate_file( + "./certs/intermediate/untrusted_anchor/leaf-deep-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(inter = X509_load_certificate_file( + "./certs/intermediate/untrusted_anchor/int-ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(inter2 = X509_load_certificate_file( + "./certs/intermediate/untrusted_anchor/int-ca2-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(root = X509_load_certificate_file( + "./certs/intermediate/untrusted_anchor/root-ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, inter), 0); + ExpectIntGT(sk_X509_push(untrusted, inter2), 0); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectNotNull(vpm = X509_VERIFY_PARAM_new()); + + /* A param without a depth does not limit the chain */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_set1_param(store, vpm), 1); + /* A builtin leaves the depth unset. ssl_server fits the leaf's EKU. */ + ExpectIntEQ(X509_VERIFY_PARAM_set1(X509_STORE_get0_param(store), + X509_VERIFY_PARAM_lookup("ssl_server")), 1); +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + ExpectNotNull(sslCtx = SSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = SSL_new(sslCtx)); + ExpectIntEQ(X509_STORE_set1_param(store, SSL_CTX_get0_param(sslCtx)), 1); + ExpectIntEQ(X509_STORE_set1_param(store, SSL_get0_param(ssl)), 1); + ExpectIntEQ(X509_STORE_set1_param(store, + X509_STORE_get0_param(SSL_CTX_get_cert_store(sslCtx))), 1); +#endif + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + + /* The context inherits the store depth */ + X509_VERIFY_PARAM_set_depth(vpm, 1); + ExpectIntEQ(X509_STORE_set1_param(store, vpm), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + X509_VERIFY_PARAM_set_depth(vpm, 2); + ExpectIntEQ(X509_STORE_set1_param(store, vpm), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + + /* set0_param() replaces the inherited param: depth 1 fails here */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectNotNull(vpm2 = X509_VERIFY_PARAM_new()); + X509_VERIFY_PARAM_set_depth(vpm2, 1); + X509_STORE_CTX_set0_param(ctx, vpm2); + vpm2 = NULL; + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + /* A negative depth rejects every chain. Only -1 is unset, so the store + * passes on -2, and -1 left in the context's param rejects too. */ + X509_VERIFY_PARAM_set_depth(vpm, -2); + ExpectIntEQ(X509_STORE_set1_param(store, vpm), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + X509_VERIFY_PARAM_set_depth(X509_STORE_CTX_get0_param(ctx), -1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + + /* With a store depth of 1, a later depth of 2 is honored */ + X509_VERIFY_PARAM_set_depth(vpm, 1); + ExpectIntEQ(X509_STORE_set1_param(store, vpm), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectNotNull(vpm2 = X509_VERIFY_PARAM_new()); + X509_VERIFY_PARAM_set_depth(vpm2, 2); + X509_STORE_CTX_set0_param(ctx, vpm2); + vpm2 = NULL; + ExpectIntEQ(X509_verify_cert(ctx), 1); + /* cleanup frees the param, the next init inherits depth 1 again */ + X509_STORE_CTX_cleanup(ctx); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + X509_VERIFY_PARAM_set_depth(X509_STORE_CTX_get0_param(ctx), 2); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + X509_STORE_CTX_set_depth(ctx, 2); + ExpectIntEQ(X509_verify_cert(ctx), 1); + /* the last setter wins */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + X509_STORE_CTX_set_depth(ctx, 1); + X509_VERIFY_PARAM_set_depth(X509_STORE_CTX_get0_param(ctx), 2); + ExpectIntEQ(X509_verify_cert(ctx), 1); + + /* A param without a depth rejects every chain, as in OpenSSL. A second + * set0_param() frees the first, NULL or the current one keeps it and + * X509_STORE_CTX_free() frees the last. */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectNotNull(vpm2 = X509_VERIFY_PARAM_new()); + X509_STORE_CTX_set0_param(ctx, vpm2); + vpm2 = NULL; + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_CHAIN_TOO_LONG); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + ExpectNotNull(vpm2 = X509_VERIFY_PARAM_new()); + X509_VERIFY_PARAM_set_depth(vpm2, 2); + X509_STORE_CTX_set0_param(ctx, vpm2); + X509_STORE_CTX_set0_param(ctx, NULL); + X509_STORE_CTX_set0_param(ctx, vpm2); + X509_STORE_CTX_set0_param(NULL, NULL); + X509_VERIFY_PARAM_set_depth(NULL, 2); + ExpectPtrEq(X509_STORE_CTX_get0_param(ctx), vpm2); + ExpectIntEQ(X509_verify_cert(ctx), 1); + + X509_STORE_CTX_free(ctx); + X509_VERIFY_PARAM_free(vpm); +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_free(ssl); + SSL_CTX_free(sslCtx); +#endif + X509_STORE_free(store); + sk_X509_free(untrusted); + X509_free(leaf); + X509_free(inter); + X509_free(inter2); + X509_free(root); +#endif /* OPENSSL_EXTRA && !NO_RSA && !NO_CERTS && !NO_FILESYSTEM */ + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +static int test_X509_verify_cert_accept_cb(int ok, X509_STORE_CTX* ctx) +{ + (void)ok; + (void)ctx; + return 1; +} +#endif + +/* An X509 made in memory has no DER. Failing closed, it is never an issuer, + * trusted or not, so every cert on the chain is checked. As in OpenSSL, the + * leaf then has no issuer. */ +int test_X509_verify_cert_no_der(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) + X509* ca = NULL; + X509* inter = NULL; + X509* leaf = NULL; + X509* leaf2 = NULL; + X509* fake = NULL; + X509* fakeInter = NULL; + STACK_OF(X509)* sk = NULL; + STACK_OF(X509)* untrusted = NULL; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; +#ifdef HAVE_CRL + X509_CRL* crl = NULL; + XFILE fp = XBADFILE; +#endif + + ExpectNotNull(ca = X509_load_certificate_file("./certs/ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(inter = X509_load_certificate_file( + "./certs/intermediate/ca-int-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(leaf = X509_load_certificate_file("./certs/server-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(leaf2 = X509_load_certificate_file( + "./certs/intermediate/ca-int2-cert.pem", SSL_FILETYPE_PEM)); + /* The names and key of ca-cert and ca-int, without DER */ + ExpectNotNull(fake = X509_new()); + ExpectIntEQ(X509_set_subject_name(fake, X509_get_subject_name(ca)), 1); + ExpectIntEQ(X509_set_issuer_name(fake, X509_get_subject_name(ca)), 1); + ExpectIntEQ(X509_set_pubkey(fake, X509_get0_pubkey(ca)), 1); + ExpectNotNull(fakeInter = X509_new()); + ExpectIntEQ(X509_set_subject_name(fakeInter, + X509_get_subject_name(inter)), 1); + ExpectIntEQ(X509_set_issuer_name(fakeInter, + X509_get_issuer_name(inter)), 1); + ExpectIntEQ(X509_set_pubkey(fakeInter, X509_get0_pubkey(inter)), 1); + + /* A trusted stack cert with PARTIAL_CHAIN would end the chain */ + ExpectNotNull(sk = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(sk, fake), 0); + ExpectNotNull(store = X509_STORE_new()); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + X509_STORE_CTX_trusted_stack(ctx, sk); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_PARTIAL_CHAIN); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), 0); + /* Nor is it verified: the error is not X509_V_OK */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, fake, NULL), 1); + ExpectIntEQ(X509_verify_cert(ctx), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_UNSPECIFIED); + + /* An untrusted one below a trusted root, every CRL checked. The + * callback accepts each error: 20, 3 and 21 at depth 0. */ + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, fakeInter), 0); + ExpectIntEQ(X509_STORE_add_cert(store, ca), 1); +#ifdef HAVE_CRL + ExpectTrue((fp = XFOPEN("./certs/crl/crl.pem", "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectIntEQ(X509_STORE_add_crl(store, crl), 1); +#endif + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf2, untrusted), 1); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); + X509_STORE_CTX_set_verify_cb(ctx, test_X509_verify_cert_accept_cb); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), 0); + ExpectIntEQ(sk_X509_num(X509_STORE_CTX_get0_chain(ctx)), 1); + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); +#ifdef HAVE_CRL + X509_CRL_free(crl); +#endif + sk_X509_free(sk); + sk_X509_free(untrusted); + X509_free(fake); + X509_free(fakeInter); + X509_free(ca); + X509_free(inter); + X509_free(leaf); + X509_free(leaf2); +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_RSA) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_ASN_TIME) +static int test_crl_time_accept; /* the callback accepts CRL date errors */ +static int test_crl_time_errs; /* CRL date errors the callback saw */ + +/* Accepts the date errors of certs, and of CRLs with test_crl_time_accept */ +static int test_crl_time_cb(int ok, X509_STORE_CTX* ctx) +{ + int err = X509_STORE_CTX_get_error(ctx); + + if (!ok && (err == X509_V_ERR_CRL_NOT_YET_VALID || + err == X509_V_ERR_CRL_HAS_EXPIRED)) { + test_crl_time_errs++; + return test_crl_time_accept; + } + return ok || err == X509_V_ERR_CERT_NOT_YET_VALID || + err == X509_V_ERR_CERT_HAS_EXPIRED; +} + +/* The time of asn1Time in *t */ +static int test_crl_time_get(const ASN1_TIME* asn1Time, time_t* t) +{ + ASN1_TIME* epoch = ASN1_TIME_adj(NULL, 0, 0, 0); + int day = 0; + int sec = 0; + int ret = epoch != NULL && + ASN1_TIME_diff(&day, &sec, epoch, asn1Time) == 1; + + *t = (time_t)day * 86400 + sec; + ASN1_TIME_free(epoch); + return ret; +} + +/* X509_verify_cert() of leaf with the trusted ca and crl, of the store or, + * with toCtx, of the ctx, CRL_CHECK and flags on the ctx at time t. The error + * is put in *err. */ +static int test_crl_time_run(X509* ca, X509* leaf, X509_CRL* crl, int toCtx, + time_t t, unsigned long flags, int* err) +{ + X509_STORE* store = X509_STORE_new(); + X509_STORE_CTX* ctx = X509_STORE_CTX_new(); + STACK_OF(X509_CRL)* crls = sk_X509_CRL_new_null(); + int ret = -1; + + if (store != NULL && ctx != NULL && crls != NULL && + X509_STORE_add_cert(store, ca) == 1 && + (toCtx ? sk_X509_CRL_push(crls, crl) > 0 : + X509_STORE_add_crl(store, crl) == 1) && + X509_STORE_CTX_init(ctx, store, leaf, NULL) == 1) { + if (toCtx) + X509_STORE_CTX_set0_crls(ctx, crls); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK | flags); + X509_STORE_CTX_set_time(ctx, 0, t); + X509_STORE_CTX_set_verify_cb(ctx, test_crl_time_cb); + test_crl_time_errs = 0; + ret = X509_verify_cert(ctx); + *err = X509_STORE_CTX_get_error(ctx); + } + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_CRL_free(crls); + return ret; +} +#endif + +/* As OpenSSL's check_crl_time(), a CRL of the store or of the ctx is current + * from its thisUpdate to its nextUpdate at the verification time: else 11 or + * 12, once accepted the CRL is used. X509_V_FLAG_USE_CHECK_TIME wins over + * X509_V_FLAG_NO_CHECK_TIME. The times are taken from the CRL. */ +int test_X509_verify_cert_crl_time(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_RSA) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_ASN_TIME) + X509* ca = NULL; + X509* leaf = NULL; + X509* revoked = NULL; + X509_CRL* crl = NULL; + XFILE fp = XBADFILE; + time_t thisUpdate = 0; + time_t nextUpdate = 0; + int toCtx; + int err = 0; + + ExpectNotNull(ca = X509_load_certificate_file("./certs/ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(leaf = X509_load_certificate_file("./certs/server-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(revoked = X509_load_certificate_file( + "./certs/server-revoked-cert.pem", SSL_FILETYPE_PEM)); + ExpectTrue((fp = XFOPEN("./certs/crl/crl.pem", "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectIntEQ(test_crl_time_get(X509_CRL_get0_lastUpdate(crl), + &thisUpdate), 1); + ExpectIntEQ(test_crl_time_get(X509_CRL_get0_nextUpdate(crl), + &nextUpdate), 1); + ExpectTrue(thisUpdate + 1 < nextUpdate - 1); + + for (toCtx = 0; toCtx <= 1 && EXPECT_SUCCESS(); toCtx++) { + test_crl_time_accept = 0; + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, thisUpdate + 1, + 0, &err), 1); + ExpectIntEQ(test_crl_time_errs, 0); + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, nextUpdate - 1, + 0, &err), 1); + ExpectIntEQ(test_crl_time_errs, 0); + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, thisUpdate - 1, + 0, &err), 0); + ExpectIntEQ(err, X509_V_ERR_CRL_NOT_YET_VALID); + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, nextUpdate + 1, + 0, &err), 0); + ExpectIntEQ(err, X509_V_ERR_CRL_HAS_EXPIRED); + /* As OpenSSL's X509_cmp_time(), current at its thisUpdate, expired + * at its nextUpdate */ + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, thisUpdate, 0, + &err), 1); + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, nextUpdate, 0, + &err), 0); + ExpectIntEQ(err, X509_V_ERR_CRL_HAS_EXPIRED); + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, nextUpdate + 1, + X509_V_FLAG_NO_CHECK_TIME, &err), 0); + ExpectIntEQ(err, X509_V_ERR_CRL_HAS_EXPIRED); + + test_crl_time_accept = 1; + ExpectIntEQ(test_crl_time_run(ca, leaf, crl, toCtx, nextUpdate + 1, + 0, &err), 1); + ExpectIntEQ(test_crl_time_errs, 1); + ExpectIntEQ(test_crl_time_run(ca, revoked, crl, toCtx, + thisUpdate - 1, 0, &err), 0); + ExpectIntEQ(err, X509_V_ERR_CERT_REVOKED); + ExpectIntEQ(test_crl_time_errs, 1); + ExpectIntEQ(test_crl_time_run(ca, revoked, crl, toCtx, + nextUpdate + 1, 0, &err), 0); + ExpectIntEQ(err, X509_V_ERR_CERT_REVOKED); + ExpectIntEQ(test_crl_time_errs, 1); + } + + X509_CRL_free(crl); + X509_free(ca); + X509_free(leaf); + X509_free(revoked); +#endif + return EXPECT_RESULT(); +} + +/* A CRL loaded after its CA is verified then and keeps no signature. As in + * OpenSSL, it is used for the certs of that CA: loaded with + * X509_STORE_load_locations(), X509_LOOKUP_load_file() or, into an SSL_CTX, + * wolfSSL_CTX_LoadCRLFile(). */ +int test_X509_verify_cert_crl_loaded(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_RSA) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \ + !defined(NO_WOLFSSL_DIR) && defined(WOLFSSL_PEM_TO_DER) + const char* crlFile = "./certs/crl/crl.pem"; + X509* ca = NULL; + X509* leaf = NULL; + X509* revoked = NULL; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX* sslCtx = NULL; +#endif + int i; + + ExpectNotNull(ca = X509_load_certificate_file("./certs/ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(leaf = X509_load_certificate_file("./certs/server-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(revoked = X509_load_certificate_file( + "./certs/server-revoked-cert.pem", SSL_FILETYPE_PEM)); + for (i = 0; i < 3 && EXPECT_SUCCESS(); i++) { + if (i < 2) { + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, ca), 1); + } + if (i == 0) { + ExpectIntEQ(X509_STORE_load_locations(store, crlFile, NULL), 1); + } + else if (i == 1) { + ExpectIntEQ(X509_LOOKUP_load_file(X509_STORE_add_lookup(store, + X509_LOOKUP_file()), crlFile, X509_FILETYPE_PEM), 1); + } + else { + /* A CA of an SSL_CTX goes on the chain only with its DER */ + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + ExpectNotNull(sslCtx = SSL_CTX_new(SSLv23_client_method())); + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, + "./certs/ca-cert.pem", NULL), 1); + ExpectIntEQ(wolfSSL_CTX_EnableCRL(sslCtx, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_LoadCRLFile(sslCtx, crlFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectNotNull(store = SSL_CTX_get_cert_store(sslCtx)); + #else + break; + #endif + } + + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, revoked, NULL), 1); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_CERT_REVOKED); + X509_STORE_CTX_free(ctx); + ctx = NULL; + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + if (sslCtx != NULL) { + SSL_CTX_free(sslCtx); + sslCtx = NULL; + store = NULL; + } + #endif + X509_STORE_free(store); + store = NULL; + } + + X509_free(ca); + X509_free(leaf); + X509_free(revoked); +#endif + return EXPECT_RESULT(); +} + +/* An intermediate CA of an SSL_CTX, without its issuer there, is the trust + * anchor. As TLS, the CRL check-all of the cert manager leaves it out, so the + * chain the handshake verified is returned. The leaf is still checked. + * X509_V_FLAG_CRL_CHECK_ALL checks the anchor: no CRL, as OpenSSL with + * X509_V_FLAG_PARTIAL_CHAIN. */ +int test_X509_verify_cert_crl_cm_anchor(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_RSA) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_CLIENT) && defined(WOLFSSL_PEM_TO_DER) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + const char* caFile = "./certs/intermediate/ca-int2-cert.pem"; + const char* crlFile = "./certs/crl/ca-int2.pem"; + X509* leaf = NULL; + SSL_CTX* sslCtx = NULL; + X509_STORE_CTX* ctx = NULL; +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_ALT_CERT_CHAINS) && defined(SESSION_CERTS) && \ + defined(KEEP_PEER_CERT) + test_ssl_memio_ctx test_ctx; + STACK_OF(X509)* chain = NULL; +#endif + + ExpectNotNull(leaf = X509_load_certificate_file( + "./certs/intermediate/server-int-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(sslCtx = SSL_CTX_new(SSLv23_client_method())); + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, caFile, NULL), 1); + ExpectIntEQ(wolfSSL_CTX_EnableCRL(sslCtx, WOLFSSL_CRL_CHECKALL), + WOLFSSL_SUCCESS); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, SSL_CTX_get_cert_store(sslCtx), leaf, + NULL), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_UNABLE_TO_GET_CRL); + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), 0); + ExpectIntEQ(wolfSSL_CTX_LoadCRLFile(sslCtx, crlFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(X509_STORE_CTX_init(ctx, SSL_CTX_get_cert_store(sslCtx), leaf, + NULL), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(sk_X509_num(X509_STORE_CTX_get0_chain(ctx)), 2); + ExpectIntEQ(X509_STORE_CTX_init(ctx, SSL_CTX_get_cert_store(sslCtx), leaf, + NULL), 1); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_UNABLE_TO_GET_CRL); + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), 1); + X509_STORE_CTX_free(ctx); + SSL_CTX_free(sslCtx); + X509_free(leaf); + +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_ALT_CERT_CHAINS) && defined(SESSION_CERTS) && \ + defined(KEEP_PEER_CERT) + /* The server sends the anchor too: only then does TLS leave it out */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_cb.caPemFile = caFile; + test_ctx.c_cb.crlPemFile = crlFile; + test_ctx.s_cb.certPemFile = "./certs/intermediate/server-chain-short.pem"; + test_ctx.s_cb.keyPemFile = "./certs/server-key.pem"; + ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + ExpectNotNull(chain = SSL_get0_verified_chain(test_ctx.c_ssl)); + ExpectIntEQ(sk_X509_num(chain), 2); + test_ssl_memio_cleanup(&test_ctx); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +static int test_internal_err_calls; /* calls of the verify callback */ + +static int test_internal_err_cb(int ok, X509_STORE_CTX* ctx) +{ + (void)ok; + (void)ctx; + test_internal_err_calls++; + return 1; +} + +/* Verifies leaf with the CA of store and returns X509_verify_cert() */ +static int test_internal_err_run(X509_STORE* store, X509* leaf, int* err) +{ + EXPECT_DECLS; + X509_STORE_CTX* ctx = NULL; + int ret = -1; + + test_internal_err_calls = 0; + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + X509_STORE_CTX_set_verify_cb(ctx, test_internal_err_cb); + if (EXPECT_SUCCESS()) { + ret = X509_verify_cert(ctx); + *err = X509_STORE_CTX_get_error(ctx); + } + X509_STORE_CTX_free(ctx); + return ret; +} +#endif + +/* An internal error fails the verification with X509_V_ERR_UNSPECIFIED and + * the verify callback is not asked. The DER of the leaf is broken after it + * decoded, so checking its signature fails to parse it, with a cert of the + * store and with a CA of an SSL_CTX. */ +int test_X509_verify_cert_internal_err(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) + X509* ca = NULL; + X509* leaf = NULL; + X509_STORE* store = NULL; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX* sslCtx = NULL; +#endif + int err = 0; + + ExpectNotNull(ca = X509_load_certificate_file("./certs/ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(leaf = X509_load_certificate_file("./certs/server-cert.pem", + SSL_FILETYPE_PEM)); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, ca), 1); + ExpectIntEQ(test_internal_err_run(store, leaf, &err), 1); + ExpectIntEQ(err, X509_V_OK); + ExpectIntEQ(test_internal_err_calls, 2); + + /* The outer SEQUENCE of the leaf becomes a SET */ + ExpectTrue(leaf != NULL && leaf->derCert != NULL && + leaf->derCert->buffer[0] == (ASN_SEQUENCE | ASN_CONSTRUCTED)); + if (EXPECT_SUCCESS()) { + leaf->derCert->buffer[0] = ASN_SET | ASN_CONSTRUCTED; + } + ExpectIntEQ(test_internal_err_run(store, leaf, &err), 0); + ExpectIntEQ(err, X509_V_ERR_UNSPECIFIED); + ExpectIntEQ(test_internal_err_calls, 0); +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + ExpectNotNull(sslCtx = SSL_CTX_new(SSLv23_client_method())); + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, "./certs/ca-cert.pem", + NULL), 1); + ExpectIntEQ(test_internal_err_run(SSL_CTX_get_cert_store(sslCtx), leaf, + &err), 0); + ExpectIntEQ(err, X509_V_ERR_UNSPECIFIED); + ExpectIntEQ(test_internal_err_calls, 0); +#endif + if (EXPECT_SUCCESS()) { + leaf->derCert->buffer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED; + } + +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX_free(sslCtx); +#endif + X509_STORE_free(store); + X509_free(ca); + X509_free(leaf); +#endif + return EXPECT_RESULT(); +} + #if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ @@ -3254,9 +3958,11 @@ static int gen_leaf_under_int(byte* out, int outMax, RsaKey* leafKey, /* Verify "leaf <- intermediate <- root(ca-cert)" where the intermediate is * supplied only as an untrusted candidate. Returns the X509_verify_cert() - * result (1 verified, 0 rejected) via *verifyRet. */ + * result (1 verified, 0 rejected) via *verifyRet, and the error and its depth + * via *verifyErr and *verifyDepth. */ static int run_int_keyusage_case(const byte* intDer, int intSz, - const byte* leafDer, int leafSz, X509* root, int* verifyRet) + const byte* leafDer, int leafSz, X509* root, int* verifyRet, + int* verifyErr, int* verifyDepth) { EXPECT_DECLS; X509* inter = NULL; @@ -3276,8 +3982,9 @@ static int run_int_keyusage_case(const byte* intDer, int intSz, ExpectIntGT(sk_X509_push(untrusted, inter), 0); ExpectNotNull(ctx = X509_STORE_CTX_new()); ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); - if (verifyRet != NULL) - *verifyRet = X509_verify_cert(ctx); + *verifyRet = X509_verify_cert(ctx); + *verifyErr = X509_STORE_CTX_get_error(ctx); + *verifyDepth = X509_STORE_CTX_get_error_depth(ctx); X509_STORE_CTX_free(ctx); X509_STORE_free(store); sk_X509_free(untrusted); @@ -3288,9 +3995,11 @@ static int run_int_keyusage_case(const byte* intDer, int intSz, /* Verify leafDer with caDer trusted only through the context, so that path * building can reach the anchor only as a temporary CA. Partial chains are - * allowed so an anchor that is not self-signed can end the path. */ + * allowed so an anchor that is not self-signed can end the path. Returns the + * X509_verify_cert() result, the error and its depth through the pointers. */ static int run_selfsigned_anchor_case(const byte* caDer, int caSz, - const byte* leafDer, int leafSz, int* verifyRet) + const byte* leafDer, int leafSz, int* verifyRet, int* verifyErr, + int* verifyDepth) { EXPECT_DECLS; X509* ca = NULL; @@ -3311,8 +4020,9 @@ static int run_selfsigned_anchor_case(const byte* caDer, int caSz, ExpectIntGT(sk_X509_push(trusted, ca), 0); X509_STORE_CTX_trusted_stack(ctx, trusted); X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_PARTIAL_CHAIN); - if (verifyRet != NULL) - *verifyRet = X509_verify_cert(ctx); + *verifyRet = X509_verify_cert(ctx); + *verifyErr = X509_STORE_CTX_get_error(ctx); + *verifyDepth = X509_STORE_CTX_get_error_depth(ctx); X509_STORE_CTX_free(ctx); X509_STORE_free(store); sk_X509_free(trusted); @@ -3347,6 +4057,7 @@ int test_X509_verify_cert_ca_no_keycertsign(void) byte* leafDer = NULL; int intSz = 0, leafSz = 0; int verifyRet = -1; + int verifyErr = 0, verifyDepth = 0; const byte* p; X509* root = NULL; @@ -3383,8 +4094,10 @@ int test_X509_verify_cert_ca_no_keycertsign(void) intDer, intSz, &intKey, &rng, "Leaf under bad int")), 0); verifyRet = -1; ExpectIntEQ(run_int_keyusage_case(intDer, intSz, leafDer, leafSz, root, - &verifyRet), 1); + &verifyRet, &verifyErr, &verifyDepth), 1); ExpectIntNE(verifyRet, 1); + ExpectIntEQ(verifyErr, X509_V_ERR_INVALID_CA); + ExpectIntEQ(verifyDepth, 1); /* Case 2: intermediate CA with NO KeyUsage extension -> must verify. */ ExpectIntGT((intSz = gen_ca_int_keyusage(intDer, FOURK_BUF, &intKey, &caKey, @@ -3393,7 +4106,7 @@ int test_X509_verify_cert_ca_no_keycertsign(void) intDer, intSz, &intKey, &rng, "Leaf under noKU int")), 0); verifyRet = -1; ExpectIntEQ(run_int_keyusage_case(intDer, intSz, leafDer, leafSz, root, - &verifyRet), 1); + &verifyRet, &verifyErr, &verifyDepth), 1); ExpectIntEQ(verifyRet, 1); X509_free(root); @@ -3407,9 +4120,10 @@ int test_X509_verify_cert_ca_no_keycertsign(void) return EXPECT_RESULT(); } -/* A self-signed trust anchor stays usable when its KeyUsage extension omits - * keyCertSign. A self-issued key rollover certificate signed by the root is not - * self-signed, so it needs keyCertSign to act as a trust anchor. +/* As in OpenSSL, a trust anchor whose KeyUsage extension omits keyCertSign + * cannot issue certificates, also when it is self-signed. A self-issued key + * rollover certificate signed by the root acts as a trust anchor with + * keyCertSign. * * @return TEST_SUCCESS on success. */ @@ -3424,10 +4138,11 @@ int test_X509_verify_cert_selfsigned_anchor_no_keycertsign(void) int selfSigned; /* else a rollover signed by the test root */ const char* keyUsage; int expectVerify; /* expected X509_verify_cert() result */ + int expectErr; /* expected error */ } cases[] = { - { 1, "digitalSignature", 1 }, - { 0, "digitalSignature", 0 }, - { 0, "digitalSignature,keyCertSign", 1 }, + { 1, "digitalSignature", 0, X509_V_ERR_INVALID_CA }, + { 0, "digitalSignature", 0, X509_V_ERR_INVALID_CA }, + { 0, "digitalSignature,keyCertSign", 1, X509_V_OK }, }; WC_RNG rng; RsaKey rootKey, caKey, leafKey; @@ -3438,6 +4153,7 @@ int test_X509_verify_cert_selfsigned_anchor_no_keycertsign(void) DecodedCert* dc = NULL; int caSz = 0, leafSz = 0; int verifyRet; + int verifyErr = 0, verifyDepth = 0; size_t i; caDer = (byte*)XMALLOC(FOURK_BUF, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -3480,8 +4196,11 @@ int test_X509_verify_cert_selfsigned_anchor_no_keycertsign(void) caDer, caSz, &caKey, &rng, "Leaf under anchor")), 0); verifyRet = -1; ExpectIntEQ(run_selfsigned_anchor_case(caDer, caSz, leafDer, leafSz, - &verifyRet), 1); + &verifyRet, &verifyErr, &verifyDepth), 1); ExpectIntEQ(verifyRet, cases[i].expectVerify); + ExpectIntEQ(verifyErr, cases[i].expectErr); + if (cases[i].expectVerify == 0) + ExpectIntEQ(verifyDepth, 1); } if (rngI) wc_FreeRng(&rng); @@ -3788,10 +4507,10 @@ int test_X509_STORE_untrusted(void) * properly */ ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted3, 1, 0, 1), TEST_SUCCESS); - /* Still needs properly loaded CA, while including it in untrusted - * list is not an error, it also doesn't count for verify */ + /* Still needs properly loaded CA. As in OpenSSL, the untrusted root + * ends the chain and is reported. */ ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted3, 0, - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 0), + X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, 0), TEST_SUCCESS); /* Succeeds because path to loaded CA is available. */ ExpectIntEQ(test_X509_STORE_untrusted_certs(untrusted4, 1, 0, 1), @@ -3803,6 +4522,7 @@ int test_X509_STORE_untrusted(void) #if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ !defined(WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE) +static int first_errcode; static int last_errcode; static int last_errdepth; @@ -3810,6 +4530,8 @@ static int X509Callback(int ok, X509_STORE_CTX *ctx) { if (!ok) { + if (first_errcode == 0) + first_errcode = X509_STORE_CTX_get_error(ctx); last_errcode = X509_STORE_CTX_get_error(ctx); last_errdepth = X509_STORE_CTX_get_error_depth(ctx); } @@ -3834,6 +4556,7 @@ int test_X509_STORE_InvalidCa(void) X509* cert = NULL; STACK_OF(X509)* untrusted = NULL; + first_errcode = 0; last_errcode = 0; last_errdepth = 0; @@ -3861,12 +4584,15 @@ int test_X509_STORE_InvalidCa(void) ExpectIntEQ(X509_STORE_CTX_init(ctx, str, cert, untrusted), 1); ExpectIntEQ(X509_verify_cert(ctx), 1); - ExpectIntEQ(last_errcode, X509_V_ERR_INVALID_CA); + ExpectIntEQ(first_errcode, X509_V_ERR_INVALID_CA); + /* As in OpenSSL, its keyUsage without keyCertSign is reported next */ + ExpectIntEQ(last_errcode, X509_V_ERR_KEYUSAGE_NO_CERTSIGN); (void)last_errdepth; /* Defense in depth: ctx->error must not be clobbered back to X509_V_OK * by the later successful verification of the intermediate against the * trusted root. The worst-seen error must persist. */ - ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_INVALID_CA); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_KEYUSAGE_NO_CERTSIGN); X509_free(cert); X509_STORE_free(str); @@ -3895,6 +4621,7 @@ int test_X509_STORE_InvalidCa_CtxCallback(void) X509* cert = NULL; STACK_OF(X509)* untrusted = NULL; + first_errcode = 0; last_errcode = 0; last_errdepth = 0; @@ -3924,9 +4651,12 @@ int test_X509_STORE_InvalidCa_CtxCallback(void) /* The callback overrides the CA:FALSE issuer, so verification succeeds... */ ExpectIntEQ(X509_verify_cert(ctx), 1); /* ...and it must actually have been handed the INVALID_CA error. */ - ExpectIntEQ(last_errcode, X509_V_ERR_INVALID_CA); + ExpectIntEQ(first_errcode, X509_V_ERR_INVALID_CA); + /* As in OpenSSL, its keyUsage without keyCertSign is reported next */ + ExpectIntEQ(last_errcode, X509_V_ERR_KEYUSAGE_NO_CERTSIGN); (void)last_errdepth; - ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_ERR_INVALID_CA); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_KEYUSAGE_NO_CERTSIGN); X509_free(cert); X509_STORE_free(str); @@ -4107,22 +4837,29 @@ int test_wolfSSL_X509_STORE_set_flags(void) !defined(NO_FILESYSTEM) && !defined(NO_RSA) X509_STORE* store = NULL; X509* x509 = NULL; -#ifdef HAVE_CRL + X509_STORE_CTX* ctx = NULL; X509_STORE_CTX* storeCtx = NULL; X509* ca = NULL; int err = 0; -#endif /* The CRL flags must not share bits with the other verify flags */ ExpectIntEQ((X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) & (X509_V_FLAG_USE_CHECK_TIME | X509_V_FLAG_NO_CHECK_TIME | - X509_V_FLAG_PARTIAL_CHAIN), 0); + X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_X509_STRICT), 0); ExpectNotNull((store = wolfSSL_X509_STORE_new())); ExpectNotNull((x509 = wolfSSL_X509_load_certificate_file(svrCertFile, WOLFSSL_FILETYPE_PEM))); ExpectIntEQ(X509_STORE_add_cert(store, x509), WOLFSSL_SUCCESS); + /* As in OpenSSL, the flags go on the verify param */ + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, x509, NULL), WOLFSSL_SUCCESS); + X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_X509_STRICT); + ExpectIntNE(X509_VERIFY_PARAM_get_flags(X509_STORE_CTX_get0_param(ctx)) & + X509_V_FLAG_X509_STRICT, 0); + X509_STORE_CTX_free(ctx); + #ifdef HAVE_CRL ExpectIntEQ(X509_STORE_set_flags(store, WOLFSSL_CRL_CHECKALL), WOLFSSL_SUCCESS); @@ -4135,19 +4872,29 @@ int test_wolfSSL_X509_STORE_set_flags(void) WC_NO_ERR_TRACE(NOT_COMPILED_IN)); ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK), WC_NO_ERR_TRACE(NOT_COMPILED_IN)); + ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK_ALL), + WC_NO_ERR_TRACE(NOT_COMPILED_IN)); #endif + /* As OpenSSL, the CRL flags go on the param, also without HAVE_CRL */ + ExpectIntEQ(X509_VERIFY_PARAM_get_flags(X509_STORE_get0_param(store)) & + (X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL), + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); wolfSSL_X509_STORE_free(store); store = NULL; -#ifdef HAVE_CRL /* X509_V_FLAG_CRL_CHECK turns on CRL checks. No CRL is loaded. */ ExpectNotNull((store = wolfSSL_X509_STORE_new())); ExpectNotNull((ca = wolfSSL_X509_load_certificate_file(caCertFile, WOLFSSL_FILETYPE_PEM))); ExpectIntEQ(X509_STORE_add_cert(store, ca), WOLFSSL_SUCCESS); +#ifdef HAVE_CRL ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK), WOLFSSL_SUCCESS); +#else + ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK), + WC_NO_ERR_TRACE(NOT_COMPILED_IN)); +#endif ExpectNotNull((storeCtx = X509_STORE_CTX_new())); ExpectIntEQ(X509_STORE_CTX_init(storeCtx, store, x509, NULL), WOLFSSL_SUCCESS); @@ -4158,7 +4905,6 @@ int test_wolfSSL_X509_STORE_set_flags(void) X509_STORE_CTX_free(storeCtx); wolfSSL_X509_free(ca); wolfSSL_X509_STORE_free(store); -#endif wolfSSL_X509_free(x509); #endif /* defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && @@ -5249,7 +5995,12 @@ int test_wolfSSL_CTX_set_cert_store(void) if (EXPECT_SUCCESS()) { ExpectIntEQ(X509_STORE_CTX_init(storeCtx, SSL_CTX_get_cert_store(ctx), svrCert, NULL), SSL_SUCCESS); + #ifdef WOLFSSL_SIGNER_DER_CERT ExpectIntEQ(X509_verify_cert(storeCtx), SSL_SUCCESS); + #else + /* Without their DER the CAs cannot go on the chain */ + ExpectIntNE(X509_verify_cert(storeCtx), SSL_SUCCESS); + #endif } X509_STORE_CTX_free(storeCtx); diff --git a/tests/api/test_ossl_x509_str.h b/tests/api/test_ossl_x509_str.h index a58655ca058..a4860db950d 100644 --- a/tests/api/test_ossl_x509_str.h +++ b/tests/api/test_ossl_x509_str.h @@ -39,6 +39,12 @@ int test_wolfSSL_X509_verify_cert_pathlen_anchor(void); int test_wolfSSL_X509_STORE_CTX_verify_cb(void); int test_wolfSSL_X509_STORE_CTX_verify_cb_check_id(void); int test_X509_verify_cert_untrusted_inter(void); +int test_wolfSSL_X509_VERIFY_PARAM_set_depth(void); +int test_X509_verify_cert_no_der(void); +int test_X509_verify_cert_crl_time(void); +int test_X509_verify_cert_crl_loaded(void); +int test_X509_verify_cert_crl_cm_anchor(void); +int test_X509_verify_cert_internal_err(void); int test_X509_verify_cert_ca_no_keycertsign(void); int test_X509_verify_cert_selfsigned_anchor_no_keycertsign(void); int test_X509_verify_cert_stale_temp_ca(void); @@ -90,6 +96,13 @@ int test_wolfSSL_CTX_set_cert_store(void); TEST_DECL_GROUP("ossl_x509_store", \ test_wolfSSL_X509_STORE_CTX_verify_cb_check_id), \ TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_untrusted_inter), \ + TEST_DECL_GROUP("ossl_x509_store", \ + test_wolfSSL_X509_VERIFY_PARAM_set_depth), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_no_der), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_crl_time), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_crl_loaded), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_crl_cm_anchor), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_internal_err), \ TEST_DECL_GROUP("ossl_x509_store", \ test_X509_verify_cert_ca_no_keycertsign), \ TEST_DECL_GROUP("ossl_x509_store", \ diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 4ff30806b8b..d30d4734235 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -1091,6 +1091,10 @@ int test_wolfSSL_get0_verified_chain(void) WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL; +#ifdef WOLFSSL_SIGNER_DER_CERT + WOLFSSL_X509_STORE_CTX* storeCtx = NULL; + WOLFSSL_X509* peer = NULL; +#endif ExpectNull(wolfSSL_get0_verified_chain(NULL)); @@ -1103,18 +1107,68 @@ int test_wolfSSL_get0_verified_chain(void) ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); +#ifdef WOLFSSL_SIGNER_DER_CERT /* The server's chain verifies against the CA the client loaded. */ ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); ExpectIntGT(wolfSSL_sk_X509_num(chain), 0); if (ssl_c != NULL) { ExpectPtrEq(ssl_c->verifiedChain, chain); } - /* Called again the previous chain is released and a new one stored. */ - ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); + /* Called again, the same chain is returned, as by OpenSSL. */ + ExpectPtrEq(wolfSSL_get0_verified_chain(ssl_c), chain); if (ssl_c != NULL) { ExpectPtrEq(ssl_c->verifiedChain, chain); } + /* Built once: without the CA it does not verify, but is still there. */ + ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx_c), WOLFSSL_SUCCESS); + ExpectNotNull(storeCtx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(storeCtx, + wolfSSL_CTX_get_cert_store(ctx_c), wolfSSL_sk_X509_value(chain, 0), + NULL), WOLFSSL_SUCCESS); + ExpectIntNE(wolfSSL_X509_verify_cert(storeCtx), WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(storeCtx); + ExpectPtrEq(wolfSSL_get0_verified_chain(ssl_c), chain); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caCertFile, NULL), + WOLFSSL_SUCCESS); + + /* Dropped by wolfSSL_clear() and by a new peer chain. */ + ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS); + if (ssl_c != NULL) { + ExpectNull(ssl_c->verifiedChain); + } + ExpectNotNull(wolfSSL_get0_verified_chain(ssl_c)); + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_CTX_free(ctx_s); + ctx_s = NULL; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx_s, + WOLFSSL_SESS_CACHE_OFF), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, + "./certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + wolfSSL_SetIORecv(ctx_s, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_s, test_memio_write_cb); + ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s, + NULL, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0); + if (ssl_c != NULL) { + ExpectNull(ssl_c->verifiedChain); + } + ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); + ExpectIntEQ(wolfSSL_sk_X509_num(chain), 4); + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_c)); + ExpectIntEQ(wolfSSL_X509_cmp(wolfSSL_sk_X509_value(chain, 0), peer), 0); + wolfSSL_X509_free(peer); +#else + /* Without its DER that CA cannot go on the chain. */ + ExpectNull(chain = wolfSSL_get0_verified_chain(ssl_c)); +#endif + wolfSSL_free(ssl_s); wolfSSL_free(ssl_c); wolfSSL_CTX_free(ctx_s); @@ -4484,9 +4538,14 @@ static int test_chain_slot_case_run(const test_chain_slot_case* tc, /* A certificate too large for its slot must not let the one behind it take * its index, which is what this getter reads as the peer. */ +#ifdef WOLFSSL_SIGNER_DER_CERT ExpectNotNull(sk = wolfSSL_get0_verified_chain(ssl)); ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 0), leafDer, leafSz), 1); +#else + /* Without its DER the CA cannot go on the chain. */ + ExpectNull(wolfSSL_get0_verified_chain(ssl)); +#endif ExpectIntEQ(wolfSSL_get_chain_count(wolfSSL_get_peer_chain(ssl)), 2); @@ -4565,6 +4624,205 @@ static int test_chain_slot_case_run(const test_chain_slot_case* tc, return EXPECT_RESULT(); } +#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SIGNER_DER_CERT) +/* Build a CA cert of key with the validity period of the cert datesDer: + * issued by issuerDer with issuerKey or, without issuerDer, self-signed. + * Returns the DER length, or < 0 on failure. */ +static int test_verified_chain_gen_ca(byte* out, int outMax, RsaKey* key, + const byte* issuerDer, int issuerDerSz, RsaKey* issuerKey, WC_RNG* rng, + const char* cn, const byte* datesDer, int datesDerSz) +{ + Cert cert; + int ret; + + if (wc_InitCert(&cert) != 0) { + return WOLFSSL_FATAL_ERROR; + } + cert.isCA = 1; + cert.sigType = CTC_SHA256wRSA; + XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.commonName, cn, CTC_NAME_SIZE - 1); + ret = wc_SetDatesBuffer(&cert, datesDer, datesDerSz); + if (ret == 0) + ret = wc_SetSubjectKeyIdFromPublicKey(&cert, key, NULL); + if (ret == 0) + ret = wc_SetKeyUsage(&cert, "keyCertSign,cRLSign"); + if (ret == 0 && issuerDer == NULL) { + ret = wc_MakeSelfCert(&cert, out, (word32)outMax, key, rng); + } + else if (ret == 0) { + ret = wc_SetAuthKeyIdFromCert(&cert, issuerDer, issuerDerSz); + if (ret == 0) + ret = wc_SetIssuerBuffer(&cert, issuerDer, issuerDerSz); + if (ret == 0) + ret = wc_MakeCert(&cert, out, (word32)outMax, key, NULL, rng); + if (ret >= 0) + ret = wc_SignCert(cert.bodySz, cert.sigType, out, (word32)outMax, + issuerKey, NULL, rng); + } +#ifdef WOLFSSL_CERT_GEN_CACHE + wc_SetCert_Free(&cert); +#endif + + return ret; +} + +/* How the handshake of test_verified_chain_run() verifies the peer. */ +#define TEST_VCHAIN_NONE 0 /* not at all */ +#define TEST_VCHAIN_PEER 1 /* verified */ +#define TEST_VCHAIN_HOST 2 /* a verify callback accepts a host name error */ +#define TEST_VCHAIN_RESUME 3 /* verified, then resumed */ +#define TEST_VCHAIN_KEYSZ 4 /* verified, then CA keys must be 4096 bits */ + +static int test_verified_chain_accept(int ok, WOLFSSL_X509_STORE_CTX* store) +{ + (void)ok; + (void)store; + return 1; +} + +/* Check the verified chain of ssl: none when num is 0, else num certs, the + * peer's, issuerDer and ca-cert. */ +static int test_verified_chain_check(WOLFSSL* ssl, int num, + const byte* issuerDer, int issuerSz) +{ + EXPECT_DECLS; + WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL; + WOLFSSL_X509* peer = NULL; + + sk = wolfSSL_get0_verified_chain(ssl); + if (num == 0) { + ExpectNull(sk); + } + else { + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl)); + ExpectIntEQ(wolfSSL_sk_X509_num(sk), num); + ExpectIntEQ(wolfSSL_X509_cmp(wolfSSL_sk_X509_value(sk, 0), peer), 0); + ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 1), + issuerDer, issuerSz), 1); + if (num > 2) { + ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 2), + ca_cert_der_2048, (int)sizeof_ca_cert_der_2048), 1); + } + /* As OpenSSL, the same chain again */ + ExpectPtrEq(wolfSSL_get0_verified_chain(ssl), sk); + } + wolfSSL_X509_free(peer); + + return EXPECT_RESULT(); +} + +/* A handshake to a client that loads ca1 and ca2, when set, into its cert + * manager with their date errors allowed, or that verifies with store. The + * server sends peerDer. The handshake checks neither the dates of a CA of the + * cert manager nor that it is a CA; X509_verify_cert() does, failing with err + * at depth 1. The verified chain is checked as test_verified_chain_check() + * does, with num 0 when the handshake does not verify the peer. */ +static int test_verified_chain_run(const byte* ca1, int ca1Sz, + const byte* ca2, int ca2Sz, WOLFSSL_X509_STORE* store, + const byte* peerDer, int peerSz, const byte* issuerDer, int issuerSz, + int mode, int err, int num) +{ + EXPECT_DECLS; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_X509_STORE_CTX* storeCtx = NULL; + WOLFSSL_X509* peer = NULL; + WOLFSSL_SESSION* sess = NULL; +#ifndef WOLFSSL_NO_TLS12 + method_provider method_c = wolfTLSv1_2_client_method; + method_provider method_s = wolfTLSv1_2_server_method; +#else + method_provider method_c = wolfTLSv1_3_client_method; + method_provider method_s = wolfTLSv1_3_server_method; +#endif + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectNotNull(ctx_c = wolfSSL_CTX_new(method_c())); + if (ctx_c != NULL) { + wolfSSL_SetIORecv(ctx_c, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_c, test_memio_write_cb); + } + if (ca1 != NULL) { + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer_ex(ctx_c, ca1, ca1Sz, + WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY), + WOLFSSL_SUCCESS); + } + if (ca2 != NULL) { + ExpectIntEQ(wolfSSL_CTX_load_verify_buffer_ex(ctx_c, ca2, ca2Sz, + WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY), + WOLFSSL_SUCCESS); + } + ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s, NULL, 0, (byte*)peerDer, peerSz, + (byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0); + if (store != NULL) { + ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl_c, store), + WOLFSSL_SUCCESS); + } + if (mode == TEST_VCHAIN_HOST) { + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host( + wolfSSL_get0_param(ssl_c), "wrong.example.com", 0), + WOLFSSL_SUCCESS); + } + wolfSSL_set_verify(ssl_c, (mode == TEST_VCHAIN_NONE) ? + WOLFSSL_VERIFY_NONE : WOLFSSL_VERIFY_PEER, + (mode == TEST_VCHAIN_HOST) ? test_verified_chain_accept : NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), (mode == TEST_VCHAIN_HOST) ? + WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH : WOLFSSL_X509_V_OK); + if (mode == TEST_VCHAIN_KEYSZ) { + ExpectIntEQ(wolfSSL_CTX_SetMinRsaKey_Sz(ctx_c, 4096), + WOLFSSL_SUCCESS); + } + + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_c)); + ExpectNotNull(storeCtx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(storeCtx, (store != NULL) ? store : + wolfSSL_CTX_get_cert_store(ctx_c), peer, NULL), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_verify_cert(storeCtx), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(storeCtx), err); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error_depth(storeCtx), 1); + + ExpectIntEQ(test_verified_chain_check(ssl_c, + (mode == TEST_VCHAIN_PEER || mode == TEST_VCHAIN_RESUME) ? num : 0, + issuerDer, issuerSz), TEST_SUCCESS); + + if (mode == TEST_VCHAIN_RESUME) { + /* Resumed, the peer is not verified again: every check applies */ + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, method_c, method_s, NULL, 0, (byte*)peerDer, peerSz, + (byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1); + ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), WOLFSSL_X509_V_OK); + ExpectIntEQ(test_verified_chain_check(ssl_c, 0, NULL, 0), + TEST_SUCCESS); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_X509_STORE_CTX_free(storeCtx); + wolfSSL_X509_free(peer); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); + + return EXPECT_RESULT(); +} +#endif /* !NO_FILESYSTEM && WOLFSSL_SIGNER_DER_CERT */ #endif /* session chain slot test dependencies */ /* Test that a certificate too large for a session chain slot does not let the @@ -4678,6 +4936,180 @@ int test_wolfSSL_session_chain_oversized_cert(void) #endif return EXPECT_RESULT(); } + +/* Test that the verified chain is the one the handshake verified, also when + * X509_verify_cert() rejects a CA of the cert manager that has expired or is + * not a CA, the trust anchor or not. Only those errors of only such a CA, and + * only after a full handshake that verified the peer, are not failed. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_get0_verified_chain_anchor(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ + !defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(WOLFSSL_ALT_NAMES) && defined(USE_CERT_BUFFERS_2048) && \ + defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && \ + defined(KEEP_PEER_CERT) && !defined(IGNORE_KEY_EXTENSIONS) && \ + (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && \ + !defined(NO_FILESYSTEM) && defined(WOLFSSL_SIGNER_DER_CERT) + WC_RNG rng; + RsaKey caKey; + RsaKey interKey; + RsaKey leafKey; + WOLFSSL_X509* expired = NULL; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_X509_STORE* store = NULL; + const byte* expiredDer = NULL; + const byte* caDer = ca_cert_der_2048; + int caSz = (int)sizeof_ca_cert_der_2048; + byte* anchorDer = NULL; + byte* interDer = NULL; + byte* leafDer = NULL; + byte* chainDer = NULL; + int expiredSz = 0; + int anchorSz = 0; + int interSz = 0; + int leafSz = 0; + int rngInit = 0; + int caInit = 0; + int interInit = 0; + int leafInit = 0; + word32 idx; + + ExpectNotNull(anchorDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(interDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(leafDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(chainDer = (byte*)XMALLOC(2 * TEST_CHAIN_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) rngInit = 1; + ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0); + if (EXPECT_SUCCESS()) caInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey, + (word32)sizeof_ca_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&interKey, NULL), 0); + if (EXPECT_SUCCESS()) interInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &interKey, + (word32)sizeof_server_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0); + if (EXPECT_SUCCESS()) leafInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey, + (word32)sizeof_client_key_der_2048), 0); + + /* A CA that expired in 2018 */ + ExpectNotNull(expired = wolfSSL_X509_load_certificate_file( + "./certs/test/expired/expired-ca.der", WOLFSSL_FILETYPE_ASN1)); + ExpectNotNull(expiredDer = wolfSSL_X509_get_der(expired, &expiredSz)); + + /* The trust anchor expired */ + ExpectIntGT((anchorSz = test_verified_chain_gen_ca(anchorDer, + TEST_CHAIN_BUF_SZ, &caKey, NULL, 0, NULL, &rng, "Expired Anchor", + expiredDer, expiredSz)), 0); + ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ, + &leafKey, anchorDer, anchorSz, &caKey, &rng, "Expired Anchor Leaf", 0, + 0)), 0); + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_PEER, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 2), TEST_SUCCESS); + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_NONE, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 2), TEST_SUCCESS); + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_HOST, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 2), TEST_SUCCESS); +#if !defined(WOLFSSL_NO_TLS12) && !defined(NO_SESSION_CACHE) + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_RESUME, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 2), TEST_SUCCESS); +#endif + + /* An intermediate that expired, below ca-cert */ + ExpectIntGT((interSz = test_verified_chain_gen_ca(interDer, + TEST_CHAIN_BUF_SZ, &interKey, caDer, caSz, &caKey, &rng, + "Expired Int", expiredDer, expiredSz)), 0); + ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ, + &leafKey, interDer, interSz, &interKey, &rng, "Expired Int Leaf", 0, + 0)), 0); + ExpectIntEQ(test_verified_chain_run(caDer, caSz, interDer, interSz, NULL, + leafDer, leafSz, interDer, interSz, TEST_VCHAIN_PEER, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 3), TEST_SUCCESS); + + /* A cert that is not a CA, issued by ca-cert, alone and below it */ + ExpectIntGT((anchorSz = test_chain_gen_cert(anchorDer, TEST_CHAIN_BUF_SZ, + &interKey, caDer, caSz, &caKey, &rng, "Not CA Anchor", 0, 0)), 0); + ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ, + &leafKey, anchorDer, anchorSz, &interKey, &rng, "Not CA Anchor Leaf", + 0, 0)), 0); + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_PEER, + WOLFSSL_X509_V_ERR_INVALID_CA, 2), TEST_SUCCESS); + ExpectIntEQ(test_verified_chain_run(anchorDer, anchorSz, NULL, 0, NULL, + leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_NONE, + WOLFSSL_X509_V_ERR_INVALID_CA, 2), TEST_SUCCESS); + ExpectIntEQ(test_verified_chain_run(caDer, caSz, anchorDer, anchorSz, + NULL, leafDer, leafSz, anchorDer, anchorSz, TEST_VCHAIN_PEER, + WOLFSSL_X509_V_ERR_INVALID_CA, 3), TEST_SUCCESS); + + /* Any other error of such a CA fails: a key that is now too small */ + ExpectIntGT((interSz = test_chain_gen_cert(interDer, TEST_CHAIN_BUF_SZ, + &interKey, caDer, caSz, &caKey, &rng, "Valid Int", 1, 0)), 0); + ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ, + &leafKey, interDer, interSz, &interKey, &rng, "Valid Int Leaf", 0, + 0)), 0); + ExpectIntEQ(test_verified_chain_run(caDer, caSz, interDer, interSz, NULL, + leafDer, leafSz, interDer, interSz, TEST_VCHAIN_KEYSZ, + WOLFSSL_X509_V_ERR_CA_KEY_TOO_SMALL, 0), TEST_SUCCESS); + + /* An expired intermediate that only a store of its own trusts, outside + * its cert manager, while the peer sends a valid one of the same name + * and key that ca-cert issued */ + ExpectIntGT((interSz = test_verified_chain_gen_ca(interDer, + TEST_CHAIN_BUF_SZ, &interKey, caDer, caSz, &caKey, &rng, "Store Int", + expiredDer, expiredSz)), 0); + ExpectIntGT((anchorSz = test_chain_gen_cert(anchorDer, TEST_CHAIN_BUF_SZ, + &interKey, caDer, caSz, &caKey, &rng, "Store Int", 1, 0)), 0); + ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ, + &leafKey, anchorDer, anchorSz, &interKey, &rng, "Store Int Leaf", 0, + 0)), 0); + if (EXPECT_SUCCESS()) { + XMEMCPY(chainDer, leafDer, (size_t)leafSz); + XMEMCPY(chainDer + leafSz, anchorDer, (size_t)anchorSz); + } + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + ExpectNotNull(x509 = wolfSSL_X509_d2i(NULL, caDer, caSz)); + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, x509), WOLFSSL_SUCCESS); + wolfSSL_X509_free(x509); + x509 = NULL; + ExpectNotNull(x509 = wolfSSL_X509_d2i(NULL, interDer, interSz)); + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, x509), WOLFSSL_SUCCESS); + wolfSSL_X509_free(x509); + x509 = NULL; + ExpectIntEQ(test_verified_chain_run(NULL, 0, NULL, 0, store, chainDer, + leafSz + anchorSz, NULL, 0, TEST_VCHAIN_PEER, + WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED, 0), TEST_SUCCESS); + + wolfSSL_X509_STORE_free(store); + wolfSSL_X509_free(expired); + if (rngInit) wc_FreeRng(&rng); + if (caInit) wc_FreeRsaKey(&caKey); + if (interInit) wc_FreeRsaKey(&interKey); + if (leafInit) wc_FreeRsaKey(&leafKey); + XFREE(anchorDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(interDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(chainDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ !defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ diff --git a/tests/api/test_ssl_cert.h b/tests/api/test_ssl_cert.h index 952e007321e..2b54d458daf 100644 --- a/tests/api/test_ssl_cert.h +++ b/tests/api/test_ssl_cert.h @@ -50,6 +50,7 @@ int test_wolfSSL_cert_setup_cb_ret(void); int test_wolfSSL_get_peer_cert_chain(void); int test_wolfSSL_set_peer_cert_chain(void); int test_wolfSSL_get0_verified_chain(void); +int test_wolfSSL_get0_verified_chain_anchor(void); int test_wolfSSL_CA_list_add(void); int test_wolfSSL_CA_list_get(void); int test_wolfSSL_load_client_CA_file(void); @@ -99,6 +100,8 @@ int test_wolfSSL_SESSION_get0_peer_full_chain(void); TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get_peer_cert_chain), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_set_peer_cert_chain), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_get0_verified_chain), \ + TEST_DECL_GROUP("ssl_cert", \ + test_wolfSSL_get0_verified_chain_anchor), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_add), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_get), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_load_client_CA_file), \ diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index b8b8b29b46c..a6414567f73 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -763,6 +763,48 @@ int test_tls_check_domain_name_single_label(void) return EXPECT_RESULT(); } +/* As in OpenSSL, the X509_STRICT and NO_CHECK_TIME flags of a connection do + * not reach the store of its SSL_CTX. */ +int test_tls_param_flags_not_on_store(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_SHA256) && !defined(NO_ASN_TIME) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + WOLFSSL_X509_STORE* store = NULL; + WOLFSSL_X509* expired = NULL; + unsigned long flags = WOLFSSL_NO_CHECK_TIME | WOLFSSL_X509_STRICT; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set_flags(wolfSSL_get0_param(ssl_c), + flags), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(store = wolfSSL_CTX_get_cert_store(ctx_c)); + ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_get_flags( + wolfSSL_X509_STORE_get0_param(store)) & flags, 0); + /* An expired CA still does not become a signer */ + ExpectNotNull(expired = wolfSSL_X509_load_certificate_file( + "./certs/test/expired/expired-ca.pem", WOLFSSL_FILETYPE_PEM)); + ExpectIntNE(wolfSSL_X509_STORE_add_cert(store, expired), + WOLFSSL_SUCCESS); + + wolfSSL_X509_free(expired); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* One macro per group test_tls_get_peer_tmp_key() can exercise, so that the * helper below is never compiled without a caller. */ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h index 0c5a30b1ef4..af7c666fbda 100644 --- a/tests/api/test_tls.h +++ b/tests/api/test_tls.h @@ -132,6 +132,7 @@ int test_wolfSSL_get_shared_ciphers(void); int test_tls12_aesgcm_record_nonce_unique(void); int test_tls_param_flags_crl_check(void); int test_tls_hmac_inner_cid_decision_coverage(void); +int test_tls_param_flags_not_on_store(void); #define TEST_TLS_DECLS \ TEST_DECL_GROUP("tls", test_utils_memio_move_message), \ @@ -235,6 +236,7 @@ int test_tls_hmac_inner_cid_decision_coverage(void); TEST_DECL_GROUP("tls", test_wolfSSL_get_shared_ciphers), \ TEST_DECL_GROUP("tls", test_tls12_aesgcm_record_nonce_unique), \ TEST_DECL_GROUP("tls", test_tls_param_flags_crl_check), \ + TEST_DECL_GROUP("tls", test_tls_param_flags_not_on_store), \ TEST_DECL_GROUP("tls", test_dhe_psk_max_premaster), \ TEST_DECL_GROUP("tls", test_tls12_chain_verify_cb), \ TEST_DECL_GROUP("tls", test_tls12_chain_verify_cb_server), \ diff --git a/tests/unit-mcdc/test_crl_whitebox.c b/tests/unit-mcdc/test_crl_whitebox.c index 3ee20d4f938..79f38c058be 100644 --- a/tests/unit-mcdc/test_crl_whitebox.c +++ b/tests/unit-mcdc/test_crl_whitebox.c @@ -90,13 +90,13 @@ static void wb_check_cert_crl_ex(WOLFSSL_CERT_MANAGER* cm) XMEMSET(issuerHash, 0x22, sizeof(issuerHash)); WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, NULL, (int)sizeof(serial), - hash, NULL, 0, NULL, cm, 1)); + hash, NULL, 0, NULL, cm, 1, NULL, NULL)); WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, NULL, (int)sizeof(serial), - NULL, NULL, 0, NULL, cm, 1)); + NULL, NULL, 0, NULL, cm, 1, NULL, NULL)); WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, 0, - NULL, NULL, 0, NULL, cm, 1)); + NULL, NULL, 0, NULL, cm, 1, NULL, NULL)); WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), - NULL, NULL, 0, NULL, cm, 1)); + NULL, NULL, 0, NULL, cm, 1, NULL, NULL)); } @@ -154,14 +154,14 @@ static void wb_missing_crl_callbacks(WOLFSSL_CERT_MANAGER* cm) /* 1. cm NULL: operand 0 false for both decisions. */ WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), NULL, (const byte*)url, (int)XSTRLEN(url), NULL, - NULL, 1)); + NULL, 1, NULL, NULL)); /* 2. real cm, no callbacks: operand 0 true, operand 1 false. */ cm->cbMissingCRL = NULL; cm->crlCb = NULL; WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), NULL, (const byte*)url, (int)XSTRLEN(url), NULL, - cm, 1)); + cm, 1, NULL, NULL)); /* 3. both callbacks installed, error cb returns 0: takes :668 TRUE (the * partner that gives operands 0 and 1 their pairs) and :686 to its third @@ -171,14 +171,14 @@ static void wb_missing_crl_callbacks(WOLFSSL_CERT_MANAGER* cm) g_crlCbResult = 0; WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), NULL, (const byte*)url, (int)XSTRLEN(url), NULL, - cm, 1)); + cm, 1, NULL, NULL)); /* 4. error cb returns non-zero: :686 operand 2 true, decision TRUE, which * is the override-the-CRL-error path. */ g_crlCbResult = 1; WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), NULL, (const byte*)url, (int)XSTRLEN(url), NULL, - cm, 1)); + cm, 1, NULL, NULL)); /* 5. a url longer than the 256-byte stack buffer takes the "CRL url too * long" arm of the copy guard inside the :668 body, which the short url @@ -189,7 +189,8 @@ static void wb_missing_crl_callbacks(WOLFSSL_CERT_MANAGER* cm) longUrl[sizeof(longUrl) - 1] = '\0'; WB_NOTE(CheckCertCRLCm(cm->crl, issuerHash, serial, (int)sizeof(serial), NULL, (const byte*)longUrl, - (int)sizeof(longUrl) - 1, NULL, cm, 1)); + (int)sizeof(longUrl) - 1, NULL, cm, 1, NULL, + NULL)); } cm->cbMissingCRL = NULL; diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 46090474893..699bd8b3de8 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -18304,7 +18304,7 @@ static int DecodeDsaAsn1Sig(const byte* sig, word32 sigSz, byte* sigCpy, /* The certificate's signatureAlgorithm (sigOID) must match the issuer's * key type (keyOID). sigOID picks the pre-hash; keyOID picks the * verifier. They need to agree or the verifier gets the wrong input. */ -static int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID) +int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID) { switch (keyOID) { #ifndef NO_RSA @@ -21046,6 +21046,63 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert) return 1; } +/* Checks the names of cert against the name constraints of caCert, as for a + * cert below caCert in a chain, leaf when it ends it. *violation is 0 when + * they are met, else NC_VIOLATION_EXCLUDED, NC_VIOLATION_PERMITTED or, for a + * critical constraint of a form that is not supported, + * NC_VIOLATION_UNSUPPORTED. + * Returns 0, or an error when a cert cannot be parsed. */ +int CheckCertNameConstraints(const byte* caCert, word32 caCertSz, + const byte* cert, word32 certSz, int leaf, void* heap, int* violation) +{ + int ret = WC_NO_ERR_TRACE(MEMORY_E); + Signer signer; + WC_DECLARE_VAR(ca, DecodedCert, 1, heap); + WC_DECLARE_VAR(dc, DecodedCert, 1, heap); + + *violation = 0; + WC_ALLOC_VAR_EX(ca, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT, goto end); + InitDecodedCert(ca, caCert, caCertSz, heap); + WC_ALLOC_VAR_EX(dc, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT, goto end); + InitDecodedCert(dc, cert, certSz, heap); + ret = ParseCertRelative(ca, CA_TYPE, NO_VERIFY, NULL, NULL); + if (ret == 0) { + ret = ParseCertRelative(dc, CERT_TYPE, NO_VERIFY, NULL, NULL); + } + if (ret != 0) { + goto end; + } + /* As OpenSSL, the CN of the leaf is checked, CA or not */ + if (leaf) + dc->isCA = 0; + XMEMSET(&signer, 0, sizeof(signer)); + signer.excludedNames = ca->excludedNames; + if (!ConfirmNameConstraints(&signer, dc)) { + *violation = NC_VIOLATION_EXCLUDED; + } + else { + signer.permittedNames = ca->permittedNames; + if (!ConfirmNameConstraints(&signer, dc)) { + *violation = NC_VIOLATION_PERMITTED; + } + else { + signer.extNameConstraintCrit = ca->extNameConstraintCrit; + signer.extNameConstraintHasUnsupported = + ca->extNameConstraintHasUnsupported; + if (!ConfirmNameConstraints(&signer, dc)) { + *violation = NC_VIOLATION_UNSUPPORTED; + } + } + } +end: + /* ca and dc are each NULL or initialised */ + FreeDecodedCert(dc); + FreeDecodedCert(ca); + WC_FREE_VAR_EX(dc, heap, DYNAMIC_TYPE_DCERT); + WC_FREE_VAR_EX(ca, heap, DYNAMIC_TYPE_DCERT); + return ret; +} + #endif /* IGNORE_NAME_CONSTRAINTS */ #ifdef WOLFSSL_ASN_TEMPLATE @@ -39581,6 +39638,8 @@ int VerifyCRL_Signature(SignatureCtx* sigCtx, const byte* toBeSigned, word32 signatureOID, const byte* sigParams, int sigParamsSz, Signer *ca, void* heap) { + int ret; + /* try to confirm/verify signature */ /* Enforced by default (RFC 5280 4.2.1.3 / 5.2: a CRL issuer MUST assert * cRLSign). IGNORE_KEY_EXTENSIONS is a deliberate, RFC-non-conformant @@ -39594,12 +39653,17 @@ int VerifyCRL_Signature(SignatureCtx* sigCtx, const byte* toBeSigned, #endif /* IGNORE_KEY_EXTENSIONS */ InitSignatureCtx(sigCtx, heap, INVALID_DEVID); - if (ConfirmSignature(sigCtx, toBeSigned, tbsSz, ca->publicKey, + ret = ConfirmSignature(sigCtx, toBeSigned, tbsSz, ca->publicKey, ca->pubKeySize, ca->keyOID, signature, sigSz, - signatureOID, sigParams, (word32)sigParamsSz, NULL) != 0) { + signatureOID, sigParams, (word32)sigParamsSz, NULL); + if (ret != 0) { WOLFSSL_MSG("CRL Confirm signature failed"); - WOLFSSL_ERROR_VERBOSE(ASN_CRL_CONFIRM_E); - return ASN_CRL_CONFIRM_E; + /* Running out of memory is not a bad signature */ + if (ret != WC_NO_ERR_TRACE(MEMORY_E)) { + ret = ASN_CRL_CONFIRM_E; + } + WOLFSSL_ERROR_VERBOSE(ret); + return ret; } return 0; diff --git a/wolfssl/crl.h b/wolfssl/crl.h index 0a1c72d7d5e..af28519af5f 100644 --- a/wolfssl/crl.h +++ b/wolfssl/crl.h @@ -49,7 +49,13 @@ WOLFSSL_LOCAL int CheckCertCRL_ex(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int serialSz, byte* serialHash, const byte* extCrlInfo, int extCrlInfoSz, void* issuerName); WOLFSSL_LOCAL int CheckCertCRLFromCm(WOLFSSL_CERT_MANAGER* cm, - WOLFSSL_CRL* crl, DecodedCert* cert); + WOLFSSL_CRL* crl, DecodedCert* cert, Signer* issuer, + const time_t* checkTime); +#ifdef OPENSSL_EXTRA +WOLFSSL_LOCAL int PickCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert, + Signer* issuer, const time_t* checkTime, int* cur, + WOLFSSL_ASN1_TIME* last); +#endif #ifdef HAVE_CRL_UPDATE_CB WOLFSSL_LOCAL int GetCRLInfo(WOLFSSL_CRL* crl, CrlInfo* info, const byte* buff, long sz, int type); diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 611d84f502c..e4af2c5f48e 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -860,10 +860,12 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_STORE_CTX_get1_issuer wolfSSL_X509_STORE_CTX_get1_issuer #define X509_STORE_CTX_set_time wolfSSL_X509_STORE_CTX_set_time #define X509_STORE_CTX_get0_param wolfSSL_X509_STORE_CTX_get0_param +#define X509_STORE_CTX_set0_param wolfSSL_X509_STORE_CTX_set0_param #define X509_VERIFY_PARAM_new wolfSSL_X509_VERIFY_PARAM_new #define X509_VERIFY_PARAM_free wolfSSL_X509_VERIFY_PARAM_free #define X509_VERIFY_PARAM_set_flags wolfSSL_X509_VERIFY_PARAM_set_flags #define X509_VERIFY_PARAM_set_time wolfSSL_X509_VERIFY_PARAM_set_time +#define X509_VERIFY_PARAM_set_depth wolfSSL_X509_VERIFY_PARAM_set_depth #define X509_VERIFY_PARAM_get_flags wolfSSL_X509_VERIFY_PARAM_get_flags #define X509_VERIFY_PARAM_clear_flags wolfSSL_X509_VERIFY_PARAM_clear_flags #define X509_VERIFY_PARAM_set_hostflags wolfSSL_X509_VERIFY_PARAM_set_hostflags diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 31ba14ed011..9d0cf6d447a 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -714,10 +714,6 @@ struct WOLFSSL_X509_STORE_CTX { char* domain; /* subject CN domain name */ #ifdef HAVE_EX_DATA WOLFSSL_CRYPTO_EX_DATA ex_data; /* external data */ -#endif -#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_EXTRA) - int depth; /* used in X509_STORE_CTX_*_depth: defaults - to WOLFSSL_X509_STORE_DEFAULT_MAX_DEPTH */ #endif void* userCtx; /* user ctx */ int error; /* current error */ @@ -730,7 +726,6 @@ struct WOLFSSL_X509_STORE_CTX { int flags; #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - WOLF_STACK_OF(WOLFSSL_X509)* owned; /* Certs owned by this CTX */ WOLF_STACK_OF(WOLFSSL_X509)* ctxIntermediates; /* Intermediates specified * on store ctx init */ WOLF_STACK_OF(WOLFSSL_X509)* setTrustedSk;/* A trusted stack override @@ -904,6 +899,7 @@ struct WOLFSSL_X509_STORE { WOLF_STACK_OF(WOLFSSL_X509)* trusted; WOLF_STACK_OF(WOLFSSL_X509)* owned; word32 numAdded; /* Unused, retained for ABI */ + WOLF_STACK_OF(WOLFSSL_X509)* added; /* certs and trusted, as added */ }; #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) @@ -932,6 +928,7 @@ struct WOLFSSL_X509_VERIFY_PARAM { char hostName[WOLFSSL_HOST_NAME_MAX]; unsigned int hostFlags; char ipasc[WOLFSSL_MAX_IPSTR]; + int depth; /* max intermediate CAs, -1 when unset */ }; #endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */ @@ -2628,6 +2625,8 @@ WOLFSSL_API int wolfSSL_X509_STORE_CTX_get_by_subject( WOLFSSL_X509_NAME* name, WOLFSSL_X509_OBJECT* obj); WOLFSSL_API WOLFSSL_X509_VERIFY_PARAM *wolfSSL_X509_STORE_CTX_get0_param( WOLFSSL_X509_STORE_CTX *ctx); +WOLFSSL_API void wolfSSL_X509_STORE_CTX_set0_param(WOLFSSL_X509_STORE_CTX *ctx, + WOLFSSL_X509_VERIFY_PARAM *param); WOLFSSL_API int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, WOLFSSL_X509_STORE* store, WOLFSSL_X509* x509, WOLF_STACK_OF(WOLFSSL_X509)*); WOLFSSL_API void wolfSSL_X509_STORE_CTX_cleanup(WOLFSSL_X509_STORE_CTX* ctx); @@ -2705,6 +2704,8 @@ WOLFSSL_API int wolfSSL_X509_VERIFY_PARAM_set_flags(WOLFSSL_X509_VERIFY_PARAM *p unsigned long flags); WOLFSSL_API void wolfSSL_X509_VERIFY_PARAM_set_time( WOLFSSL_X509_VERIFY_PARAM *param, time_t t); +WOLFSSL_API void wolfSSL_X509_VERIFY_PARAM_set_depth( + WOLFSSL_X509_VERIFY_PARAM *param, int depth); WOLFSSL_API int wolfSSL_X509_VERIFY_PARAM_get_flags(WOLFSSL_X509_VERIFY_PARAM *param); WOLFSSL_API int wolfSSL_X509_VERIFY_PARAM_clear_flags(WOLFSSL_X509_VERIFY_PARAM *param, unsigned long flags); @@ -2975,14 +2976,19 @@ WOLFSSL_API long wolfSSL_get_default_read_buffer_len(const WOLFSSL* ssl); enum { WOLFSSL_X509_V_OK = 0, WOLFSSL_X509_V_ERR_UNSPECIFIED = 1, + WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2, WOLFSSL_X509_V_ERR_UNABLE_TO_GET_CRL = 3, WOLFSSL_X509_V_ERR_CERT_SIGNATURE_FAILURE = 7, + WOLFSSL_X509_V_ERR_CRL_SIGNATURE_FAILURE = 8, WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID = 9, WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED = 10, + WOLFSSL_X509_V_ERR_CRL_NOT_YET_VALID = 11, WOLFSSL_X509_V_ERR_CRL_HAS_EXPIRED = 12, WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD = 13, WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD = 14, + WOLFSSL_X509_V_ERR_OUT_OF_MEM = 17, WOLFSSL_X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = 18, + WOLFSSL_X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19, WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20, WOLFSSL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21, WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG = 22, @@ -2991,9 +2997,15 @@ enum { WOLFSSL_X509_V_ERR_INVALID_PURPOSE = 26, WOLFSSL_X509_V_ERR_CERT_REJECTED = 28, WOLFSSL_X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29, + WOLFSSL_X509_V_ERR_KEYUSAGE_NO_CERTSIGN = 32, + WOLFSSL_X509_V_ERR_KEYUSAGE_NO_CRL_SIGN = 35, + WOLFSSL_X509_V_ERR_PERMITTED_VIOLATION = 47, + WOLFSSL_X509_V_ERR_EXCLUDED_VIOLATION = 48, + WOLFSSL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE = 51, WOLFSSL_X509_V_ERR_APPLICATION_VERIFICATION = 50, WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH = 62, WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH = 64, + WOLFSSL_X509_V_ERR_CA_KEY_TOO_SMALL = 67, WOLFSSL_X509_V_ERR_INVALID_CA = 79, WC_OSSL_V509_V_ERR_MAX = 80, /* 95 matches OpenSSL's X509_V_ERR_RPK_UNTRUSTED (OpenSSL 3.2+). It is diff --git a/wolfssl/test.h b/wolfssl/test.h index d89a48cfa60..5222a24f27c 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -2762,7 +2762,7 @@ static WC_INLINE int myVerify(int preverify, WOLFSSL_X509_STORE_CTX* store) #if defined(OPENSSL_EXTRA) && defined(SHOW_CERTS) && !defined(NO_FILESYSTEM) \ && !defined(OPENSSL_COEXIST) /* avoid printing duplicate certs */ - if (store->depth == 1) { + if (store->error_depth == 0) { int i; /* retrieve x509 certs and display them on stdout */ sk = wolfSSL_X509_STORE_GetCerts(store); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 29cd5d62759..50fdf10fd59 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2612,8 +2612,20 @@ WOLFSSL_LOCAL int EncodePolicyOID(byte *out, word32 *outSz, WOLFSSL_TEST_VIS int DecodeExtensionType(const byte* input, word32 length, word32 oid, byte critical, DecodedCert* cert, int *isUnknownExt); +WOLFSSL_LOCAL int SigOidMatchesKeyOid(word32 sigOID, word32 keyOID); WOLFSSL_LOCAL int CheckCertSignaturePubKey(const byte* cert, word32 certSz, void* heap, const byte* pubKey, word32 pubKeySz, int pubKeyOID); +#ifndef IGNORE_NAME_CONSTRAINTS +/* Name constraint violations of CheckCertNameConstraints() */ +enum { + NC_VIOLATION_EXCLUDED = 1, + NC_VIOLATION_PERMITTED = 2, + NC_VIOLATION_UNSUPPORTED = 3 +}; +WOLFSSL_LOCAL int CheckCertNameConstraints(const byte* caCert, + word32 caCertSz, const byte* cert, word32 certSz, int leaf, void* heap, + int* violation); +#endif #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_SMALL_CERT_VERIFY) WOLFSSL_API int wc_CheckCertSignature(const byte* cert, word32 certSz, void* heap, diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index b4756d1f57a..62b4512ef1b 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5251,9 +5251,10 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #define WOLFSSL_COPY_KEY #endif -/* X509_STORE_get0_objects() can only enumerate a store's certificates when the - * CA table keeps their DER. Costs one DER copy per loaded CA, so it stays - * optional; define WOLFSSL_NO_SIGNER_DER_CERT to opt back out. */ +/* X509_verify_cert() can only put a CA of the cert manager on the chain and + * check it, and X509_STORE_get0_objects() can only enumerate a store's + * certificates, when the CA table keeps their DER. Costs one DER copy per + * loaded CA; define WOLFSSL_NO_SIGNER_DER_CERT to opt back out. */ #if defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_SIGNER_DER_CERT) #undef WOLFSSL_SIGNER_DER_CERT #define WOLFSSL_SIGNER_DER_CERT From 808c799eab87a3451d0006bf316bdb836a95fca7 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:33:12 +0000 Subject: [PATCH 22/31] Add table-driven tests of the X509_verify_cert chain test_X509_verify_cert_chains runs the verifications of the table x509VfyCases. Each row gives the trusted certs (store, file, lookup or SSL_CTX), a trusted stack, the untrusted certs, the leaf, CRLs, flags, the param (as xmlsec sets it, without X509_V_FLAG_TRUSTED_FIRST), the time, the depth and the verify callback, with the expected return, error, error depth and, where it matters, the chain and every error the callback saw. One runner builds each case. It also checks that the current cert of each callback error is the chain cert at its depth, and that the caller's stacks are left as they were. The rows cover CA key sizes (RSA, P-256), self-signed copies and self-issued anchors, trusted certs that are not CAs, v1 roots and X509_V_FLAG_X509_STRICT, the search order with and without TRUSTED_FIRST, dead ends and twins, forged and wrong signers, trusted stacks and their CRLs, issuer key ids and key types, the dates of anchors, intermediates and CRLs at a verification time, partial chains and where the flag is read, SSL_CTX stores and the roots of their CAs, the depth when a CA of an SSL_CTX ends the chain, a chain too long and the callback, path lengths, untrusted roots, name constraints, also of a CA of an SSL_CTX, CRL checks without CRLs (also without HAVE_CRL), CRL signatures, stale CRLs, with and without X509_V_FLAG_NO_CHECK_TIME, CRLs of the ctx over the store's, the pick among CRLs of one name, the CRLs of a self-signed anchor, and issuers without cRLSign. The CRL rows also run with WOLFSSL_CRL_ALLOW_MISSING_CDP. A few certs and CRLs are made at run time with the X509 API; the rest are in certs/intermediate/trust_anchor/. More tests: - test_X509_verify_cert_oom fails each allocation of X509_verify_cert() in turn for a leaf without a trusted issuer, a chain through a pathlen:0 CA of an SSL_CTX, a cert that a set0_crls CRL revokes and an expired self-signed CA of an SSL_CTX; it never verifies. With a callback that accepts every error, a failed allocation either has no effect or fails the verification with X509_V_ERR_OUT_OF_MEM or X509_V_ERR_UNSPECIFIED, the callback seeing only the errors it sees without a failure. Some failed allocation gives X509_V_ERR_OUT_OF_MEM. Where verifying an RSA signature allocates, as with sp_int math, a failure there is a bad signature: then only that the chain still fails without a callback is checked. - test_X509_verify_cert_tls_cached_ca: after a handshake whose callback waives the missing issuer, the peer's CA cached in the SSL_CTX does not anchor X509_verify_cert() on its store (20). - test_X509_STORE_add_cert_expired: the store API and TLS side of a store that takes an expired self-signed cert. Rows that give another result with OpenSSL 3.0.13 have a comment: - key_size: OpenSSL has no CA-only minimum; at auth level 3 or 4 it first reports X509_V_ERR_EE_KEY_TOO_SMALL for the leaf, so the row whose callback accepts every 67 verifies only in wolfSSL. - v1 root with X509_STRICT: OpenSSL first reports X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER at depth 0, wolfSSL 79 at depth 1. - dead_end: OpenSSL takes an alternate chain via a trusted cert and verifies (once after its callback accepts 9) or gives 79 at depth 1; wolfSSL fails closed with 2 at depth 2. - trusted_cert_error: OpenSSL gives 7 at depth 1 through a trusted twin; wolfSSL fails closed with 20 at depth 1. - crl_dates bad CRL signature, callback accepts: OpenSSL checks the CRL's entries and gives 23; wolfSSL keeps 8. - depth, callback accepts: OpenSSL reports VE_LOCAL at depth 1 and verifies; wolfSSL keeps 22. - ctx_store_ca CA without its root: wolfSSL trusts the SSL_CTX CA, as its handshake does; OpenSSL gives 2 at depth 2, and with a depth limit 22 at depth 1 or 2. --- tests/api/test_ossl_x509_str.c | 2614 ++++++++++++++++++++++++++++++++ tests/api/test_ossl_x509_str.h | 8 + 2 files changed, 2622 insertions(+) diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 7d2823248d9..29abd6f7965 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -34,6 +34,7 @@ #include #include #endif +#include #include #include @@ -4401,6 +4402,2560 @@ int test_X509_verify_cert_stale_temp_ca(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +/* See certs/intermediate/trust_anchor/gen_certs.sh and gen_dated_certs.sh */ +#define TA_CERT_DIR "./certs/intermediate/trust_anchor/" +#if defined(HAVE_CRL) && defined(WOLFSSL_CERT_GEN) && \ + !defined(NO_ASN_TIME) && !defined(NO_STDIO_FILESYSTEM) +/* CRLs are generated */ +#define X509_VFY_GEN_CRLS +#endif + +/* Verify errors */ +#define VE_OK X509_V_OK +#define VE_ISSUER X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT +#define VE_NOCRL X509_V_ERR_UNABLE_TO_GET_CRL +#define VE_CRLSIG X509_V_ERR_CRL_SIGNATURE_FAILURE +#define VE_SIG X509_V_ERR_CERT_SIGNATURE_FAILURE +#define VE_NOTYET X509_V_ERR_CERT_NOT_YET_VALID +#define VE_EXPIRED X509_V_ERR_CERT_HAS_EXPIRED +#define VE_SELF0 X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT +#define VE_SELF X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN +#define VE_LOCAL X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY +#define VE_LEAFSIG X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE +#define VE_LONG X509_V_ERR_CERT_CHAIN_TOO_LONG +#define VE_REVOKED X509_V_ERR_CERT_REVOKED +#define VE_CA X509_V_ERR_INVALID_CA +#define VE_CERTSIGN X509_V_ERR_KEYUSAGE_NO_CERTSIGN +#define VE_KEYSIZE X509_V_ERR_CA_KEY_TOO_SMALL +#define VE_NCPERM X509_V_ERR_PERMITTED_VIOLATION +#define VE_NCEXCL X509_V_ERR_EXCLUDED_VIOLATION +#define VE_PATHLEN X509_V_ERR_PATH_LENGTH_EXCEEDED +#define VE_CRLEXP X509_V_ERR_CRL_HAS_EXPIRED +#define VE_CRLNYV X509_V_ERR_CRL_NOT_YET_VALID +#define VE_CRLKU X509_V_ERR_KEYUSAGE_NO_CRL_SIGN + +/* Options of a case */ +#define VO_XMLSEC 0x00001 /* a new param, as xmlsec: no TRUSTED_FIRST */ +#define VO_PARTIAL 0x00002 /* X509_V_FLAG_PARTIAL_CHAIN */ +#define VO_STRICT 0x00004 /* X509_V_FLAG_X509_STRICT */ +#define VO_NOTIME 0x00008 /* X509_V_FLAG_NO_CHECK_TIME */ +#define VO_S_STRICT 0x00010 /* X509_V_FLAG_X509_STRICT on the store */ +#define VO_S_NOTIME 0x00020 /* X509_V_FLAG_NO_CHECK_TIME on the store */ +#define VO_CRL 0x00040 /* X509_V_FLAG_CRL_CHECK on the store */ +#define VO_CRL_ALL 0x000C0 /* and X509_V_FLAG_CRL_CHECK_ALL */ +#define VO_FILE 0x00100 /* trusted certs by X509_STORE_load_locations */ +#define VO_LOOKUP 0x00200 /* by X509_LOOKUP_load_file */ +#define VO_SSLCTX 0x00400 /* by SSL_CTX_load_verify_locations */ +#define VO_ADD1999 0x00800 /* trusted certs added in 1999 */ +#define VO_MINRSA 0x01000 /* a minimum RSA CA key of 4096 bits */ +#define VO_DEPTH0 0x02000 /* X509_STORE_CTX_set_depth(ctx, 0) */ +#define VO_DEPTH1 0x04000 /* X509_STORE_CTX_set_depth(ctx, 1) */ +#define VO_MINECC 0x08000 /* a minimum ECC CA key of 384 bits */ +#define VO_T2005 0x10000 /* verification time 2005 */ +#define VO_T2015 0x20000 /* 2015 */ +#define VO_T2025 0x30000 /* 2025 */ +#define VO_TIME 0x30000 +#define VO_S_PARTIAL 0x40000 /* X509_V_FLAG_PARTIAL_CHAIN on the store */ +#define VO_C_CRL 0x80000 /* X509_V_FLAG_CRL_CHECK on the ctx */ +#define VO_C_CRL_ALL 0x180000 /* and X509_V_FLAG_CRL_CHECK_ALL */ + +/* The verify callback logs every error and accepts these */ +#define VA_ISSUER 0x01 /* VE_ISSUER */ +#define VA_LOCAL 0x02 /* VE_LOCAL */ +#define VA_SELF 0x04 /* VE_SELF0 and VE_SELF */ +#define VA_DATES 0x08 /* VE_NOTYET and VE_EXPIRED */ +#define VA_CA 0x10 /* VE_CA */ +#define VA_KEYSIZE 0x20 /* VE_KEYSIZE */ +#define VA_NOCRL 0x40 /* VE_NOCRL */ +#define VA_CRLKU 0x100 /* VE_CRLKU */ +#define VA_CRLEXP 0x200 /* VE_CRLEXP */ +#define VA_ALL 0x80 /* any */ + +/* A verification. Certs are named by file: TA_CERT_DIR -cert.pem or + * an alias of x509VfyFiles; "#2" is another copy of it. CRLs alike, + * with -crl.pem; "+" is given with X509_STORE_CTX_set0_crls(). */ +typedef struct x509_vfy_case { + const char* name; + const char* trusted; /* store certs, in the order added */ + const char* stack; /* X509_STORE_CTX_trusted_stack() */ + const char* untrusted; + const char* leaf; + const char* crls; + int opts; /* VO_* */ + int cb; /* VA_*, 0: no verify callback */ + int ret; /* of X509_verify_cert() */ + int err; + int depth; /* of err, unless VE_OK */ + const char* chain; /* leaf first, NULL: not checked */ + int cbLog[6]; /* errors and depths the callback saw */ +} x509_vfy_case; + +static const x509_vfy_case x509VfyCases[] = { + /* Every issuer, untrusted or trusted, meets the cert manager's minimum + * CA key size. All keys are 2048 bits. OpenSSL has no minimum for CAs + * only: at auth level 3 it first reports + * X509_V_ERR_EE_KEY_TOO_SMALL for the leaf, then the same errors. */ + { "key_size: 2048 bits", + "ca", NULL, "int1 int2", "server-int", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "key_size: 4096 bits minimum", + "ca", NULL, "int1 int2", "server-int", NULL, + VO_MINRSA, 0, 0, VE_KEYSIZE, 1, NULL, { 0 } }, + { "key_size: callback accepts each issuer, the anchor too", + "ca", NULL, "int1 int2", "server-int", NULL, + VO_MINRSA, VA_KEYSIZE, 1, VE_KEYSIZE, 0, NULL, + { VE_KEYSIZE, 1, VE_KEYSIZE, 2, VE_KEYSIZE, 3 } }, +#ifdef HAVE_ECC + /* The size of an ECC key is that of its curve. All keys are P-256. */ + { "key_size: P-256", + "ca-ecc", NULL, "int1-ecc int2-ecc", "server-int-ecc", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "key_size: 384 bits ECC minimum", + "ca-ecc", NULL, "int1-ecc int2-ecc", "server-int-ecc", NULL, + VO_MINECC, 0, 0, VE_KEYSIZE, 1, NULL, { 0 } }, +#endif + + /* A self-signed cert ends the chain. self-copy has the name and key of + * the trusted self, which verifies it, but only a trusted cert itself + * ends the chain. */ + { "selfsigned_copy: trusted", + "self", NULL, NULL, "self", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfsigned_copy: copy", + "self", NULL, NULL, "self-copy", NULL, + 0, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "selfsigned_copy: callback accepts copy", + "self", NULL, NULL, "self-copy", NULL, + 0, VA_ALL, 1, VE_SELF0, 0, "self-copy", { VE_SELF0, 0 } }, + { "selfsigned_copy: copy, partial chain", + "self", NULL, NULL, "self-copy", NULL, + VO_PARTIAL, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + /* As in OpenSSL, the first trusted cert of the name, self, is taken: the + * copy is a mimic of it. Added first, the copy is taken. */ + { "selfsigned_copy: copy trusted too", + "self self-copy", NULL, NULL, "self-copy", NULL, + 0, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "selfsigned_copy: copy trusted first", + "self-copy self", NULL, NULL, "self-copy", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfsigned_copy: copy, trusted stack", + NULL, "self", NULL, "self-copy", NULL, + 0, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "selfsigned_copy: trusted stack", + NULL, "self", NULL, "self", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfsigned_copy: copy, store file", + "self", NULL, NULL, "self-copy", NULL, + VO_FILE, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "selfsigned_copy: store file", + "self", NULL, NULL, "self", NULL, + VO_FILE, 0, 1, VE_OK, 0, NULL, { 0 } }, +#if defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM) + { "selfsigned_copy: copy, lookup file", + "self", NULL, NULL, "self-copy", NULL, + VO_LOOKUP, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "selfsigned_copy: lookup file", + "self", NULL, NULL, "self", NULL, + VO_LOOKUP, 0, 1, VE_OK, 0, NULL, { 0 } }, +#endif +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + /* An SSL_CTX keeps its CAs in the cert manager only */ + { "selfsigned_copy: SSL_CTX", + "self", NULL, NULL, "self", NULL, + VO_SSLCTX, 0, 1, VE_OK, 0, NULL, { 0 } }, +#endif + + /* A trusted cert is a root only when self-signed. link has the name of + * root and its own key, and root issued it: it is self-issued, but its + * authority key id names root's key. */ + { "selfissued_anchor: link", + "link", NULL, NULL, "leaf", NULL, + 0, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "selfissued_anchor: callback accepts link", + "link", NULL, NULL, "leaf", NULL, + 0, VA_ALL, 1, VE_ISSUER, 0, "leaf link", { VE_ISSUER, 1 } }, + { "selfissued_anchor: link, partial chain", + "link", NULL, NULL, "leaf", NULL, + VO_PARTIAL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfissued_anchor: link without aki is self-signed", + "link-noaki", NULL, NULL, "leaf", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfissued_anchor: link and root", + "link root", NULL, NULL, "leaf", NULL, + 0, 0, 1, VE_OK, 0, "leaf link root", { 0 } }, + /* link is not the anchor: it counts against the depth */ + { "selfissued_anchor: link and root, depth 0", + "link root", NULL, NULL, "leaf", NULL, + VO_DEPTH0, 0, 0, VE_LONG, 1, NULL, { 0 } }, + { "selfissued_anchor: link and root, depth 1", + "link root", NULL, NULL, "leaf", NULL, + VO_DEPTH1, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "selfissued_anchor: link, store file", + "link", NULL, NULL, "leaf", NULL, + VO_FILE, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "selfissued_anchor: link and root, store file", + "link root", NULL, NULL, "leaf", NULL, + VO_FILE, 0, 1, VE_OK, 0, NULL, { 0 } }, +#if defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM) + { "selfissued_anchor: link, lookup file", + "link", NULL, NULL, "leaf", NULL, + VO_LOOKUP, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "selfissued_anchor: link and root, lookup file", + "link root", NULL, NULL, "leaf", NULL, + VO_LOOKUP, 0, 1, VE_OK, 0, NULL, { 0 } }, +#endif +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + /* A CA of an SSL_CTX is a trusted cert, the chain goes on to its + * issuer there */ + { "selfissued_anchor: root and link, SSL_CTX", + "root link", NULL, NULL, "leaf", NULL, + VO_SSLCTX, 0, 1, VE_OK, 0, "leaf link root", { 0 } }, + { "selfissued_anchor: link, SSL_CTX", + "link", NULL, NULL, "leaf", NULL, + VO_SSLCTX, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, +#endif + + /* Every cert above the leaf is a CA that may sign certs, a trusted one + * too. Without basicConstraints only a v1 self-signed root is one, + * unless X509_V_FLAG_X509_STRICT is set. With that flag OpenSSL first + * reports that v1-root-leaf has no authority key id: + * X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER at depth 0. */ + { "trusted_ca: CA:FALSE root", + "notca-root nobc-root v1-root", NULL, NULL, "notca-root-leaf", NULL, + 0, 0, 0, VE_CA, 1, NULL, { 0 } }, + { "trusted_ca: callback accepts CA:FALSE root", + "notca-root nobc-root v1-root", NULL, NULL, "notca-root-leaf", NULL, + 0, VA_ALL, 1, VE_CA, 0, "notca-root-leaf notca-root", { VE_CA, 1 } }, + { "trusted_ca: CA:FALSE root, a stack without it", + "notca-root nobc-root v1-root", "v1-root", NULL, "notca-root-leaf", + NULL, 0, 0, 0, VE_LOCAL, 0, NULL, { 0 } }, + /* The stack's leaf ends the chain: the store's root is not on it */ + { "trusted_ca: leaf in a stack, partial chain", + "notca-root nobc-root v1-root", "notca-root-leaf", NULL, + "notca-root-leaf", NULL, + VO_PARTIAL, 0, 1, VE_OK, 0, "notca-root-leaf", { 0 } }, + { "trusted_ca: CA:FALSE root as the leaf", + "notca-root nobc-root v1-root", NULL, NULL, "notca-root", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "trusted_ca: copy of a stack cert", + NULL, "notca-root", NULL, "notca-root#2", NULL, + 0, 0, 1, VE_OK, 0, "notca-root", { 0 } }, + { "trusted_ca: v3 root without basicConstraints", + "notca-root nobc-root v1-root", NULL, NULL, "nobc-root-leaf", NULL, + 0, 0, 0, VE_CA, 1, NULL, { 0 } }, + { "trusted_ca: callback accepts v3 root without basicConstraints", + "notca-root nobc-root v1-root", NULL, NULL, "nobc-root-leaf", NULL, + 0, VA_ALL, 1, VE_CA, 0, "nobc-root-leaf nobc-root", { VE_CA, 1 } }, + { "trusted_ca: v1 root", + "notca-root nobc-root v1-root", NULL, NULL, "v1-root-leaf", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "trusted_ca: v1 root, strict", + "notca-root nobc-root v1-root", NULL, NULL, "v1-root-leaf", NULL, + VO_STRICT, 0, 0, VE_CA, 1, NULL, { 0 } }, + { "trusted_ca: v1 root in a stack", + NULL, "notca-root v1-root", NULL, "v1-root-leaf", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "trusted_ca: v1 root in a stack, strict", + NULL, "notca-root v1-root", NULL, "v1-root-leaf", NULL, + VO_STRICT, 0, 0, VE_CA, 1, NULL, { 0 } }, + { "trusted_ca: v1 root, strict store", + "notca-root nobc-root v1-root", NULL, NULL, "v1-root-leaf", NULL, + VO_S_STRICT, 0, 0, VE_CA, 1, NULL, { 0 } }, +#ifndef WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE + /* An untrusted CA:FALSE intermediate is reported as itself, then its + * keyUsage without keyCertSign */ + { "trusted_ca: CA:FALSE intermediate", + "nca-ca", NULL, "nca-int", "nca-leaf", NULL, + VO_FILE, VA_ALL, 1, VE_CERTSIGN, 0, "nca-leaf nca-int nca-ca", + { VE_CA, 1, VE_CERTSIGN, 1 } }, + /* The leaf signature is still checked after that */ + { "trusted_ca: CA:FALSE intermediate, bad leaf signature", + "nca-ca", NULL, "nca-int", "nca-leaf-badsig", NULL, + VO_FILE, VA_ALL, 1, VE_SIG, 0, "nca-leaf-badsig nca-int nca-ca", + { VE_CA, 1, VE_CERTSIGN, 1, VE_SIG, 0 } }, +#else + /* An untrusted CA:FALSE intermediate is allowed */ + { "trusted_ca: CA:FALSE intermediate allowed", + "nca-ca", NULL, "nca-int", "nca-leaf", NULL, + VO_FILE, VA_ALL, 1, VE_OK, 0, "nca-leaf nca-int nca-ca", { 0 } }, + { "trusted_ca: CA:FALSE intermediate allowed, bad leaf signature", + "nca-ca", NULL, "nca-int", "nca-leaf-badsig", NULL, + VO_FILE, VA_ALL, 1, VE_SIG, 0, "nca-leaf-badsig nca-int nca-ca", + { VE_SIG, 0 } }, +#endif + + /* With X509_V_FLAG_TRUSTED_FIRST, the default, a trusted root that + * issues a cert ends the chain, also when an untrusted cross-signed copy + * of it issues it too. cross-root is dated-root issued by expired-root. + * Without the flag the untrusted certs come first. */ + { "trusted_first: trusted root first", + "dated-root expired-root", NULL, "dated-int cross-root", + "dated-int-leaf", NULL, + 0, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", { 0 } }, +#ifndef NO_ASN_TIME + { "trusted_first: untrusted first", + "dated-root expired-root", NULL, "dated-int cross-root", + "dated-int-leaf", NULL, + VO_XMLSEC, 0, 0, VE_EXPIRED, 3, NULL, { 0 } }, + /* dated-root-late is not valid in 2015, expired-root is */ + { "trusted_first: late root", + "dated-root-late expired-root", NULL, "dated-int cross-root", + "dated-int-leaf", NULL, + VO_T2015, 0, 0, VE_NOTYET, 2, NULL, { 0 } }, + { "trusted_first: late root, untrusted first", + "dated-root-late expired-root", NULL, "dated-int cross-root", + "dated-int-leaf", NULL, + VO_XMLSEC | VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int cross-root expired-root", { 0 } }, + /* A trusted cert valid at the verification time comes first, also + * before a trusted root */ + { "trusted_first: trusted cross-root valid in 2015", + "dated-root-late expired-root cross-root", NULL, + "dated-int cross-root", "dated-int-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int cross-root expired-root", { 0 } }, + + /* Without X509_V_FLAG_TRUSTED_FIRST the issuer of an untrusted cert + * comes from the untrusted certs first, then from the trusted certs, one + * valid at the verification time first. Above a trusted cert only a + * trusted cert issues, with the flag too. */ + { "untrusted_first: trusted root valid in 2015", + "dated-root expired-root cross-root-late", NULL, "dated-int", + "dated-int-leaf", NULL, + VO_XMLSEC | VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int dated-root", { 0 } }, + { "untrusted_first: no untrusted issuer of a trusted cert", + "dated-root expired-root dated-int", NULL, "cross-root", + "dated-int-leaf", NULL, + VO_XMLSEC, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", + { 0 } }, + { "untrusted_first: no untrusted issuer of a trusted cert, no root", + "expired-root dated-int", NULL, "cross-root", "dated-int-leaf", NULL, + VO_XMLSEC | VO_T2015, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "untrusted_first: no root, trusted first", + "expired-root dated-int", NULL, "cross-root", "dated-int-leaf", NULL, + VO_T2015, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "untrusted_first: trusted cross-root valid in 2015", + "dated-root-late expired-root cross-root", NULL, "dated-int", + "dated-int-leaf", NULL, + VO_XMLSEC | VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int cross-root expired-root", { 0 } }, + + /* Without X509_V_FLAG_TRUSTED_FIRST the untrusted certs are searched in + * their order, self-signed ones included */ + { "untrusted_order: copy of the root first", + "dated-root expired-root", NULL, "dated-int dated-root#2 cross-root", + "dated-int-leaf", NULL, + VO_XMLSEC, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", + { 0 } }, + { "untrusted_order: cross-root first", + "dated-root expired-root", NULL, "dated-int cross-root dated-root#2", + "dated-int-leaf", NULL, + VO_XMLSEC, 0, 0, VE_EXPIRED, 3, NULL, { 0 } }, + + /* alt-end, issued by a cert not given, is above the untrusted twins of + * alt-int, which alt-root issued. OpenSSL goes on from the trusted + * alt-int and verifies. wolfSSL fails closed: the twin is the issuer and + * alt-end above it has none. */ + { "dead_end: twin out of date", + "alt-end alt-int alt-root", NULL, "alt-old-int", "alt-int-leaf", NULL, + VO_XMLSEC, 0, 0, VE_ISSUER, 2, NULL, { 0 } }, + { "dead_end: twin not a CA", + "alt-end alt-int alt-root", NULL, "alt-notca-int", "alt-int-leaf", + NULL, VO_XMLSEC, 0, 0, VE_ISSUER, 2, NULL, { 0 } }, + /* OpenSSL: VE_NOTYET at depth 1 on its chain through alt-int, which + * the callback accepts. The missing issuer is final as a trusted cert + * issued the leaf. */ + { "dead_end: alt-int not valid yet", + "alt-end alt-int alt-root", NULL, "alt-end-int", "alt-int-leaf", NULL, + VO_XMLSEC | VO_T2015, VA_ALL, 0, VE_ISSUER, 2, + "alt-int-leaf alt-end-int alt-end", { VE_ISSUER, 2 } }, + /* OpenSSL: VE_CA at depth 1 on its chain through the trusted twin */ + { "dead_end: trusted twin not a CA", + "alt-notca-int alt-end", NULL, "alt-end-int", "alt-int-leaf", NULL, + VO_XMLSEC, VA_ISSUER | VA_LOCAL, 0, VE_ISSUER, 2, NULL, + { VE_ISSUER, 2 } }, +#endif + + /* A trusted self-signed root ends the chain. noaki-root has no authority + * key id, so by name noaki-root-link, a trusted CA it issued, could + * issue it too. */ + { "noaki_root: root ends the chain", + NULL, "noaki-root-link noaki-root", NULL, "noaki-root-leaf", NULL, + 0, VA_ALL, 1, VE_OK, 0, "noaki-root-leaf noaki-root", { 0 } }, + + /* A cert is verified with the issuer chosen for it only. The cert + * manager finds forge-fake-int by name before forge-int. */ + { "forged_issuer: forge-int", + "forge-root", NULL, NULL, "forge-int", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "forged_issuer: bad sub first", + "forge-root", NULL, "forge-bad-sub forge-sub forge-int forge-fake-int", + "forge-sub-leaf", NULL, + 0, 0, 0, VE_LOCAL, 2, NULL, { 0 } }, + { "forged_issuer: fake int", + "forge-root", NULL, "forge-fake-int forge-q forge-int", + "forge-fake-int-leaf", NULL, + 0, 0, 0, VE_SIG, 2, NULL, { 0 } }, + + /* The issuer chosen for a cert must have signed it. notca-twin has the + * name of notca-root, which signed the leaf, but not its key. Without a + * subject key id it issues the leaf by name. */ + { "wrong_signer: trusted notca-root first", + "root notca-root", NULL, "notca-twin", "notca-root-leaf", NULL, + 0, 0, 0, VE_CA, 1, NULL, { 0 } }, + /* The untrusted twin is taken by name: VE_SIG at depth 0 */ + { "wrong_signer: untrusted first", + "root notca-root", NULL, "notca-twin", "notca-root-leaf", NULL, + VO_XMLSEC, 0, 0, VE_SIG, 0, "notca-root-leaf notca-twin root", + { 0 } }, + { "wrong_signer: only a stack issues", + "notca-root", "root", "notca-twin", "notca-root-leaf", NULL, + 0, 0, 0, VE_SIG, 0, NULL, { 0 } }, + +#ifndef NO_ASN_TIME + /* A callback that rejects an error of a trusted cert ends the + * verification. The trusted wrongca-int names wrongca as its issuer, + * whose key did not sign it. Its untrusted twin wrongca-old-int, out of + * date, has no issuer. OpenSSL: VE_SIG at depth 1 through the trusted + * twin. wolfSSL fails closed on the missing issuer. */ + { "trusted_cert_error: no root", + "wrongca-int wrongca", NULL, "wrongca-old-int", "wrongca-int-leaf", + NULL, VO_XMLSEC, VA_ISSUER | VA_LOCAL, 0, VE_LOCAL, 1, NULL, + { VE_LOCAL, 1 } }, + { "trusted_cert_error: root", + "wrongca-int wrongca wrongca-root", NULL, "wrongca-old-int", + "wrongca-int-leaf", NULL, + VO_XMLSEC, VA_ISSUER | VA_LOCAL, 0, VE_LOCAL, 1, NULL, + { VE_LOCAL, 1 } }, +#endif + + /* With a trusted stack, trusted certs come only from the stack. The + * store's dated-root has the name and key of the stack's cross-root but + * does not issue it, nor dated-int in a stack, nor any other cert. */ + { "trusted_stack_only: stack cert without issuer", + "dated-root", "cross-root", NULL, "dated-int", NULL, + 0, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "trusted_stack_only: leaf", + "dated-root", "cross-root", "dated-int", "dated-int-leaf", NULL, + 0, 0, 0, VE_ISSUER, 2, NULL, { 0 } }, + { "trusted_stack_only: dated-int in the stack", + "dated-root", "dated-int", NULL, "dated-int-leaf", NULL, + 0, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "trusted_stack_only: callback accepts, the chain ends there", + "dated-root", "dated-int", NULL, "dated-int-leaf", NULL, + 0, VA_ALL, 1, VE_ISSUER, 0, "dated-int-leaf dated-int", + { VE_ISSUER, 1 } }, + { "trusted_stack_only: partial chain", + "dated-root", "cross-root", "dated-int", "dated-int-leaf", NULL, + VO_PARTIAL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "trusted_stack_only: store CA does not issue the leaf", + "dated-root", "expired-root", NULL, "dated-int", NULL, + 0, 0, 0, VE_LOCAL, 0, NULL, { 0 } }, + { "trusted_stack_only: store CA does not issue an untrusted cert", + "dated-root", "expired-root", "dated-int", "dated-int-leaf", NULL, + 0, 0, 0, VE_LOCAL, 1, NULL, { 0 } }, + /* The top of a partial chain: its dates are not checked */ + { "trusted_stack_only: expired stack cert", + "dated-root", "expired-int", NULL, "dated-int-leaf", NULL, + 0, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "trusted_stack_only: callback accepts expired stack cert", + "dated-root", "expired-int", NULL, "dated-int-leaf", NULL, + 0, VA_ALL, 1, VE_ISSUER, 0, NULL, { VE_ISSUER, 1 } }, + { "trusted_stack_only: store root not trusted", + "dated-root", "expired-int", NULL, "dated-root", NULL, + 0, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + { "trusted_stack_only: store root not trusted, partial chain", + "dated-root", "expired-int", NULL, "dated-root", NULL, + VO_PARTIAL, 0, 0, VE_SELF0, 0, NULL, { 0 } }, + +#ifdef HAVE_CRL + /* With X509_V_FLAG_CRL_CHECK_ALL the CRL of a stack cert that no stack + * cert issues cannot be checked: its issuer is not on the chain. + * Accepting the missing issuer does not accept that. */ + { "trusted_stack_crl: missing issuer accepted", + "ca", "int1", NULL, "int2", "ca-int-crl", + VO_CRL_ALL, VA_ISSUER | VA_LOCAL, 0, VE_NOCRL, 1, NULL, + { VE_ISSUER, 1, VE_NOCRL, 1 } }, + { "trusted_stack_crl: callback accepts both", + "ca", "int1", NULL, "int2", "ca-int-crl", + VO_CRL_ALL, VA_ALL, 1, VE_NOCRL, 0, "int2 int1", + { VE_ISSUER, 1, VE_NOCRL, 1 } }, + { "trusted_stack_crl: partial chain", + "ca", "int1", NULL, "int2", "ca-int-crl", + VO_CRL_ALL | VO_PARTIAL, 0, 0, VE_NOCRL, 1, NULL, { 0 } }, +#endif + + /* A trusted root of the name of a cert's issuer issues it when the key + * ids do not tell otherwise, also when its key did not sign it. + * expired-root-nokid has expired-root's name, a new key, no key ids. */ + { "nokid_issuer: wrong key", + "expired-root-nokid", NULL, NULL, "expired-root-leaf", NULL, + 0, 0, 0, VE_SIG, 0, NULL, { 0 } }, + { "nokid_issuer: callback accepts", + "expired-root-nokid", NULL, NULL, "expired-root-leaf", NULL, + 0, VA_ALL, 1, VE_SIG, 0, "expired-root-leaf expired-root-nokid", + { VE_SIG, 0 } }, + +#ifdef HAVE_ECC + /* An issuer has a key of the type of the cert's signature. ec-name-root, + * an EC root without key ids, has the name of the RSA link, which issued + * the leaf. */ + { "keytype_issuer: EC root of the name", + "ec-name-root", NULL, NULL, "leaf", NULL, + 0, 0, 0, VE_LOCAL, 0, NULL, { 0 } }, + { "keytype_issuer: callback accepts", + "ec-name-root", NULL, NULL, "leaf", NULL, + 0, VA_ALL, 1, VE_LEAFSIG, 0, "leaf", + { VE_LOCAL, 0, VE_LEAFSIG, 0 } }, +#endif + +#ifndef NO_ASN_TIME + /* The dates of every cert of the chain are checked at the verification + * time, the trust anchor too. expired-root is valid from 2010 to 2019, + * the leaf it issued from 2000 to 2049. */ + { "anchor_dates: no check time on the store", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_S_NOTIME, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "anchor_dates: expired root", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + 0, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "anchor_dates: 2015", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "anchor_dates: 2005", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2005, 0, 0, VE_NOTYET, 1, NULL, { 0 } }, + /* The verification time wins over X509_V_FLAG_NO_CHECK_TIME */ + { "anchor_dates: 2025 and no check time", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2025 | VO_NOTIME, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "anchor_dates: no check time", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_NOTIME, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "anchor_dates: callback accepts", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, "expired-root-leaf expired-root", + { VE_EXPIRED, 1 } }, + /* A trusted leaf is checked once, as the current cert */ + { "anchor_dates: trusted leaf", + "expired-root", NULL, NULL, "expired-root", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, "expired-root", { VE_EXPIRED, 0 } }, + { "anchor_dates: copy of a stack cert", + NULL, "expired-root", NULL, "expired-root#2", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, "expired-root", { VE_EXPIRED, 0 } }, + /* With X509_V_FLAG_PARTIAL_CHAIN the first trusted cert ends the chain. + * The root above it is not checked. */ + { "anchor_dates: trusted intermediate", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, 0, 0, 0, VE_EXPIRED, 2, NULL, { 0 } }, + { "anchor_dates: trusted intermediate, partial chain", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, VO_PARTIAL, 0, 1, VE_OK, 0, NULL, { 0 } }, + /* A cert above that trusted cert is not on the chain. Its date error + * does not end the verification: cross-root-late issues the trusted + * dated-int and is valid from 2020. */ + { "anchor_dates: late cert above", + "expired-root expired-root-int dated-int", NULL, "cross-root-late", + "dated-int-leaf", NULL, + VO_T2015, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "anchor_dates: late cert above, partial chain", + "expired-root expired-root-int dated-int", NULL, "cross-root-late", + "dated-int-leaf", NULL, + VO_T2015 | VO_PARTIAL, 0, 1, VE_OK, 0, "dated-int-leaf dated-int", + { 0 } }, + /* The store is not used with a trusted stack. With the leaf in the + * stack, the partial chain ends below the store's dated-root-late, not + * valid in 2015. */ + { "anchor_dates: stack cert as the leaf", + "dated-root-late", "dated-int", NULL, "dated-int", NULL, + VO_T2015 | VO_PARTIAL, 0, 1, VE_OK, 0, "dated-int", { 0 } }, + /* As in OpenSSL, the leaf ends the partial chain only when no issuer + * is found: the untrusted dated-int is put on it */ + { "anchor_dates: stack cert as the leaf, untrusted issuer", + "dated-root-late", "dated-int-leaf", "dated-int", "dated-int-leaf", + NULL, VO_T2015 | VO_PARTIAL, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int", { 0 } }, + + /* The ctx param inherits the store's X509_V_FLAG_PARTIAL_CHAIN. As in + * OpenSSL, a new param, as xmlsec sets, does not have it. */ + { "partial_chain: store flag", + "int2", NULL, NULL, "server-int", NULL, + VO_S_PARTIAL, 0, 1, VE_OK, 0, "server-int int2", { 0 } }, + { "partial_chain: store flag, new param", + "int2", NULL, NULL, "server-int", NULL, + VO_S_PARTIAL | VO_XMLSEC, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "partial_chain: new param with the flag", + "int2", NULL, NULL, "server-int", NULL, + VO_PARTIAL | VO_XMLSEC, 0, 1, VE_OK, 0, "server-int int2", { 0 } }, + + /* As OpenSSL, the callback is told of a chain too long. Failing closed, + * the error is final: once it is accepted, OpenSSL reports VE_LOCAL at + * depth 1 and verifies. */ + { "depth: callback told", + "ca", NULL, "int1 int2", "server-int", NULL, + VO_DEPTH0, VA_ISSUER, 0, VE_LONG, 1, NULL, { VE_LONG, 1 } }, + { "depth: callback accepts", + "ca", NULL, "int1 int2", "server-int", NULL, + VO_DEPTH0, VA_ALL, 0, VE_LONG, 1, NULL, { VE_LONG, 1 } }, + + /* X509_V_FLAG_CRL_CHECK(_ALL) on the ctx without CRLs, also without + * HAVE_CRL: each cert checked has no CRL */ + { "crl_none: CRL_CHECK on the ctx", + "ca", NULL, NULL, "server", NULL, + VO_C_CRL, 0, 0, VE_NOCRL, 0, NULL, { 0 } }, + { "crl_none: CRL_CHECK on the ctx, callback accepts", + "ca", NULL, NULL, "server", NULL, + VO_C_CRL, VA_NOCRL, 1, VE_NOCRL, 0, NULL, { VE_NOCRL, 0 } }, + { "crl_none: CRL_CHECK_ALL on the ctx, partial chain", + "int2", NULL, NULL, "server-int", NULL, + VO_C_CRL_ALL | VO_PARTIAL, VA_NOCRL, 1, VE_NOCRL, 0, NULL, + { VE_NOCRL, 0, VE_NOCRL, 1 } }, + /* As OpenSSL, a self-signed anchor has its own CRLs */ + { "crl_none: CRL_CHECK_ALL on the ctx, anchor", + "ca", NULL, NULL, "server", NULL, + VO_C_CRL_ALL, VA_NOCRL, 1, VE_NOCRL, 0, NULL, + { VE_NOCRL, 0, VE_NOCRL, 1 } }, + { "crl_none: CRL_CHECK on the ctx, the anchor alone", + "ca", NULL, NULL, "ca", NULL, + VO_C_CRL, 0, 0, VE_NOCRL, 0, NULL, { 0 } }, + +#ifdef HAVE_CRL + /* The store's CRLs are checked for a cert out of date now but valid at + * the verification time. dated-root-crl revokes expired-int. */ + { "crl_dates: no CRL check", + "dated-root", NULL, NULL, "expired-int", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_dates: no CRL", + "dated-root", NULL, NULL, "expired-int", NULL, + VO_T2015 | VO_CRL, 0, 0, VE_NOCRL, 0, NULL, { 0 } }, + { "crl_dates: revoked", + "dated-root", NULL, NULL, "expired-int", "dated-root", + VO_T2015 | VO_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + /* Out of date now: revoked first, then the date error */ + { "crl_dates: dates accepted", + "dated-root", NULL, NULL, "expired-int", "dated-root", + VO_CRL, VA_DATES, 0, VE_REVOKED, 0, NULL, { VE_REVOKED, 0 } }, + { "crl_dates: callback accepts", + "dated-root", NULL, NULL, "expired-int", "dated-root", + VO_CRL, VA_ALL, 1, VE_EXPIRED, 0, NULL, + { VE_REVOKED, 0, VE_EXPIRED, 0 } }, + { "crl_dates: CRL of X509_STORE_CTX_set0_crls", + "dated-root", NULL, NULL, "expired-int", "dated-root-empty +dated-root", + VO_CRL, VA_DATES, 0, VE_REVOKED, 0, NULL, { VE_REVOKED, 0 } }, + /* As in OpenSSL, the flag on the ctx turns on the check */ + { "crl_dates: CRL_CHECK on the ctx, revoked", + "dated-root", NULL, NULL, "expired-int", "+dated-root", + VO_T2015 | VO_C_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_dates: CRL_CHECK on the ctx, no CRL", + "dated-root", NULL, NULL, "expired-int", NULL, + VO_T2015 | VO_C_CRL, 0, 0, VE_NOCRL, 0, NULL, { 0 } }, + { "crl_dates: CRL_CHECK on the ctx, xmlsec param, revoked", + "dated-root", NULL, NULL, "expired-int", "+dated-root", + VO_XMLSEC | VO_T2015 | VO_C_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_dates: CRL_CHECK on the ctx, not revoked", + "dated-root", NULL, NULL, "expired-int", "+dated-root-empty", + VO_T2015 | VO_C_CRL_ALL, 0, 1, VE_OK, 0, NULL, { 0 } }, + /* A CRL whose signature does not verify is not used: its error is + * final. Once the callback accepts it, OpenSSL takes its entries: + * VE_REVOKED, accepted. */ + { "crl_dates: bad CRL signature", + "dated-root", NULL, NULL, "expired-int", "+dated-root-badsig", + VO_T2015 | VO_CRL, 0, 0, VE_CRLSIG, 0, NULL, { 0 } }, + { "crl_dates: bad CRL signature, callback accepts", + "dated-root", NULL, NULL, "expired-int", "+dated-root-badsig", + VO_T2015 | VO_CRL, VA_ALL, 0, VE_CRLSIG, 0, NULL, { VE_CRLSIG, 0 } }, +#endif +#ifdef X509_VFY_GEN_CRLS + /* A stale CRL is reported. As in OpenSSL, once that is accepted its + * entries are still checked. A current CRL of the ctx is used over a + * stale one of the store. ca-stale and ca-revoked list server. */ + { "crl_stale: stale CRL", + "ca", NULL, NULL, "server", "ca-stale", + VO_CRL, 0, 0, VE_CRLEXP, 0, NULL, { 0 } }, + { "crl_stale: stale CRL, callback accepts it", + "ca", NULL, NULL, "server", "ca-stale", + VO_CRL, VA_CRLEXP, 0, VE_REVOKED, 0, NULL, + { VE_CRLEXP, 0, VE_REVOKED, 0 } }, + { "crl_stale: stale CRL without the cert, callback accepts it", + "ca", NULL, NULL, "server", "ca-stale-empty", + VO_CRL, VA_CRLEXP, 1, VE_CRLEXP, 0, NULL, { VE_CRLEXP, 0 } }, + { "crl_stale: current CRL of the ctx", + "ca", NULL, NULL, "server", "ca-stale-empty +ca-revoked", + VO_CRL, VA_CRLEXP, 0, VE_REVOKED, 0, NULL, { VE_REVOKED, 0 } }, + { "crl_stale: current CRL of the ctx without the cert", + "ca", NULL, NULL, "server", "ca-stale +ca-empty", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + /* X509_V_FLAG_NO_CHECK_TIME skips the dates of CRLs too */ + { "crl_stale: stale CRL, no time check", + "ca", NULL, NULL, "server", "ca-stale", + VO_CRL | VO_NOTIME, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_stale: stale CRL without the cert, no time check", + "ca", NULL, NULL, "server", "ca-stale-empty", + VO_CRL | VO_NOTIME, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_stale: stale CRL of the ctx, no time check on the store", + "ca", NULL, NULL, "server", "+ca-stale", + VO_C_CRL | VO_S_NOTIME, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + /* As OpenSSL, the dates of a CRL before its signature */ + { "crl_stale: stale CRL with a bad signature", + "ca", NULL, NULL, "server", "ca-stale-empty-badsig", + VO_CRL, 0, 0, VE_CRLEXP, 0, NULL, { 0 } }, + { "crl_stale: stale CRL with a bad signature, callback accepts it", + "ca", NULL, NULL, "server", "ca-stale-empty-badsig", + VO_CRL, VA_CRLEXP, 0, VE_CRLSIG, 0, NULL, + { VE_CRLEXP, 0, VE_CRLSIG, 0 } }, + /* As OpenSSL, the CRLs of the ctx are used over the store's, which are + * not looked at */ + { "crl_stale: current CRL of the ctx, the store's revokes", + "ca", NULL, NULL, "server", "ca-revoked +ca-empty", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_stale: current CRL of the ctx, the store's does not verify", + "ca", NULL, NULL, "server", "ca-empty-badsig +ca-empty", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_stale: stale CRL of the ctx, the store's revokes", + "ca", NULL, NULL, "server", "ca-revoked +ca-stale-empty", + VO_CRL, VA_CRLEXP, 1, VE_CRLEXP, 0, NULL, { VE_CRLEXP, 0 } }, + { "crl_stale: CRL of the store not yet valid", + "ca", NULL, NULL, "server", "ca-nyv-empty", + VO_CRL, 0, 0, VE_CRLNYV, 0, NULL, { 0 } }, + { "crl_stale: CRL of the store not yet valid, current one of the ctx", + "ca", NULL, NULL, "server", "ca-nyv-empty +ca-empty", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + /* Not yet valid over expired, as OpenSSL takes the CRL of the latest + * thisUpdate */ + { "crl_stale: CRLs of the ctx expired and not yet valid", + "ca", NULL, NULL, "server", "+ca-stale-empty +ca-nyv-empty", + VO_CRL, 0, 0, VE_CRLNYV, 0, NULL, { 0 } }, + { "crl_stale: CRLs of the ctx not yet valid and expired", + "ca", NULL, NULL, "server", "+ca-nyv-empty +ca-stale-empty", + VO_CRL, 0, 0, VE_CRLNYV, 0, NULL, { 0 } }, + + /* As OpenSSL's get_crl_sk(), only the best CRL is checked: a current + * one, then the latest. ca-otherkey has the name of ca, no AKID and + * another key: its signature fails. The -old CRLs are a day older. */ + { "crl_pick: older CRL of another key", + "ca", NULL, NULL, "server", "ca-otherkey-old ca-empty", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_pick: older CRL of another key, added last", + "ca", NULL, NULL, "server", "ca-empty ca-otherkey-old", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_pick: newer CRL of another key", + "ca", NULL, NULL, "server", "ca-otherkey ca-empty-old", + VO_CRL, 0, 0, VE_CRLSIG, 0, NULL, { 0 } }, + { "crl_pick: newer CRL of another key, added last", + "ca", NULL, NULL, "server", "ca-empty-old ca-otherkey", + VO_CRL, 0, 0, VE_CRLSIG, 0, NULL, { 0 } }, + { "crl_pick: older CRL of another key, ctx", + "ca", NULL, NULL, "server", "+ca-otherkey-old +ca-empty", + VO_C_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_pick: older CRL of another key, ctx, last", + "ca", NULL, NULL, "server", "+ca-empty +ca-otherkey-old", + VO_C_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_pick: newer CRL of another key, ctx", + "ca", NULL, NULL, "server", "+ca-otherkey +ca-empty-old", + VO_C_CRL, 0, 0, VE_CRLSIG, 0, NULL, { 0 } }, + { "crl_pick: newer CRL of another key, ctx, last", + "ca", NULL, NULL, "server", "+ca-empty-old +ca-otherkey", + VO_C_CRL, 0, 0, VE_CRLSIG, 0, NULL, { 0 } }, + { "crl_pick: current CRL over a newer one not yet valid", + "ca", NULL, NULL, "server", "ca-revoked ca-nyv-empty", + VO_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_pick: current CRL over a newer one not yet valid, ctx", + "ca", NULL, NULL, "server", "+ca-nyv-empty +ca-revoked", + VO_C_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_pick: same thisUpdate, the first", + "ca", NULL, NULL, "server", "ca-revoked ca-empty", + VO_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_pick: same thisUpdate, the first, other order", + "ca", NULL, NULL, "server", "ca-empty ca-revoked", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_pick: same thisUpdate, the first, ctx", + "ca", NULL, NULL, "server", "+ca-revoked +ca-empty", + VO_C_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_pick: same thisUpdate, the first, ctx, other order", + "ca", NULL, NULL, "server", "+ca-empty +ca-revoked", + VO_C_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + + /* As OpenSSL, a self-signed anchor has its own CRLs: checked with + * X509_V_FLAG_CRL_CHECK_ALL, or as the leaf. ca-self lists ca. */ + { "crl_anchor: anchor revoked", + "ca", NULL, NULL, "server", "ca-self", + VO_CRL_ALL, 0, 0, VE_REVOKED, 1, NULL, { 0 } }, + { "crl_anchor: anchor revoked, CRL_CHECK_ALL on the ctx", + "ca", NULL, NULL, "server", "+ca-self", + VO_C_CRL_ALL, 0, 0, VE_REVOKED, 1, NULL, { 0 } }, + { "crl_anchor: anchor revoked, only the leaf checked", + "ca", NULL, NULL, "server", "ca-self", + VO_CRL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "crl_anchor: anchor revoked, the leaf", + "ca", NULL, NULL, "ca", "ca-self", + VO_CRL, 0, 0, VE_REVOKED, 0, NULL, { 0 } }, + { "crl_anchor: anchor not revoked", + "ca", NULL, NULL, "server", "ca-empty", + VO_CRL_ALL, 0, 1, VE_OK, 0, NULL, { 0 } }, +#endif + + /* The error is reported at the CA whose path length is exceeded: + * pl0-int, of pathlen:0, issued the CA pl0-sub */ + { "path_len: trusted CA", + "ca pl0-int", NULL, "pl0-sub", "pl0-leaf", NULL, + 0, 0, 0, VE_PATHLEN, 2, NULL, { 0 } }, + { "path_len: untrusted CA", + "ca", NULL, "pl0-int pl0-sub", "pl0-leaf", NULL, + 0, 0, 0, VE_PATHLEN, 2, NULL, { 0 } }, + { "path_len: callback accepts", + "ca", NULL, "pl0-int pl0-sub", "pl0-leaf", NULL, + 0, VA_ALL, 1, VE_PATHLEN, 0, "pl0-leaf pl0-sub pl0-int ca", + { VE_PATHLEN, 2 } }, + { "path_len: partial chain", + "pl0-int", NULL, "pl0-sub", "pl0-leaf", NULL, + VO_PARTIAL, VA_ALL, 1, VE_PATHLEN, 0, "pl0-leaf pl0-sub pl0-int", + { VE_PATHLEN, 2 } }, + +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) +#ifdef WOLFSSL_SIGNER_DER_CERT + /* With X509_V_FLAG_TRUSTED_FIRST the trusted dated-int of an SSL_CTX is + * taken over an untrusted twin out of date, then its issuer there */ + { "ctx_store_twin: only the root trusted", + "dated-root", NULL, "expired-int", "dated-int-leaf", NULL, + VO_SSLCTX, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "ctx_store_twin: trusted twin", + "dated-root dated-int", NULL, "expired-int", "dated-int-leaf", NULL, + VO_SSLCTX, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int dated-root", { 0 } }, + { "ctx_store_twin: trusted twin, 2005", + "dated-root dated-int", NULL, "expired-int", "dated-int-leaf", NULL, + VO_SSLCTX | VO_T2005, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int dated-root", { 0 } }, + { "ctx_store_twin: trusted twin, 2015", + "dated-root dated-int", NULL, "expired-int", "dated-int-leaf", NULL, + VO_SSLCTX | VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int dated-root", { 0 } }, + { "ctx_store_twin: untrusted first", + "dated-root dated-int", NULL, "expired-int", "dated-int-leaf", NULL, + VO_SSLCTX | VO_XMLSEC, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + /* A CA of an SSL_CTX is a trusted cert: it goes on the chain, then its + * issuer there, each checked as in a store: pl0-int, of pathlen:0, may + * not issue the CA pl0-sub, and a CA:FALSE root may not issue */ + { "ctx_store_ca: path length", + "ca pl0-int", NULL, "pl0-sub", "pl0-leaf", NULL, + VO_SSLCTX, 0, 0, VE_PATHLEN, 2, NULL, { 0 } }, + { "ctx_store_ca: path length, callback accepts", + "ca pl0-int", NULL, "pl0-sub", "pl0-leaf", NULL, + VO_SSLCTX, VA_ALL, 1, VE_PATHLEN, 0, NULL, { VE_PATHLEN, 2 } }, + { "ctx_store_ca: CA:FALSE root", + "notca-root", NULL, NULL, "notca-root-leaf", NULL, + VO_SSLCTX, 0, 0, VE_CA, 1, NULL, { 0 } }, + { "ctx_store_ca: path length of the root of a trusted CA", + "ca pl0-sub pl0-int", NULL, NULL, "pl0-leaf", NULL, + VO_SSLCTX, 0, 0, VE_PATHLEN, 2, NULL, { 0 } }, + { "ctx_store_ca: expired root of a trusted CA", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, VO_SSLCTX, 0, 0, VE_EXPIRED, 2, NULL, { 0 } }, + { "ctx_store_ca: root of a trusted CA, 2015", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, VO_SSLCTX | VO_T2015, 0, 1, VE_OK, 0, + "expired-root-int-leaf expired-root-int expired-root", { 0 } }, +#ifdef HAVE_CRL + { "ctx_store_ca: root revokes a trusted CA", + "dated-root expired-int", NULL, NULL, "dated-int-leaf", + "dated-root int", VO_SSLCTX | VO_T2015 | VO_CRL_ALL, 0, 0, + VE_REVOKED, 1, NULL, { 0 } }, +#endif + /* wolfSSL trusts a CA of an SSL_CTX without its issuer there, as its + * handshake does. OpenSSL: VE_ISSUER at depth 2. */ + { "ctx_store_ca: CA without its root", + "int1", NULL, "int2", "server-int", NULL, + VO_SSLCTX, 0, 1, VE_OK, 0, "server-int int2 int1", { 0 } }, + /* That CA is the trust anchor: it does not count against the depth, + * as the anchor of a partial chain in OpenSSL. OpenSSL, without the + * root: VE_LONG at depth 1 and 2 where these verify. */ + { "ctx_store_ca: CA without its root, depth 0", + "int2", NULL, NULL, "server-int", NULL, + VO_SSLCTX | VO_DEPTH0, 0, 1, VE_OK, 0, "server-int int2", { 0 } }, + { "ctx_store_ca: CA without its root, intermediate, depth 0", + "int1", NULL, "int2", "server-int", NULL, + VO_SSLCTX | VO_DEPTH0, 0, 0, VE_LONG, 1, NULL, { 0 } }, + { "ctx_store_ca: CA without its root, intermediate, depth 1", + "int1", NULL, "int2", "server-int", NULL, + VO_SSLCTX | VO_DEPTH1, 0, 1, VE_OK, 0, "server-int int2 int1", + { 0 } }, +#else + /* Without its DER a CA of an SSL_CTX cannot go on the chain: the cert + * it issued has no issuer */ + { "ctx_store_ca: CA without DER", + "ca pl0-int", NULL, "pl0-sub", "pl0-leaf", NULL, + VO_SSLCTX, 0, 0, VE_LOCAL, 1, NULL, { 0 } }, +#endif +#endif + + /* An untrusted intermediate is date checked when verified, at the + * verification time, and one valid then is chosen first. expired-int is + * valid from 2010 to 2019, dated-int is the same CA valid until 2049. */ + { "untrusted_dates: expired", + "dated-root", NULL, "expired-int", "dated-int-leaf", NULL, + 0, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "untrusted_dates: 2015", + "dated-root", NULL, "expired-int", "dated-int-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "untrusted_dates: callback accepts", + "dated-root", NULL, "expired-int", "dated-int-leaf", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, "dated-int-leaf expired-int dated-root", + { VE_EXPIRED, 1 } }, +#ifdef HAVE_CRL + /* The store's CRLs are checked for it too, dated-root-crl revokes it */ + { "untrusted_dates: revoked", + "dated-root", NULL, "expired-int", "dated-int-leaf", "dated-root int", + VO_T2015 | VO_CRL_ALL, 0, 0, VE_REVOKED, 1, NULL, { 0 } }, + { "untrusted_dates: callback accepts revoked", + "dated-root", NULL, "expired-int", "dated-int-leaf", "dated-root int", + VO_T2015 | VO_CRL_ALL, VA_ALL, 1, VE_REVOKED, 0, + "dated-int-leaf expired-int dated-root", { VE_REVOKED, 1 } }, + /* Out of date now: revoked first, then the date error */ + { "untrusted_dates: revoked, dates accepted", + "dated-root", NULL, "expired-int", "dated-int-leaf", "dated-root int", + VO_CRL_ALL, VA_DATES, 0, VE_REVOKED, 1, NULL, { VE_REVOKED, 1 } }, + { "untrusted_dates: callback accepts revoked and expired", + "dated-root", NULL, "expired-int", "dated-int-leaf", "dated-root int", + VO_CRL_ALL, VA_ALL, 1, VE_EXPIRED, 0, NULL, + { VE_REVOKED, 1, VE_EXPIRED, 1 } }, +#endif + { "untrusted_dates: valid twin chosen", + "dated-root", NULL, "expired-int dated-int", "dated-int-leaf", NULL, + 0, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", { 0 } }, + /* With X509_V_FLAG_TRUSTED_FIRST a trusted issuer is taken first, also + * one out of date */ + { "untrusted_dates: trusted twin out of date", + "dated-root expired-int", NULL, "expired-int dated-int", + "dated-int-leaf", NULL, + 0, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "untrusted_dates: trusted twin out of date, 2015", + "dated-root expired-int", NULL, "expired-int dated-int", + "dated-int-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "untrusted_dates: trusted twin out of date, untrusted first", + "dated-root expired-int", NULL, "expired-int dated-int", + "dated-int-leaf", NULL, + VO_XMLSEC, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", + { 0 } }, + /* The top of a partial chain is not date checked */ + { "untrusted_dates: top of a partial chain", + NULL, NULL, "expired-int", "dated-int-leaf", NULL, + 0, VA_LOCAL, 1, VE_LOCAL, 0, NULL, { VE_LOCAL, 1 } }, + /* Unless it is a lone leaf */ + { "untrusted_dates: lone leaf", + NULL, NULL, NULL, "expired-int", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, "expired-int", + { VE_LOCAL, 0, VE_LEAFSIG, 0, VE_EXPIRED, 0 } }, + /* With an untrusted issuer above it, it is date checked. A rejection is + * final. */ + { "untrusted_dates: untrusted issuer above", + NULL, NULL, "expired-int cross-root", "dated-int-leaf", NULL, + 0, VA_LOCAL, 0, VE_EXPIRED, 1, NULL, + { VE_LOCAL, 2, VE_EXPIRED, 1 } }, + /* Also with a trusted issuer above that the cert manager does not find: + * badkid-int names nokid-root by a key id that does not match */ + { "untrusted_dates: trusted issuer by name", + "nokid-root", NULL, "badkid-int", "badkid-int-leaf", NULL, + 0, VA_LOCAL, 0, VE_EXPIRED, 1, NULL, { VE_EXPIRED, 1 } }, + { "untrusted_dates: trusted issuer by name, 2015", + "nokid-root", NULL, "badkid-int", "badkid-int-leaf", NULL, + VO_T2015, VA_LOCAL, 1, VE_OK, 0, + "badkid-int-leaf badkid-int nokid-root", { 0 } }, + { "untrusted_dates: top of a partial chain, partial chain flag", + NULL, NULL, "expired-int", "dated-int-leaf", NULL, + VO_PARTIAL, VA_LOCAL, 0, VE_EXPIRED, 1, NULL, + { VE_LOCAL, 1, VE_EXPIRED, 1 } }, + /* A trusted one that ends a partial chain */ + { "untrusted_dates: trusted, partial chain", + "expired-int", NULL, NULL, "dated-int-leaf", NULL, + VO_PARTIAL, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "untrusted_dates: trusted, partial chain, 2015", + "expired-int", NULL, NULL, "dated-int-leaf", NULL, + VO_PARTIAL | VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "untrusted_dates: trusted with its root, partial chain", + "expired-int dated-root", NULL, NULL, "dated-int-leaf", NULL, + VO_PARTIAL, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + /* Also when the depth limit ends the chain at it */ + { "untrusted_dates: trusted with its root, partial chain, depth 0", + "expired-int dated-root", NULL, NULL, "dated-int-leaf", NULL, + VO_PARTIAL | VO_DEPTH0, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + + /* A store of its own takes a trusted cert that is out of date. Its dates + * are checked at the verification time. */ + { "add_cert_expired: expired root", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + 0, 0, 0, VE_EXPIRED, 1, NULL, { 0 } }, + { "add_cert_expired: 2005", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2005, 0, 0, VE_NOTYET, 1, NULL, { 0 } }, + { "add_cert_expired: 2015", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: callback accepts", + "expired-root", NULL, NULL, "expired-root-leaf", NULL, + 0, VA_ALL, 1, VE_EXPIRED, 0, NULL, { VE_EXPIRED, 1 } }, + /* A valid root of the same name with a new key does not hide it. Nor is + * it the anchor of a leaf it did not sign: renewed-root signed + * renewed-root-leaf and is valid from 2020. Both orders. In 2015 + * expired-root, valid then, is taken by name for renewed-root-leaf: + * VE_SIG at depth 0. */ + { "add_cert_expired: renewed root first", + "renewed-root expired-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: renewed root first, renewed leaf", + "renewed-root expired-root", NULL, NULL, "renewed-root-leaf", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: renewed root first, renewed leaf, 2015", + "renewed-root expired-root", NULL, NULL, "renewed-root-leaf", NULL, + VO_T2015, 0, 0, VE_SIG, 0, "renewed-root-leaf expired-root", { 0 } }, + { "add_cert_expired: renewed root last", + "expired-root renewed-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: renewed root last, renewed leaf", + "expired-root renewed-root", NULL, NULL, "renewed-root-leaf", NULL, + 0, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: renewed root last, renewed leaf, 2015", + "expired-root renewed-root", NULL, NULL, "renewed-root-leaf", NULL, + VO_T2015, 0, 0, VE_SIG, 0, "renewed-root-leaf expired-root", { 0 } }, + /* A root not yet valid when added anchors a chain once valid */ + { "add_cert_expired: root added in 1999", + "dated-root", NULL, NULL, "dated-int", NULL, + VO_ADD1999, 0, 1, VE_OK, 0, NULL, { 0 } }, + /* A trusted root comes before an untrusted cross-signed copy of it */ + { "add_cert_expired: trusted root before cross-root", + "dated-root expired-root", NULL, "dated-int cross-root", + "dated-int-leaf", NULL, + 0, 0, 1, VE_OK, 0, "dated-int-leaf dated-int dated-root", { 0 } }, + /* Of two roots with the same name and key, the anchor is the one valid + * at the verification time: dated-root-late is valid from 2020 */ + { "add_cert_expired: late twin of the root", + "dated-root-late dated-root", NULL, "dated-int", "dated-int-leaf", + NULL, VO_T2015, 0, 1, VE_OK, 0, + "dated-int-leaf dated-int dated-root", { 0 } }, + /* With a trusted stack the store's certs are not used */ + { "add_cert_expired: stack without the root", + "expired-root", "expired-root-int", NULL, "expired-root-int-leaf", + NULL, 0, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + { "add_cert_expired: stack without the root, 2015", + "expired-root", "expired-root-int", NULL, "expired-root-int-leaf", + NULL, VO_T2015, 0, 0, VE_ISSUER, 1, NULL, { 0 } }, + /* Nor does it end a chain whose missing issuer the callback accepts */ + { "add_cert_expired: stack without the root, callback accepts", + "expired-root", "expired-root-int", NULL, "expired-root-int-leaf", + NULL, 0, VA_ALL, 1, VE_ISSUER, 0, + "expired-root-int-leaf expired-root-int", { VE_ISSUER, 1 } }, + /* Nor does a root of the missing issuer's name with another key: + * renewed-root did not sign cross-root. It is not valid in 2015 either. */ + { "add_cert_expired: renewed root not the issuer", + "renewed-root", NULL, "dated-int cross-root", "dated-int-leaf", NULL, + VO_T2015, VA_ALL, 1, VE_LOCAL, 0, + "dated-int-leaf dated-int cross-root", { VE_LOCAL, 2 } }, + /* Nor for a lone leaf, whose signature is then reported unverified */ + { "add_cert_expired: renewed root, lone leaf", + "renewed-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, VA_ALL, 1, VE_LEAFSIG, 0, "expired-root-leaf", + { VE_LOCAL, 0, VE_LEAFSIG, 0 } }, + { "add_cert_expired: renewed root, lone leaf, missing issuer accepted", + "renewed-root", NULL, NULL, "expired-root-leaf", NULL, + VO_T2015, VA_LOCAL, 0, VE_LEAFSIG, 0, NULL, + { VE_LOCAL, 0, VE_LEAFSIG, 0 } }, + /* With X509_V_FLAG_PARTIAL_CHAIN the first trusted cert ends the chain. + * The root above it is not used. */ + { "add_cert_expired: trusted intermediate", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, 0, 0, 0, VE_EXPIRED, 2, NULL, { 0 } }, + { "add_cert_expired: trusted intermediate in a stack", + NULL, "expired-root-int expired-root", NULL, "expired-root-int-leaf", + NULL, 0, 0, 0, VE_EXPIRED, 2, NULL, { 0 } }, + { "add_cert_expired: trusted intermediate, partial chain", + "expired-root expired-root-int", NULL, NULL, "expired-root-int-leaf", + NULL, VO_PARTIAL, 0, 1, VE_OK, 0, + "expired-root-int-leaf expired-root-int", { 0 } }, + { "add_cert_expired: trusted intermediate in a stack, partial chain", + NULL, "expired-root-int expired-root", NULL, "expired-root-int-leaf", + NULL, VO_PARTIAL, 0, 1, VE_OK, 0, NULL, { 0 } }, + { "add_cert_expired: renewed root not the issuer, partial chain", + "renewed-root", NULL, "dated-int cross-root", "dated-int-leaf", NULL, + VO_T2015 | VO_PARTIAL, VA_ALL, 1, VE_LOCAL, 0, + "dated-int-leaf dated-int cross-root", { VE_LOCAL, 2 } }, + + /* An untrusted self-signed cert issues a cert that no other cert + * issues. It ends the chain as VE_SELF. */ + { "untrusted_root: self-signed in the chain", + NULL, NULL, "dated-root", "dated-int", NULL, + 0, 0, 0, VE_SELF, 1, NULL, { 0 } }, + { "untrusted_root: missing issuer accepted", + NULL, NULL, "dated-root", "dated-int", NULL, + 0, VA_LOCAL, 0, VE_SELF, 1, NULL, { VE_SELF, 1 } }, + { "untrusted_root: callback accepts", + NULL, NULL, "dated-root", "dated-int", NULL, + 0, VA_SELF | VA_ISSUER | VA_LOCAL, 1, VE_SELF, 0, + "dated-int dated-root", { VE_SELF, 1 } }, + { "untrusted_root: untrusted first", + NULL, NULL, "dated-root", "dated-int", NULL, + VO_XMLSEC, 0, 0, VE_SELF, 1, NULL, { 0 } }, + { "untrusted_root: partial chain", + NULL, NULL, "dated-root", "dated-int", NULL, + VO_PARTIAL, 0, 0, VE_SELF, 1, NULL, { 0 } }, + { "untrusted_root: trusted", + "dated-root", NULL, "dated-root", "dated-int", NULL, + 0, 0, 1, VE_OK, 0, "dated-int dated-root", { 0 } }, + /* A trusted leaf ends a partial chain without a trusted cert, as + * OpenSSL's last resort, also above a self-signed cert */ + { "untrusted_root: trusted leaf", + "dated-int", NULL, "dated-root", "dated-int", NULL, + 0, 0, 0, VE_SELF, 1, NULL, { 0 } }, + { "untrusted_root: trusted leaf, partial chain", + "dated-int", NULL, "dated-root", "dated-int", NULL, + VO_PARTIAL, 0, 1, VE_OK, 0, "dated-int dated-root", { 0 } }, + + /* A trusted self-issued cert that is not self-signed is not a root. + * expired-root-link has expired-root's name and a new key. The chain + * goes on to expired-root, out of date now, and its dates are checked at + * the verification time. */ + { "selfissued_dates: link as the leaf", + "expired-root", NULL, NULL, "expired-root-link", NULL, + 0, 0, 0, VE_EXPIRED, 1, "expired-root-link expired-root", { 0 } }, + { "selfissued_dates: link as the leaf, 2005", + "expired-root", NULL, NULL, "expired-root-link", NULL, + VO_T2005, 0, 0, VE_NOTYET, 1, NULL, { 0 } }, + { "selfissued_dates: link as the leaf, 2015", + "expired-root", NULL, NULL, "expired-root-link", NULL, + VO_T2015, 0, 1, VE_OK, 0, "expired-root-link expired-root", { 0 } }, + { "selfissued_dates: link trusted, 2015", + "expired-root expired-root-link", NULL, NULL, + "expired-root-link-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, + "expired-root-link-leaf expired-root-link expired-root", { 0 } }, + { "selfissued_dates: link trusted", + "expired-root expired-root-link", NULL, NULL, + "expired-root-link-leaf", NULL, + 0, 0, 0, VE_EXPIRED, 2, NULL, { 0 } }, + /* A valid root of the name without key ids did not sign the link. It is + * taken by name as it is valid now: VE_SIG at depth 1. */ + { "selfissued_dates: valid nokid twin of the root", + "expired-root expired-root-link expired-root-nokid", NULL, NULL, + "expired-root-link-leaf", NULL, + 0, 0, 0, VE_SIG, 1, + "expired-root-link-leaf expired-root-link expired-root-nokid", + { 0 } }, + { "selfissued_dates: valid nokid twin of the root, 2015", + "expired-root expired-root-link expired-root-nokid", NULL, NULL, + "expired-root-link-leaf", NULL, + VO_T2015, 0, 1, VE_OK, 0, + "expired-root-link-leaf expired-root-link expired-root", { 0 } }, + /* Alone, the link out of date ends a partial chain the callback + * accepts. Its dates are not checked then. */ + { "selfissued_dates: old link alone", + "expired-root-old-link", NULL, NULL, "expired-root-link-leaf", NULL, + 0, VA_ALL, 1, VE_ISSUER, 0, NULL, { VE_ISSUER, 1 } }, +#endif + +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(IGNORE_NAME_CONSTRAINTS) + /* The names of every cert meet the name constraints of the certs above + * it, the anchor's too: nc-ca permits only the email addresses of + * .wolfssl.com */ + { "name_constraints: email permitted", + "nc-ca", NULL, NULL, "nc-leaf", NULL, + 0, 0, 1, VE_OK, 0, "nc-leaf nc-ca", { 0 } }, + { "name_constraints: email not permitted", + "nc-ca", NULL, NULL, "nc-leaf-bad", NULL, + 0, 0, 0, VE_NCPERM, 0, NULL, { 0 } }, + { "name_constraints: callback accepts", + "nc-ca", NULL, NULL, "nc-leaf-bad", NULL, + 0, VA_ALL, 1, VE_NCPERM, 0, "nc-leaf-bad nc-ca", { VE_NCPERM, 0 } }, +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + { "name_constraints: CA of an SSL_CTX", + "nc-ca", NULL, NULL, "nc-leaf-bad", NULL, + VO_SSLCTX, VA_ALL, 1, VE_NCPERM, 0, "nc-leaf-bad nc-ca", + { VE_NCPERM, 0 } }, +#endif + /* The CN of the leaf, CA or not, meets the DNS constraints when it has + * no dNSName SAN: ncdns-ca permits .example.com but .blocked.example.com */ + { "name_constraints: CA leaf CN permitted", + "ncdns-ca", NULL, NULL, "ncdns-leaf", NULL, + 0, 0, 1, VE_OK, 0, "ncdns-leaf ncdns-ca", { 0 } }, + { "name_constraints: CA leaf CN not permitted", + "ncdns-ca", NULL, NULL, "ncdns-leaf-bad", NULL, + 0, 0, 0, VE_NCPERM, 0, NULL, { 0 } }, + { "name_constraints: CA leaf CN excluded", + "ncdns-ca", NULL, NULL, "ncdns-leaf-excl", NULL, + 0, 0, 0, VE_NCEXCL, 0, NULL, { 0 } }, +#endif + +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_ASN_TIME) && !defined(ALLOW_INVALID_CERTSIGN) + /* Without X509_V_FLAG_TRUSTED_FIRST the untrusted twin-bad, a CA without + * keyCertSign, issues the leaf. twin-good is the same CA, trusted, with + * keyCertSign. Once the callback accepts VE_CA, VE_CERTSIGN is reported + * at depth 1, so a callback that accepts VE_CA only rejects. */ + { "no_keycertsign: callback accepts", + "ca twin-good", NULL, "twin-bad", "twin-leaf", NULL, + VO_XMLSEC, VA_ALL, 1, VE_CERTSIGN, 0, "twin-leaf twin-bad ca", + { VE_CA, 1, VE_CERTSIGN, 1 } }, + { "no_keycertsign: callback accepts VE_CA only", + "ca twin-good", NULL, "twin-bad", "twin-leaf", NULL, + VO_XMLSEC, VA_CA, 0, VE_CERTSIGN, 1, "twin-leaf twin-bad ca", + { VE_CA, 1, VE_CERTSIGN, 1 } }, +#ifdef X509_VFY_GEN_CRLS + /* nocrlsign has keyUsage keyCertSign only. Its CRL nocrlsign-revoked, + * listing nocrlsign-leaf, is reported. As in OpenSSL, once that is + * accepted, the CRL is used. */ + { "crl_sign: issuer without cRLSign", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "+nocrlsign-revoked", + VO_C_CRL, 0, 0, VE_CRLKU, 0, NULL, { 0 } }, + { "crl_sign: callback accepts a missing CRL", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "+nocrlsign-revoked", + VO_C_CRL, VA_NOCRL, 0, VE_CRLKU, 0, NULL, { VE_CRLKU, 0 } }, + { "crl_sign: callback accepts it", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "+nocrlsign-revoked", + VO_C_CRL, VA_CRLKU, 0, VE_REVOKED, 0, NULL, + { VE_CRLKU, 0, VE_REVOKED, 0 } }, + { "crl_sign: callback accepts it, store CRLs", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "ca-empty nocrlsign-revoked", + VO_CRL_ALL, VA_CRLKU, 0, VE_REVOKED, 0, NULL, + { VE_CRLKU, 0, VE_REVOKED, 0 } }, + /* As OpenSSL's check_crl(), cRLSign before the CRL dates */ + { "crl_sign: stale CRL", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "+nocrlsign-stale-empty", + VO_C_CRL, 0, 0, VE_CRLKU, 0, NULL, { 0 } }, + { "crl_sign: stale CRL, callback accepts both, store CRLs", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", + "ca-empty nocrlsign-stale-empty", + VO_CRL_ALL, VA_CRLKU | VA_CRLEXP, 1, VE_CRLEXP, 0, NULL, + { VE_CRLKU, 0, VE_CRLEXP, 0 } }, + { "crl_sign: stale CRL, callback accepts both", + "ca", NULL, "nocrlsign", "nocrlsign-leaf", "+nocrlsign-stale-empty", + VO_C_CRL, VA_CRLKU | VA_CRLEXP, 1, VE_CRLEXP, 0, NULL, + { VE_CRLKU, 0, VE_CRLEXP, 0 } }, +#endif + /* pl0-notca is not a CA. As in OpenSSL, it counts against the path + * length of pl0-ca, of pathlen:0, also once its VE_CA is accepted. */ +#ifndef WOLFSSL_X509_STORE_ALLOW_NON_CA_INTERMEDIATE + { "path_len: not a CA, callback accepts VE_CA", + "ca", NULL, "pl0-ca pl0-notca", "pl0-notca-leaf", NULL, + 0, VA_CA, 0, VE_PATHLEN, 2, NULL, { VE_CA, 1, VE_PATHLEN, 2 } }, +#else + { "path_len: not a CA", + "ca", NULL, "pl0-ca pl0-notca", "pl0-notca-leaf", NULL, + 0, 0, 0, VE_PATHLEN, 2, NULL, { 0 } }, +#endif +#endif +}; + +/* Files of the names not in TA_CERT_DIR */ +static const struct { + const char* name; + const char* file; +} x509VfyFiles[] = { + { "ca", "./certs/ca-cert.pem" }, + { "int1", "./certs/intermediate/ca-int-cert.pem" }, + { "int2", "./certs/intermediate/ca-int2-cert.pem" }, + { "server-int", "./certs/intermediate/server-int-cert.pem" }, + { "server", "./certs/server-cert.pem" }, + { "ca-ecc", "./certs/ca-ecc-cert.pem" }, + { "int1-ecc", "./certs/intermediate/ca-int-ecc-cert.pem" }, + { "int2-ecc", "./certs/intermediate/ca-int2-ecc-cert.pem" }, + { "server-int-ecc", "./certs/intermediate/server-int-ecc-cert.pem" }, + { "nca-ca", "./certs/intermediate/ca_false_intermediate/test_ca.pem" }, + { "nca-int", + "./certs/intermediate/ca_false_intermediate/test_int_not_cacert.pem" }, + { "nca-leaf", + "./certs/intermediate/ca_false_intermediate/test_sign_bynoca_srv.pem" }, + { "ca-int-crl", "./certs/crl/ca-int.pem" }, + { "nc-ca", "./certs/test/cert-ext-nc.pem" }, + { "ncdns-ca", "./certs/test/cert-ext-ncmulti.pem" }, + { "pl0-int", "./certs/test-pathlen/chainF-ICA2-pathlen0.pem" }, + { "pl0-sub", "./certs/test-pathlen/chainF-ICA1-pathlen1.pem" }, + { "pl0-leaf", "./certs/test-pathlen/chainF-entity.pem" } +}; + +/* The certs by name, loaded once */ +#define X509_VFY_NAME_SZ 32 +static struct { + char name[X509_VFY_NAME_SZ]; + X509* x509; +} x509VfyCerts[96]; +static int x509VfyCertsNum = 0; + +/* What the verify callback saw */ +static int x509VfyAccept; +static struct { + int err; + int depth; + X509* cert; +} x509VfyLog[4]; +static int x509VfyLogNum; + +/* Copies the next space separated name of *list to name. Returns 0 when + * there is none. */ +static int x509_vfy_next(const char** list, char* name) +{ + const char* p = *list; + int n = 0; + + if (p == NULL) + return 0; + while (*p == ' ') + p++; + while (*p != '\0' && *p != ' ' && n < X509_VFY_NAME_SZ - 1) + name[n++] = *p++; + name[n] = '\0'; + *list = p; + return n > 0; +} + +/* Writes the file of a cert or CRL name to path */ +static void x509_vfy_path(const char* name, const char* suffix, char* path, + int pathSz) +{ + char base[X509_VFY_NAME_SZ]; + size_t i; + int n = 0; + + while (name[n] != '\0' && name[n] != '#' && n < X509_VFY_NAME_SZ - 1) { + base[n] = name[n]; + n++; + } + base[n] = '\0'; + for (i = 0; i < XELEM_CNT(x509VfyFiles); i++) { + if (XSTRCMP(base, x509VfyFiles[i].name) == 0) { + XSNPRINTF(path, (size_t)pathSz, "%s", x509VfyFiles[i].file); + return; + } + } + XSNPRINTF(path, (size_t)pathSz, "%s%s%s", TA_CERT_DIR, base, suffix); +} + +/* Keeps x509 under name */ +static int x509_vfy_keep(const char* name, X509* x509) +{ + if (x509 == NULL || x509VfyCertsNum == (int)XELEM_CNT(x509VfyCerts)) + return 0; + XSTRNCPY(x509VfyCerts[x509VfyCertsNum].name, name, X509_VFY_NAME_SZ - 1); + x509VfyCerts[x509VfyCertsNum].name[X509_VFY_NAME_SZ - 1] = '\0'; + x509VfyCerts[x509VfyCertsNum].x509 = x509; + x509VfyCertsNum++; + return 1; +} + +/* The cert of name, loaded on first use. NULL on failure. */ +static X509* x509_vfy_cert(const char* name) +{ + char path[160]; + X509* x509; + int i; + + for (i = 0; i < x509VfyCertsNum; i++) { + if (XSTRCMP(x509VfyCerts[i].name, name) == 0) + return x509VfyCerts[i].x509; + } + x509_vfy_path(name, "-cert.pem", path, (int)sizeof(path)); + x509 = X509_load_certificate_file(path, SSL_FILETYPE_PEM); + if (!x509_vfy_keep(name, x509)) { + X509_free(x509); + x509 = NULL; + } + return x509; +} + +#ifdef X509_VFY_GEN_CRLS +/* Generated CRLs by name, as DER */ +static struct { + char name[X509_VFY_NAME_SZ]; + unsigned char* der; + int derSz; +} x509VfyCrls[12]; +static int x509VfyCrlsNum = 0; + +/* A new copy of the generated CRL of name, NULL when there is none */ +static X509_CRL* x509_vfy_gen_crl(const char* name) +{ + const unsigned char* p; + int i; + + for (i = 0; i < x509VfyCrlsNum; i++) { + if (XSTRCMP(x509VfyCrls[i].name, name) == 0) { + p = x509VfyCrls[i].der; + return d2i_X509_CRL(NULL, &p, x509VfyCrls[i].derSz); + } + } + return NULL; +} +#endif + +static void x509_vfy_free_certs(void) +{ + int i; + + for (i = 0; i < x509VfyCertsNum; i++) + X509_free(x509VfyCerts[i].x509); + x509VfyCertsNum = 0; +#ifdef X509_VFY_GEN_CRLS + for (i = 0; i < x509VfyCrlsNum; i++) + XFREE(x509VfyCrls[i].der, NULL, DYNAMIC_TYPE_OPENSSL); + x509VfyCrlsNum = 0; +#endif +} + +/* Pushes the certs of list on sk. Returns 1 on success. */ +static int x509_vfy_push(STACK_OF(X509)* sk, const char* list) +{ + char name[X509_VFY_NAME_SZ]; + X509* x509; + + while (x509_vfy_next(&list, name)) { + if ((x509 = x509_vfy_cert(name)) == NULL || + sk_X509_push(sk, x509) <= 0) + return 0; + } + return 1; +} + +/* Returns 1 when sk holds the certs of list in order: the same objects when + * same is set, else equal ones. */ +static int x509_vfy_match(STACK_OF(X509)* sk, const char* list, int same) +{ + char name[X509_VFY_NAME_SZ]; + X509* x509; + X509* x509Sk; + int i = 0; + + while (x509_vfy_next(&list, name)) { + if (i >= sk_X509_num(sk) || (x509 = x509_vfy_cert(name)) == NULL) + return 0; + x509Sk = sk_X509_value(sk, i); + if (same ? (x509Sk != x509) : (X509_cmp(x509Sk, x509) != 0)) + return 0; + i++; + } + return i == sk_X509_num(sk); +} + +static int x509_vfy_cb(int ok, X509_STORE_CTX* ctx) +{ + int err = X509_STORE_CTX_get_error(ctx); + + if (!ok) { + if (x509VfyLogNum < (int)XELEM_CNT(x509VfyLog)) { + x509VfyLog[x509VfyLogNum].err = err; + x509VfyLog[x509VfyLogNum].depth = + X509_STORE_CTX_get_error_depth(ctx); + x509VfyLog[x509VfyLogNum].cert = + X509_STORE_CTX_get_current_cert(ctx); + } + x509VfyLogNum++; + ok = (x509VfyAccept & VA_ALL) || + ((x509VfyAccept & VA_ISSUER) && err == VE_ISSUER) || + ((x509VfyAccept & VA_LOCAL) && err == VE_LOCAL) || + ((x509VfyAccept & VA_SELF) && + (err == VE_SELF0 || err == VE_SELF)) || + ((x509VfyAccept & VA_DATES) && + (err == VE_NOTYET || err == VE_EXPIRED)) || + ((x509VfyAccept & VA_CA) && err == VE_CA) || + ((x509VfyAccept & VA_KEYSIZE) && err == VE_KEYSIZE) || + ((x509VfyAccept & VA_NOCRL) && err == VE_NOCRL) || + ((x509VfyAccept & VA_CRLKU) && err == VE_CRLKU) || + ((x509VfyAccept & VA_CRLEXP) && err == VE_CRLEXP); + } + return ok; +} + +#ifndef NO_ASN_TIME +/* The current time in 1999, before the dated certs */ +static time_t x509_vfy_time_1999(time_t* t) +{ + time_t now = (time_t)915148800; + + if (t != NULL) + *t = now; + return now; +} +#endif + +/* nca-leaf with a broken signature, as nca-leaf-badsig */ +static int x509_vfy_gen_badsig(void) +{ + X509* x509 = x509_vfy_cert("nca-leaf"); + X509* bad = NULL; + unsigned char* der = NULL; + const unsigned char* p; + int derSz; + + if (x509 != NULL && (derSz = i2d_X509(x509, &der)) > 0) { + der[derSz - 1] ^= 0x01; + p = der; + bad = d2i_X509(NULL, &p, derSz); + } + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + if (!x509_vfy_keep("nca-leaf-badsig", bad)) { + X509_free(bad); + return 0; + } + return 1; +} + +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + ((!defined(NO_ASN_TIME) && !defined(ALLOW_INVALID_CERTSIGN)) || \ + !defined(IGNORE_NAME_CONSTRAINTS)) +/* Keeps the DER cert of der under name */ +static int x509_vfy_keep_der(const char* name, const byte* der, int derSz) +{ + X509* x509; + + if (derSz <= 0) + return 0; + x509 = d2i_X509(NULL, &der, derSz); + if (!x509_vfy_keep(name, x509)) { + X509_free(x509); + return 0; + } + return 1; +} +#endif + +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_ASN_TIME) && !defined(ALLOW_INVALID_CERTSIGN) +/* Twin CAs of one name and key issued by ca-cert: twin-good with + * keyCertSign, twin-bad without. twin-bad issued twin-leaf. Also pl0-ca, of + * pathlen:0, issued by ca-cert, pl0-notca, not a CA, issued by it, and + * pl0-notca-leaf. nocrlsign, issued by ca-cert, may sign certs but not + * CRLs; it issued nocrlsign-leaf. */ +static int x509_vfy_gen_twins(void) +{ + EXPECT_DECLS; + WC_RNG rng; + RsaKey caKey, intKey, leafKey; + int rngI = 0, caI = 0, intI = 0, leafI = 0; + word32 idx; + byte* der = NULL; + byte* leafDer = NULL; + int sz = 0; + Cert cert; + + ExpectNotNull(der = (byte*)XMALLOC(FOURK_BUF, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(leafDer = (byte*)XMALLOC(FOURK_BUF, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) rngI = 1; + ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0); + if (EXPECT_SUCCESS()) caI = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey, + sizeof_ca_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&intKey, NULL), 0); + if (EXPECT_SUCCESS()) intI = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &intKey, + sizeof_server_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0); + if (EXPECT_SUCCESS()) leafI = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey, + sizeof_client_key_der_2048), 0); + + if (EXPECT_SUCCESS()) { + sz = gen_ca_int_keyusage(der, FOURK_BUF, &intKey, &caKey, &rng, + "Twin Intermediate", "keyCertSign,cRLSign", 0); + } + ExpectIntEQ(x509_vfy_keep_der("twin-good", der, sz), 1); + if (EXPECT_SUCCESS()) { + sz = gen_ca_int_keyusage(der, FOURK_BUF, &intKey, &caKey, &rng, + "Twin Intermediate", "digitalSignature", 0); + } + ExpectIntEQ(x509_vfy_keep_der("twin-bad", der, sz), 1); + if (EXPECT_SUCCESS()) { + sz = gen_leaf_under_int(leafDer, FOURK_BUF, &leafKey, der, sz, + &intKey, &rng, "Leaf under twins"); + } + ExpectIntEQ(x509_vfy_keep_der("twin-leaf", leafDer, sz), 1); + + ExpectIntEQ(wc_InitCert(&cert), 0); + cert.isCA = 1; + cert.pathLen = 0; + cert.pathLenSet = 1; + cert.sigType = CTC_SHA256wRSA; + XSTRNCPY(cert.subject.commonName, "Pathlen 0 CA", CTC_NAME_SIZE - 1); + ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey(&cert, &intKey, NULL), 0); + ExpectIntEQ(wc_SetAuthKeyIdFromCert(&cert, ca_cert_der_2048, + (int)sizeof_ca_cert_der_2048), 0); + ExpectIntEQ(wc_SetIssuerBuffer(&cert, ca_cert_der_2048, + (int)sizeof_ca_cert_der_2048), 0); + ExpectIntGT(wc_MakeCert(&cert, der, FOURK_BUF, &intKey, NULL, &rng), 0); + if (EXPECT_SUCCESS()) { + sz = wc_SignCert(cert.bodySz, cert.sigType, der, FOURK_BUF, &caKey, + NULL, &rng); + } + ExpectIntEQ(x509_vfy_keep_der("pl0-ca", der, sz), 1); + if (EXPECT_SUCCESS()) { + sz = gen_leaf_under_int(leafDer, FOURK_BUF, &leafKey, der, sz, + &intKey, &rng, "Not a CA"); + } + ExpectIntEQ(x509_vfy_keep_der("pl0-notca", leafDer, sz), 1); + if (EXPECT_SUCCESS()) { + sz = gen_leaf_under_int(der, FOURK_BUF, &caKey, leafDer, sz, + &leafKey, &rng, "Leaf of not a CA"); + } + ExpectIntEQ(x509_vfy_keep_der("pl0-notca-leaf", der, sz), 1); + + if (EXPECT_SUCCESS()) { + sz = gen_ca_int_keyusage(der, FOURK_BUF, &intKey, &caKey, &rng, + "No CRL Sign CA", "keyCertSign", 0); + } + ExpectIntEQ(x509_vfy_keep_der("nocrlsign", der, sz), 1); + if (EXPECT_SUCCESS()) { + sz = gen_leaf_under_int(leafDer, FOURK_BUF, &leafKey, der, sz, + &intKey, &rng, "Leaf of no CRL Sign CA"); + } + ExpectIntEQ(x509_vfy_keep_der("nocrlsign-leaf", leafDer, sz), 1); + + if (rngI) wc_FreeRng(&rng); + if (caI) wc_FreeRsaKey(&caKey); + if (intI) wc_FreeRsaKey(&intKey); + if (leafI) wc_FreeRsaKey(&leafKey); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return EXPECT_RESULT(); +} +#endif + +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(IGNORE_NAME_CONSTRAINTS) +/* Leaves of nc-ca, which permits only the email addresses of .wolfssl.com: + * nc-leaf with one, nc-leaf-bad with another. Leaves of ncdns-ca, CAs with a + * CN and no SAN: ncdns-leaf in .example.com, ncdns-leaf-bad outside it, + * ncdns-leaf-excl in the excluded .blocked.example.com. */ +static int x509_vfy_gen_nc(void) +{ + EXPECT_DECLS; + static const struct { + const char* name; + const char* ca; + int isCA; + const char* cn; + const char* email; + } leaves[] = { + { "nc-leaf", "nc-ca", 0, "NC Leaf", + "leaf@info.wolfssl.com" }, + { "nc-leaf-bad", "nc-ca", 0, "NC Leaf", + "leaf@info.example.com" }, + { "ncdns-leaf", "ncdns-ca", 1, "www.example.com", "" }, + { "ncdns-leaf-bad", "ncdns-ca", 1, "www.bank.com", "" }, + { "ncdns-leaf-excl", "ncdns-ca", 1, "www.blocked.example.com", "" } + }; + WC_RNG rng; + RsaKey caKey, leafKey; + int rngI = 0, caI = 0, leafI = 0; + word32 idx; + byte* der = NULL; + X509* ca = NULL; + const byte* caDer = NULL; + int caDerSz = 0; + Cert cert; + int sz = 0; + int i; + + ExpectNotNull(der = (byte*)XMALLOC(FOURK_BUF, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) rngI = 1; + ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0); + if (EXPECT_SUCCESS()) caI = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &caKey, + sizeof_server_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0); + if (EXPECT_SUCCESS()) leafI = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey, + sizeof_client_key_der_2048), 0); + + for (i = 0; i < (int)XELEM_CNT(leaves) && EXPECT_SUCCESS(); i++) { + ExpectNotNull(ca = x509_vfy_cert(leaves[i].ca)); + ExpectNotNull(caDer = wolfSSL_X509_get_der(ca, &caDerSz)); + ExpectIntEQ(wc_InitCert(&cert), 0); + cert.isCA = leaves[i].isCA; + cert.sigType = CTC_SHA256wRSA; + XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.commonName, leaves[i].cn, CTC_NAME_SIZE - 1); + XSTRNCPY(cert.subject.email, leaves[i].email, CTC_NAME_SIZE - 1); + ExpectIntEQ(wc_SetAuthKeyIdFromCert(&cert, caDer, caDerSz), 0); + ExpectIntEQ(wc_SetIssuerBuffer(&cert, caDer, caDerSz), 0); + ExpectIntGT(wc_MakeCert(&cert, der, FOURK_BUF, &leafKey, NULL, &rng), + 0); + if (EXPECT_SUCCESS()) { + sz = wc_SignCert(cert.bodySz, cert.sigType, der, FOURK_BUF, + &caKey, NULL, &rng); + } + ExpectIntEQ(x509_vfy_keep_der(leaves[i].name, der, sz), 1); + } + + if (rngI) wc_FreeRng(&rng); + if (caI) wc_FreeRsaKey(&caKey); + if (leafI) wc_FreeRsaKey(&leafKey); + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return EXPECT_RESULT(); +} +#endif + +/* Adds the trusted certs of c to store */ +static int x509_vfy_add_trusted(const x509_vfy_case* c, X509_STORE* store, + SSL_CTX* sslCtx) +{ + EXPECT_DECLS; + const char* list = c->trusted; + char name[X509_VFY_NAME_SZ]; + char path[160]; + X509_LOOKUP* lookup = NULL; + + (void)sslCtx; + while (EXPECT_SUCCESS() && x509_vfy_next(&list, name)) { + x509_vfy_path(name, "-cert.pem", path, (int)sizeof(path)); + if (c->opts & VO_FILE) { + ExpectIntEQ(X509_STORE_load_locations(store, path, NULL), 1); + } +#if defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM) + else if (c->opts & VO_LOOKUP) { + if (lookup == NULL) { + ExpectNotNull(lookup = X509_STORE_add_lookup(store, + X509_LOOKUP_file())); + } + ExpectIntEQ(X509_LOOKUP_load_file(lookup, path, + X509_FILETYPE_PEM), 1); + } +#endif +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + else if (c->opts & VO_SSLCTX) { + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, path, NULL), + 1); + } +#endif + else { + ExpectIntEQ(X509_STORE_add_cert(store, x509_vfy_cert(name)), 1); + } + } + (void)lookup; + return EXPECT_RESULT(); +} + +#ifdef HAVE_CRL +/* Breaks the signature of *crl */ +static int x509_vfy_crl_badsig(X509_CRL** crl) +{ + X509_CRL* bad = NULL; + unsigned char* der = NULL; + const unsigned char* p; + int derSz; + + if ((derSz = i2d_X509_CRL(*crl, &der)) > 0) { + der[derSz - 1] ^= 0x01; + p = der; + bad = d2i_X509_CRL(NULL, &p, derSz); + } + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + if (bad == NULL) + return 0; + X509_CRL_free(*crl); + *crl = bad; + return 1; +} + +/* Adds the CRLs of c to store or, with a '+', to *crls. "-badsig" is + * the CRL of name with a broken signature. */ +static int x509_vfy_add_crls(const x509_vfy_case* c, X509_STORE* store, + STACK_OF(X509_CRL)** crls) +{ + EXPECT_DECLS; + const char* list = c->crls; + char name[X509_VFY_NAME_SZ]; + char path[160]; + XFILE fp = XBADFILE; + X509_CRL* crl = NULL; + int toCtx; + int badSig; + size_t len; + + while (EXPECT_SUCCESS() && x509_vfy_next(&list, name)) { + toCtx = (name[0] == '+'); + len = XSTRLEN(name); + badSig = len > 7 && XSTRCMP(name + len - 7, "-badsig") == 0; + if (badSig) + name[len - 7] = '\0'; + #ifdef X509_VFY_GEN_CRLS + crl = x509_vfy_gen_crl(name + toCtx); + #endif + if (crl == NULL) { + x509_vfy_path(name + toCtx, "-crl.pem", path, (int)sizeof(path)); + ExpectTrue((fp = XFOPEN(path, "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) { + XFCLOSE(fp); + fp = XBADFILE; + } + } + if (badSig) { + ExpectIntEQ(x509_vfy_crl_badsig(&crl), 1); + } + if (!toCtx) { + ExpectIntEQ(X509_STORE_add_crl(store, crl), 1); + } + else { + if (*crls == NULL) { + ExpectNotNull(*crls = sk_X509_CRL_new_null()); + } + ExpectIntGT(sk_X509_CRL_push(*crls, crl), 0); + if (EXPECT_SUCCESS()) + crl = NULL; + } + X509_CRL_free(crl); + crl = NULL; + } + return EXPECT_RESULT(); +} +#endif + +/* Runs the verification of c and checks its results */ +static int x509_vfy_run(const x509_vfy_case* c) +{ + EXPECT_DECLS; + X509* leaf = NULL; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + X509_VERIFY_PARAM* vpm = NULL; + STACK_OF(X509)* untrusted = NULL; + STACK_OF(X509)* trusted = NULL; + STACK_OF(X509)* chain = NULL; +#ifdef HAVE_CRL + STACK_OF(X509_CRL)* crls = NULL; +#endif + SSL_CTX* sslCtx = NULL; + unsigned long flags = 0; + time_t t = 0; + int i; + int n; + + if (c->opts & VO_SSLCTX) { +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + ExpectNotNull(sslCtx = SSL_CTX_new(wolfSSLv23_client_method())); + /* As OpenSSL, load CAs unverified */ + SSL_CTX_set_verify(sslCtx, SSL_VERIFY_NONE, NULL); + ExpectNotNull(store = SSL_CTX_get_cert_store(sslCtx)); +#endif + } + else { + ExpectNotNull(store = X509_STORE_new()); + } + if (c->opts & VO_S_NOTIME) { + ExpectIntEQ(X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(store), + X509_V_FLAG_NO_CHECK_TIME), 1); + } + if (c->opts & VO_S_STRICT) + flags |= X509_V_FLAG_X509_STRICT; + if (c->opts & VO_S_PARTIAL) + flags |= X509_V_FLAG_PARTIAL_CHAIN; + if (c->opts & VO_CRL) + flags |= X509_V_FLAG_CRL_CHECK; + if ((c->opts & VO_CRL_ALL) == VO_CRL_ALL) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + if (flags != 0) { + ExpectIntEQ(X509_STORE_set_flags(store, flags), 1); + } +#ifndef NO_ASN_TIME + if (c->opts & VO_ADD1999) { + ExpectIntEQ(wc_SetTimeCb(x509_vfy_time_1999), 0); + } +#endif + if (c->trusted != NULL) { + ExpectIntEQ(x509_vfy_add_trusted(c, store, sslCtx), TEST_SUCCESS); + } +#ifndef NO_ASN_TIME + if (c->opts & VO_ADD1999) + wc_SetTimeCb(NULL); +#endif + if ((c->opts & VO_MINRSA) && store != NULL) + store->cm->minRsaKeySz = 4096 / 8; + if ((c->opts & VO_MINECC) && store != NULL) + store->cm->minEccKeySz = 384 / 8; +#ifdef HAVE_CRL + if (c->crls != NULL) { + ExpectIntEQ(x509_vfy_add_crls(c, store, &crls), TEST_SUCCESS); + } +#else + ExpectNull(c->crls); +#endif + if (c->untrusted != NULL) { + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntEQ(x509_vfy_push(untrusted, c->untrusted), 1); + } + if (c->stack != NULL) { + ExpectNotNull(trusted = sk_X509_new_null()); + ExpectIntEQ(x509_vfy_push(trusted, c->stack), 1); + } + + ExpectNotNull(leaf = x509_vfy_cert(c->leaf)); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + if (EXPECT_SUCCESS()) { + if (trusted != NULL) + X509_STORE_CTX_trusted_stack(ctx, trusted); + switch (c->opts & VO_TIME) { + case VO_T2005: t = (time_t)1104537600; break; + case VO_T2015: t = (time_t)1420070400; break; + case VO_T2025: t = (time_t)1735689600; break; + default: break; + } + if (c->opts & VO_XMLSEC) { + ExpectNotNull(vpm = X509_VERIFY_PARAM_new()); + if (vpm != NULL) { + X509_VERIFY_PARAM_set_depth(vpm, 9); + if (t != 0) + X509_VERIFY_PARAM_set_time(vpm, t); + X509_STORE_CTX_set0_param(ctx, vpm); + } + } + else if (t != 0) { + X509_STORE_CTX_set_time(ctx, 0, t); + } + flags = 0; + if (c->opts & VO_PARTIAL) + flags |= X509_V_FLAG_PARTIAL_CHAIN; + if (c->opts & VO_STRICT) + flags |= X509_V_FLAG_X509_STRICT; + if (c->opts & VO_NOTIME) + flags |= X509_V_FLAG_NO_CHECK_TIME; + if (c->opts & VO_C_CRL) + flags |= X509_V_FLAG_CRL_CHECK; + if ((c->opts & VO_C_CRL_ALL) == VO_C_CRL_ALL) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + if (flags != 0) + X509_STORE_CTX_set_flags(ctx, flags); + if (c->opts & VO_DEPTH0) + X509_STORE_CTX_set_depth(ctx, 0); + if (c->opts & VO_DEPTH1) + X509_STORE_CTX_set_depth(ctx, 1); +#ifdef HAVE_CRL + if (crls != NULL) { + X509_STORE_CTX_set0_crls(ctx, crls); + } +#endif + x509VfyAccept = c->cb; + x509VfyLogNum = 0; + if (c->cb != 0) + X509_STORE_CTX_set_verify_cb(ctx, x509_vfy_cb); + } + + ExpectIntEQ(X509_verify_cert(ctx), c->ret); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), c->err); + if (c->err != VE_OK) { + ExpectIntEQ(X509_STORE_CTX_get_error_depth(ctx), c->depth); + } + if (EXPECT_SUCCESS()) + chain = X509_STORE_CTX_get0_chain(ctx); + if (c->chain != NULL) { + ExpectNotNull(chain); + ExpectIntEQ(x509_vfy_match(chain, c->chain, 0), 1); + } + if (c->cb != 0) { + for (n = 0; n < 3 && c->cbLog[2 * n] != 0; n++) { + ExpectIntLT(n, x509VfyLogNum); + ExpectIntEQ(x509VfyLog[n].err, c->cbLog[2 * n]); + ExpectIntEQ(x509VfyLog[n].depth, c->cbLog[2 * n + 1]); + } + ExpectIntEQ(x509VfyLogNum, n); + /* The current cert of each error is the cert at its depth */ + for (i = 0; i < n && chain != NULL && EXPECT_SUCCESS(); i++) { + if (x509VfyLog[i].depth < sk_X509_num(chain)) { + ExpectIntEQ(X509_cmp(x509VfyLog[i].cert, + sk_X509_value(chain, x509VfyLog[i].depth)), 0); + } + } + } + /* The caller's stacks are left as they were */ + if (untrusted != NULL) { + ExpectIntEQ(x509_vfy_match(untrusted, c->untrusted, 1), 1); + } + if (trusted != NULL) { + ExpectIntEQ(x509_vfy_match(trusted, c->stack, 1), 1); + } + + X509_STORE_CTX_free(ctx); +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + if (sslCtx != NULL) + SSL_CTX_free(sslCtx); + else +#endif + X509_STORE_free(store); + sk_X509_free(untrusted); + sk_X509_free(trusted); +#ifdef HAVE_CRL + sk_X509_CRL_pop_free(crls, X509_CRL_free); +#endif + return EXPECT_RESULT(); +} +#endif + +#ifdef X509_VFY_GEN_CRLS +/* CRLs of ca: ca-stale and ca-stale-empty out of date for a month, + * ca-revoked, ca-empty and ca-self current, ca-nyv-empty valid in a month. + * ca-stale and ca-revoked list server, ca-self lists ca. ca-empty-old is a + * day older than ca-empty. ca-otherkey and ca-otherkey-old, a day older, are + * signed with another key. The current ones have the same thisUpdate. + * nocrlsign-revoked of nocrlsign lists nocrlsign-leaf. nocrlsign-stale-empty + * is out of date for a month. */ +static int x509_vfy_gen_crls(void) +{ + EXPECT_DECLS; + static const struct { + const char* name; + const char* issuer; + const char* key; /* of the issuer */ + int days; /* from now to the next update */ + const char* revoked; /* NULL: none */ + } crls[] = { + { "ca-stale", "ca", "./certs/ca-key.pem", -30, "server" }, + { "ca-stale-empty", "ca", "./certs/ca-key.pem", -30, NULL }, + { "ca-revoked", "ca", "./certs/ca-key.pem", 30, "server" }, + { "ca-empty", "ca", "./certs/ca-key.pem", 30, NULL }, + { "ca-nyv-empty", "ca", "./certs/ca-key.pem", 60, NULL }, + { "ca-self", "ca", "./certs/ca-key.pem", 30, "ca" }, + { "ca-empty-old", "ca", "./certs/ca-key.pem", 29, NULL }, + { "ca-otherkey", "ca", "./certs/server-key.pem", 30, NULL }, + { "ca-otherkey-old", "ca", "./certs/server-key.pem", 29, NULL }, + #if defined(WOLFSSL_CERT_EXT) && !defined(NO_SHA256) && \ + defined(USE_CERT_BUFFERS_2048) && !defined(ALLOW_INVALID_CERTSIGN) + { "nocrlsign-revoked", "nocrlsign", "./certs/server-key.pem", 30, + "nocrlsign-leaf" }, + { "nocrlsign-stale-empty", "nocrlsign", "./certs/server-key.pem", -30, + NULL } + #endif + }; + EVP_PKEY* key = NULL; + X509_CRL* crl = NULL; + ASN1_TIME t; + XFILE fp = XBADFILE; + const unsigned char* der = NULL; + int derSz = 0; + time_t now = XTIME(NULL); + size_t i; + + for (i = 0; i < XELEM_CNT(crls) && EXPECT_SUCCESS(); i++) { + ExpectTrue((fp = XFOPEN(crls[i].key, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(key = PEM_read_PrivateKey(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + } + ExpectNotNull(crl = wolfSSL_X509_CRL_new()); + ExpectIntEQ(wolfSSL_X509_CRL_set_issuer_name(crl, + X509_get_subject_name(x509_vfy_cert(crls[i].issuer))), + WOLFSSL_SUCCESS); + XMEMSET(&t, 0, sizeof(t)); + ExpectNotNull(ASN1_TIME_adj(&t, now, crls[i].days - 30, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_lastUpdate(crl, &t), + WOLFSSL_SUCCESS); + ExpectNotNull(ASN1_TIME_adj(&t, now, crls[i].days, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_nextUpdate(crl, &t), + WOLFSSL_SUCCESS); + if (crls[i].revoked != NULL) { + ExpectNotNull(der = wolfSSL_X509_get_der( + x509_vfy_cert(crls[i].revoked), &derSz)); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked_cert(crl, der, derSz), + WOLFSSL_SUCCESS); + } + ExpectIntEQ(wolfSSL_X509_CRL_sign(crl, key, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + XSTRNCPY(x509VfyCrls[i].name, crls[i].name, + X509_VFY_NAME_SZ - 1); + x509VfyCrls[i].der = NULL; + x509VfyCrls[i].derSz = i2d_X509_CRL(crl, &x509VfyCrls[i].der); + x509VfyCrlsNum = (int)i + 1; + } + ExpectIntGT(x509VfyCrls[i].derSz, 0); + X509_CRL_free(crl); + crl = NULL; + EVP_PKEY_free(key); + key = NULL; + } + return EXPECT_RESULT(); +} +#endif + +/* X509_verify_cert() on the chains of x509VfyCases, each compared with + * OpenSSL 3.0. A comment tells where OpenSSL gives another result. */ +int test_X509_verify_cert_chains(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + size_t i; + int failed = 0; + + ExpectIntEQ(x509_vfy_gen_badsig(), 1); +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(NO_ASN_TIME) && !defined(ALLOW_INVALID_CERTSIGN) + ExpectIntEQ(x509_vfy_gen_twins(), TEST_SUCCESS); +#endif +#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + !defined(NO_SHA256) && defined(USE_CERT_BUFFERS_2048) && \ + !defined(IGNORE_NAME_CONSTRAINTS) + ExpectIntEQ(x509_vfy_gen_nc(), TEST_SUCCESS); +#endif +#ifdef X509_VFY_GEN_CRLS + ExpectIntEQ(x509_vfy_gen_crls(), TEST_SUCCESS); +#endif + + /* A context has X509_V_FLAG_TRUSTED_FIRST: VO_XMLSEC replaces its + * param */ + ExpectNotNull(store = X509_STORE_new()); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, x509_vfy_cert("leaf"), NULL), + 1); + ExpectIntNE(X509_VERIFY_PARAM_get_flags(X509_STORE_CTX_get0_param(ctx)) & + X509_V_FLAG_TRUSTED_FIRST, 0); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + + for (i = 0; i < XELEM_CNT(x509VfyCases) && EXPECT_SUCCESS(); i++) { + if (x509_vfy_run(&x509VfyCases[i]) != TEST_SUCCESS) { + fprintf(stderr, "X509_verify_cert() case %d failed: %s\n", + (int)i, x509VfyCases[i].name); + failed++; + } + } + ExpectIntEQ(failed, 0); + x509_vfy_free_certs(); +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && defined(USE_WOLFSSL_MEMORY) && \ + !defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \ + !defined(WOLFSSL_DEBUG_MEMORY) +#define TEST_X509_VERIFY_CERT_OOM +static wolfSSL_Malloc_cb x509VfyOomMalloc = NULL; +static wolfSSL_Realloc_cb x509VfyOomRealloc = NULL; +static int x509VfyOomNum = 0; +static int x509VfyOomAt = 0; + +/* Fails allocation number x509VfyOomAt */ +static void* x509_vfy_oom_malloc(size_t size) +{ + if (++x509VfyOomNum == x509VfyOomAt) + return NULL; + if (x509VfyOomMalloc != NULL) + return x509VfyOomMalloc(size); + return malloc(size); +} + +static void* x509_vfy_oom_realloc(void* ptr, size_t size) +{ + if (++x509VfyOomNum == x509VfyOomAt) + return NULL; + if (x509VfyOomRealloc != NULL) + return x509VfyOomRealloc(ptr, size); + return realloc(ptr, size); +} + +/* Returns 1 when verifying a signature with the RSA key of ca allocates */ +static int x509_vfy_oom_rsa_allocs(X509* ca, wolfSSL_Free_cb freeCb) +{ + RsaKey* key = NULL; + byte sig[256]; + byte* out = NULL; + word32 idx = 0; + int ret = 0; + + key = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_RSA); + if (key != NULL && wc_InitRsaKey(key, NULL) == 0) { + if (wc_RsaPublicKeyDecode(ca->pubKey.buffer, &idx, key, + ca->pubKey.length) == 0 && + wc_RsaEncryptSize(key) == (int)sizeof(sig)) { + XMEMSET(sig, 1, sizeof(sig)); + sig[0] = 0; + x509VfyOomNum = 0; + x509VfyOomAt = 0; + if (wolfSSL_SetAllocators(x509_vfy_oom_malloc, freeCb, + x509_vfy_oom_realloc) == 0) { + (void)wc_RsaSSL_VerifyInline(sig, sizeof(sig), &out, key); + (void)wolfSSL_SetAllocators(x509VfyOomMalloc, freeCb, + x509VfyOomRealloc); + ret = x509VfyOomNum > 0; + } + } + wc_FreeRsaKey(key); + } + XFREE(key, NULL, DYNAMIC_TYPE_RSA); + return ret; +} +#endif + +/* X509_verify_cert() rejects whichever allocation fails: a chain without a + * trusted issuer, a chain through a CA of an SSL_CTX of pathlen:0, a cert + * that a CRL of X509_STORE_CTX_set0_crls revokes while a CRL of the store + * does not, and an expired self-signed CA of an SSL_CTX. A callback that + * accepts every error cannot accept a failed allocation, and some is + * reported as X509_V_ERR_OUT_OF_MEM. Where verifying an RSA signature + * allocates, a failure there is a bad signature, as in OpenSSL: then only + * that a failing chain still fails without a callback is checked. */ +int test_X509_verify_cert_oom(void) +{ + EXPECT_DECLS; +#ifdef TEST_X509_VERIFY_CERT_OOM + static const struct { + const char* leaf; + const char* untrusted; /* NULL: none */ + const char* sslCtxCa; /* with ca-cert, the CAs of an SSL_CTX */ + const char* storeCrl; + const char* ctxCrl; + int err; + } cases[] = { + { "./certs/intermediate/server-int-cert.pem", NULL, NULL, NULL, NULL, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY }, + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + { "./certs/test-pathlen/chainF-entity.pem", + "./certs/test-pathlen/chainF-ICA1-pathlen1.pem", + "./certs/test-pathlen/chainF-ICA2-pathlen0.pem", NULL, NULL, + X509_V_ERR_PATH_LENGTH_EXCEEDED }, + #endif + #ifdef HAVE_CRL + { "./certs/server-cert.pem", NULL, NULL, + "./certs/crl/extra-crls/ca-int-cert-revoked.pem", + "./certs/crl/extra-crls/general-server-crl.pem", + X509_V_ERR_CERT_REVOKED }, + #endif + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + defined(WOLFSSL_SIGNER_DER_CERT) + { TA_CERT_DIR "expired-root-cert.pem", NULL, + TA_CERT_DIR "expired-root-cert.pem", NULL, NULL, + X509_V_ERR_CERT_HAS_EXPIRED }, + #endif + }; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + X509* ca = NULL; + X509* leaf = NULL; + X509* inter = NULL; + STACK_OF(X509)* untrusted = NULL; +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX* sslCtx = NULL; +#endif +#ifdef HAVE_CRL + X509_CRL* crl = NULL; + STACK_OF(X509_CRL)* crls = NULL; + XFILE fp = XBADFILE; +#endif + wolfSSL_Free_cb prevF = NULL; + int allocs; + int acceptAll; + int ret; + int err; + int baseErr = 0; + int baseLogNum = 0; + int baseLog[2 * 4]; /* x509VfyLog without a failure: errors, depths */ + int same; + int rsaAllocs = 0; + int outOfMem; + int i; + int n; + size_t c; + + ExpectNotNull(ca = X509_load_certificate_file("./certs/ca-cert.pem", + SSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_GetAllocators(&x509VfyOomMalloc, &prevF, + &x509VfyOomRealloc), 0); + if (EXPECT_SUCCESS()) + rsaAllocs = x509_vfy_oom_rsa_allocs(ca, prevF); + for (c = 0; c < XELEM_CNT(cases) && EXPECT_SUCCESS(); c++) { + ExpectNotNull(leaf = X509_load_certificate_file(cases[c].leaf, + SSL_FILETYPE_PEM)); + if (cases[c].untrusted != NULL) { + ExpectNotNull(inter = X509_load_certificate_file( + cases[c].untrusted, SSL_FILETYPE_PEM)); + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, inter), 0); + } + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + if (cases[c].sslCtxCa != NULL) { + ExpectNotNull(sslCtx = SSL_CTX_new(wolfSSLv23_client_method())); + SSL_CTX_set_verify(sslCtx, SSL_VERIFY_NONE, NULL); + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, + "./certs/ca-cert.pem", NULL), 1); + ExpectIntEQ(SSL_CTX_load_verify_locations(sslCtx, + cases[c].sslCtxCa, NULL), 1); + ExpectNotNull(store = SSL_CTX_get_cert_store(sslCtx)); + } + else + #endif + { + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, ca), 1); + } + #ifdef HAVE_CRL + if (cases[c].storeCrl != NULL) { + ExpectTrue((fp = XFOPEN(cases[c].storeCrl, "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectIntEQ(X509_STORE_add_crl(store, crl), 1); + X509_CRL_free(crl); + crl = NULL; + } + if (cases[c].ctxCrl != NULL) { + ExpectTrue((fp = XFOPEN(cases[c].ctxCrl, "rb")) != XBADFILE); + ExpectNotNull(crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL)); + if (fp != XBADFILE) + XFCLOSE(fp); + ExpectNotNull(crls = sk_X509_CRL_new_null()); + ExpectIntGT(sk_X509_CRL_push(crls, crl), 0); + } + #endif + /* First without a failure, to count the allocations. A callback + * that accepts every error is not asked about a failed allocation, + * as OpenSSL: X509_V_ERR_OUT_OF_MEM, or X509_V_ERR_UNSPECIFIED + * where it is not known. */ + for (acceptAll = 0; acceptAll <= 1 && EXPECT_SUCCESS(); acceptAll++) { + allocs = 0; + outOfMem = 0; + for (i = 0; i <= allocs && EXPECT_SUCCESS(); i++) { + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), + 1); + #ifdef HAVE_CRL + if (crls != NULL) + X509_STORE_CTX_set0_crls(ctx, crls); + #endif + x509VfyAccept = VA_ALL; + x509VfyLogNum = 0; + if (acceptAll) + X509_STORE_CTX_set_verify_cb(ctx, x509_vfy_cb); + x509VfyOomNum = 0; + x509VfyOomAt = i; + ExpectIntEQ(wolfSSL_SetAllocators(x509_vfy_oom_malloc, prevF, + x509_vfy_oom_realloc), 0); + ret = X509_verify_cert(ctx); + (void)wolfSSL_SetAllocators(x509VfyOomMalloc, prevF, + x509VfyOomRealloc); + err = X509_STORE_CTX_get_error(ctx); + if (i == 0) { + allocs = x509VfyOomNum; + if (!acceptAll) + ExpectIntEQ(err, cases[c].err); + ExpectIntEQ(ret, acceptAll); + baseErr = err; + baseLogNum = x509VfyLogNum; + for (n = 0; n < (int)XELEM_CNT(x509VfyLog); n++) { + baseLog[2 * n] = x509VfyLog[n].err; + baseLog[2 * n + 1] = x509VfyLog[n].depth; + } + } + else { + /* Without effect, or internal: the callback saw only + * errors it sees without a failure */ + same = x509VfyLogNum <= baseLogNum; + for (n = 0; same && n < x509VfyLogNum && + n < (int)XELEM_CNT(x509VfyLog); n++) { + same = x509VfyLog[n].err == baseLog[2 * n] && + x509VfyLog[n].depth == baseLog[2 * n + 1]; + } + if (ret == 1) { + same = acceptAll && (rsaAllocs || (same && + err == baseErr && x509VfyLogNum == baseLogNum)); + } + else if (err != X509_V_ERR_OUT_OF_MEM && + err != X509_V_ERR_UNSPECIFIED) { + same = rsaAllocs || + (same && !acceptAll && err == baseErr); + } + if (!same) { + fprintf(stderr, "X509_verify_cert() of %s with " + "allocation %d failing: %d, error %d\n", + cases[c].leaf, i, ret, err); + } + ExpectTrue(same); + outOfMem |= (err == X509_V_ERR_OUT_OF_MEM); + } + X509_STORE_CTX_free(ctx); + ctx = NULL; + } + ExpectIntGT(allocs, 0); + /* Some failed allocation is reported as such */ + ExpectTrue(outOfMem); + } + #ifdef HAVE_CRL + sk_X509_CRL_pop_free(crls, X509_CRL_free); + crls = NULL; + #endif + #if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + if (sslCtx != NULL) { + SSL_CTX_free(sslCtx); + sslCtx = NULL; + store = NULL; + } + #endif + X509_STORE_free(store); + store = NULL; + sk_X509_pop_free(untrusted, X509_free); + untrusted = NULL; + X509_free(leaf); + leaf = NULL; + } + X509_free(ca); +#endif + return EXPECT_RESULT(); +} + +/* A store of its own takes a trusted cert that is out of date, as in + * OpenSSL (the add_cert_expired cases of x509VfyCases). The cert stays out + * of the cert manager, so TLS does not trust it. */ +int test_X509_STORE_add_cert_expired(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_ASN_TIME) + const char* leafFile = TA_CERT_DIR "expired-root-leaf-cert.pem"; + X509* root = NULL; + X509* renewed = NULL; + X509* leaf = NULL; + X509* issuer = NULL; + X509_STORE* store = NULL; + X509_STORE* store2 = NULL; + X509_STORE_CTX* ctx = NULL; +#if defined(WOLFSSL_SIGNER_DER_CERT) + STACK_OF(X509)* certs = NULL; +#endif +#if defined(OPENSSL_ALL) && defined(WOLFSSL_SIGNER_DER_CERT) + STACK_OF(X509_OBJECT)* objs = NULL; + int i; +#endif +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + SSL_CTX* sslCtx = NULL; + X509* datedRoot = NULL; + X509* expiredInt = NULL; +#endif + + ExpectNotNull(root = X509_load_certificate_file( + TA_CERT_DIR "expired-root-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(renewed = X509_load_certificate_file( + TA_CERT_DIR "renewed-root-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(leaf = X509_load_certificate_file(leafFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectIntEQ(wolfSSL_CertManagerVerify(store->cm, leafFile, + SSL_FILETYPE_PEM), WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)); + /* Adding it again keeps one copy */ + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectIntEQ(sk_X509_num(store->trusted), 1); + + /* Verifying with it does not put it in the cert manager */ + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + X509_STORE_CTX_set_time(ctx, 0, (time_t)1420070400); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(wolfSSL_CertManagerVerify(store->cm, leafFile, + SSL_FILETYPE_PEM), WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)); + /* The issuer lookups find it */ + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + ExpectIntEQ(X509_STORE_CTX_get1_issuer(&issuer, ctx, leaf), 1); + ExpectIntEQ(X509_cmp(issuer, root), 0); + X509_free(issuer); +#if defined(WOLFSSL_SIGNER_DER_CERT) + ExpectNotNull(certs = X509_STORE_get1_certs(ctx, + X509_get_subject_name(root))); + ExpectIntEQ(sk_X509_num(certs), 1); + sk_X509_pop_free(certs, X509_free); +#endif +#if defined(OPENSSL_ALL) && defined(WOLFSSL_SIGNER_DER_CERT) + ExpectNotNull(objs = X509_STORE_get0_objects(store)); + ExpectIntEQ(sk_X509_OBJECT_num(objs), 1); + ExpectIntEQ(X509_cmp(X509_OBJECT_get0_X509( + (X509_OBJECT*)sk_X509_OBJECT_value(objs, 0)), root), 0); +#endif + + /* Nor with X509_V_FLAG_NO_CHECK_TIME */ + ExpectNotNull(store2 = X509_STORE_new()); + ExpectIntEQ(X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(store2), + X509_V_FLAG_NO_CHECK_TIME), 1); + ExpectIntEQ(X509_STORE_add_cert(store2, root), 1); + ExpectIntEQ(wolfSSL_CertManagerVerify(store2->cm, leafFile, + SSL_FILETYPE_PEM), WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)); + X509_STORE_free(store2); + store2 = NULL; + +#if defined(OPENSSL_ALL) && defined(WOLFSSL_SIGNER_DER_CERT) + /* A valid root of the same name with a new key does not hide it */ + for (i = 0; i < 2; i++) { + ExpectNotNull(store2 = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store2, (i == 0) ? renewed : root), + 1); + ExpectIntEQ(X509_STORE_add_cert(store2, (i == 0) ? root : renewed), + 1); + ExpectNotNull(objs = X509_STORE_get0_objects(store2)); + ExpectIntEQ(sk_X509_OBJECT_num(objs), 2); + X509_STORE_free(store2); + store2 = NULL; + } +#endif + +#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + /* The store of an SSL_CTX still refuses it */ + ExpectNotNull(datedRoot = X509_load_certificate_file( + TA_CERT_DIR "dated-root-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(expiredInt = X509_load_certificate_file( + TA_CERT_DIR "expired-int-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(sslCtx = SSL_CTX_new(wolfSSLv23_client_method())); + ExpectIntEQ(X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), root), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(X509_STORE_set_flags(SSL_CTX_get_cert_store(sslCtx), + X509_V_FLAG_NO_CHECK_TIME), 1); + ExpectIntEQ(X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), root), + WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + /* Nor does an SSL_CTX trust it when given the store. The intermediates + * of the store go in as before. */ + ExpectIntEQ(X509_STORE_add_cert(store, datedRoot), 1); + ExpectIntEQ(X509_STORE_add_cert(store, expiredInt), 1); + ExpectIntEQ(X509_VERIFY_PARAM_set_flags(X509_STORE_get0_param(store), + X509_V_FLAG_NO_CHECK_TIME), 1); + SSL_CTX_set_cert_store(sslCtx, store); + if (SSL_CTX_get_cert_store(sslCtx) == store) { + store = NULL; + } + ExpectIntEQ(wolfSSL_CertManagerVerify(wolfSSL_CTX_GetCertManager(sslCtx), + leafFile, SSL_FILETYPE_PEM), WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)); + ExpectIntEQ(wolfSSL_CertManagerVerify(wolfSSL_CTX_GetCertManager(sslCtx), + TA_CERT_DIR "dated-int-leaf-cert.pem", SSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + SSL_CTX_free(sslCtx); + X509_free(datedRoot); + X509_free(expiredInt); +#endif + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + X509_free(root); + X509_free(renewed); + X509_free(leaf); +#endif + return EXPECT_RESULT(); +} + #if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) static int test_X509_STORE_untrusted_load_cert_to_stack(const char* filename, STACK_OF(X509)* chain) @@ -6056,3 +8611,62 @@ int test_wolfSSL_CTX_set_cert_store(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(NO_FILESYSTEM) +static int test_X509_verify_cert_tls_cached_ca_cb(int ok, X509_STORE_CTX* ctx) +{ + int err = X509_STORE_CTX_get_error(ctx); + + return ok || err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || + err == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E); +} +#endif + +/* A CA that TLS cached from a peer's chain, after the verify callback waived + * its missing issuer, is not a trust anchor for X509_verify_cert() on the + * SSL_CTX's store. OpenSSL never adds the peer's certs to the store. */ +int test_X509_verify_cert_tls_cached_ca(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(NO_FILESYSTEM) + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + X509_STORE_CTX* storeCtx = NULL; + X509* leaf = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method), 0); + /* The client trusts no CA of the server's chain. */ + ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, + "./certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, "./certs/server-key.pem", + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, + test_X509_verify_cert_tls_cached_ca_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Issued by an intermediate of the server's chain */ + ExpectNotNull(leaf = X509_load_certificate_file( + "./certs/intermediate/client-int-cert.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(storeCtx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(storeCtx, SSL_CTX_get_cert_store(ctx_c), + leaf, NULL), 1); + ExpectIntNE(X509_verify_cert(storeCtx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(storeCtx), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + + X509_STORE_CTX_free(storeCtx); + X509_free(leaf); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_c); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ossl_x509_str.h b/tests/api/test_ossl_x509_str.h index a4860db950d..758398a6917 100644 --- a/tests/api/test_ossl_x509_str.h +++ b/tests/api/test_ossl_x509_str.h @@ -48,6 +48,10 @@ int test_X509_verify_cert_internal_err(void); int test_X509_verify_cert_ca_no_keycertsign(void); int test_X509_verify_cert_selfsigned_anchor_no_keycertsign(void); int test_X509_verify_cert_stale_temp_ca(void); +int test_X509_verify_cert_chains(void); +int test_X509_verify_cert_oom(void); +int test_X509_verify_cert_tls_cached_ca(void); +int test_X509_STORE_add_cert_expired(void); int test_X509_STORE_untrusted(void); int test_X509_STORE_InvalidCa(void); int test_X509_STORE_InvalidCa_CtxCallback(void); @@ -108,6 +112,10 @@ int test_wolfSSL_CTX_set_cert_store(void); TEST_DECL_GROUP("ossl_x509_store", \ test_X509_verify_cert_selfsigned_anchor_no_keycertsign), \ TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_stale_temp_ca), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_chains), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_oom), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_tls_cached_ca), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_add_cert_expired), \ TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_untrusted), \ TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_InvalidCa), \ TEST_DECL_GROUP("ossl_x509_store", test_X509_STORE_InvalidCa_CtxCallback), \ From f78dc609f206638b2f0fcf19560cfc08b3ff4d28 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 5 Nov 2026 14:33:12 +0000 Subject: [PATCH 23/31] Add xmlsec workflow Build wolfSSL with --enable-all, build xmlsec with the osp patch and run its key, signature and encryption test scripts. The failed tests must match the per-version list of known DSA failures. Add the xmlsec build dependencies to the ubuntu-24.04-full apt package list. --- .../ci-deps/packages-ubuntu-24.04-full.txt | 3 + .github/workflows/xmlsec.yml | 175 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 .github/workflows/xmlsec.yml diff --git a/.github/ci-deps/packages-ubuntu-24.04-full.txt b/.github/ci-deps/packages-ubuntu-24.04-full.txt index a0e73e0f3d4..e9d09dde43e 100644 --- a/.github/ci-deps/packages-ubuntu-24.04-full.txt +++ b/.github/ci-deps/packages-ubuntu-24.04-full.txt @@ -58,6 +58,7 @@ libjansson-dev libkrb5-dev libldb-dev libldb2 +libltdl-dev liblz4-dev liblzma-dev liblzo2-dev @@ -82,6 +83,8 @@ libssl-dev libtool liburcu-dev libuv1-dev +libxml2-dev +libxslt1-dev linux-libc-dev make man2html diff --git a/.github/workflows/xmlsec.yml b/.github/workflows/xmlsec.yml new file mode 100644 index 00000000000..b9901784bec --- /dev/null +++ b/.github/workflows/xmlsec.yml @@ -0,0 +1,175 @@ +name: xmlsec Tests + +# START OF COMMON SECTION +on: + push: + branches: [ 'release/**' ] + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read +# END OF COMMON SECTION + +jobs: + build_wolfssl: + name: Build wolfSSL + if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} + # Just to keep it the same as the testing target + runs-on: ubuntu-24.04 + # This should be a safe limit for the tests to run. + timeout-minutes: 4 + steps: + - name: Build wolfSSL + uses: wolfSSL/actions-build-autotools-project@v1 + with: + path: wolfssl + # The CFLAGS only relax limits for the xmlsec test data: a 512-bit + # RSA CA, rsa-1024 key generation, serial-0 certs and MD5-signed + # certs. + configure: >- + --enable-all + 'CFLAGS=-DWOLFSSL_MIN_RSA_BITS=512 -DRSA_MIN_SIZE=1024 -DWOLFSSL_ASN_ALLOW_0_SERIAL -DWOLFSSL_ALLOW_MD5_CERT_SIGS' + install: true + check: false + + - name: tar build-dir + run: tar -zcf build-dir.tgz build-dir + + - name: Upload built lib + uses: actions/upload-artifact@v6 + with: + name: wolf-install-xmlsec + path: build-dir.tgz + retention-days: 5 + + xmlsec_check: + strategy: + fail-fast: false + matrix: + include: + - ref: 1.2.42 + tag: xmlsec-1_2_42 + sha256: 5359e3c19016ab164b505c1222ef5a2d87b34f50f85ff91d765f4aaa0df42db2 + # wolfSSL PKCS12_parse() does not load DSA keys, and a DSA CA's + # key is not kept for chain verification. + expect_fail: >- + aleksey-xmldsig-01/enveloping-dsa-x509chain + aleksey-xmldsig-01/enveloping-sha256-dsa2048-sha256 + aleksey-xmldsig-01/enveloping-sha256-dsa3072-sha256 + merlin-exc-c14n-one/exc-signature + merlin-xmldsig-twenty-three/signature + merlin-xmldsig-twenty-three/signature-enveloped-dsa + merlin-xmldsig-twenty-three/signature-enveloping-b64-dsa + merlin-xmldsig-twenty-three/signature-enveloping-dsa + merlin-xmldsig-twenty-three/signature-external-b64-dsa + merlin-xmldsig-twenty-three/signature-external-dsa + merlin-xmldsig-twenty-three/signature-keyname + merlin-xmldsig-twenty-three/signature-retrievalmethod-rawx509crt + merlin-xmldsig-twenty-three/signature-x509-crt + merlin-xmldsig-twenty-three/signature-x509-is + merlin-xmldsig-twenty-three/signature-x509-ski + merlin-xmldsig-twenty-three/signature-x509-sn + phaos-xmldsig-three/signature-dsa-detached + phaos-xmldsig-three/signature-dsa-enveloped + phaos-xmldsig-three/signature-dsa-enveloping + name: ${{ matrix.ref }} + if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} + runs-on: ubuntu-24.04 + # This should be a safe limit for the tests to run. + timeout-minutes: 10 + needs: build_wolfssl + steps: + - name: Checkout wolfSSL CI actions + uses: actions/checkout@v5 + with: + sparse-checkout: .github/actions + fetch-depth: 1 + + - name: Install dependencies + uses: ./.github/actions/install-apt-deps + with: + # Fit the loop inside this job's timeout-minutes. + budget-seconds: '120' + pull-timeout: '60' + packages: libltdl-dev libxml2-dev libxslt1-dev + ghcr-debs-tag: ubuntu-24.04-full + + - name: Download lib + uses: actions/download-artifact@v7 + with: + name: wolf-install-xmlsec + + - name: untar build-dir + run: tar -xf build-dir.tgz + + - name: Checkout OSP + uses: actions/checkout@v5 + with: + repository: wolfssl/osp + path: osp + fetch-depth: 1 + + - name: Download xmlsec + run: | + curl -fLO https://github.com/lsh123/xmlsec/releases/download/${{ matrix.tag }}/xmlsec1-${{ matrix.ref }}.tar.gz + echo "${{ matrix.sha256 }} xmlsec1-${{ matrix.ref }}.tar.gz" | sha256sum -c - + tar xf xmlsec1-${{ matrix.ref }}.tar.gz + + - name: Build xmlsec + working-directory: xmlsec1-${{ matrix.ref }} + run: | + patch -p1 < "$GITHUB_WORKSPACE/osp/xmlsec/${{ matrix.ref }}/xmlsec1-${{ matrix.ref }}.patch" + # --disable-ripemd160: wolfSSL EVP_ripemd160() is a stub + OPENSSL_CFLAGS="-I$GITHUB_WORKSPACE/build-dir/include -I$GITHUB_WORKSPACE/build-dir/include/wolfssl -DWOLFSSL_USE_OPTIONS_H" \ + OPENSSL_LIBS="-L$GITHUB_WORKSPACE/build-dir/lib -Wl,-rpath,$GITHUB_WORKSPACE/build-dir/lib -lwolfssl" \ + ./configure --with-default-crypto=openssl --without-nss --without-gnutls \ + --without-gcrypt --disable-ripemd160 --disable-docs --disable-mans + make -j + ldd src/openssl/.libs/libxmlsec1-openssl.so | grep wolfssl + if ldd src/openssl/.libs/libxmlsec1-openssl.so | grep -E 'libssl|libcrypto'; then + echo "::error::libxmlsec1-openssl is linked against OpenSSL" + exit 1 + fi + + - name: Run xmlsec tests + working-directory: xmlsec1-${{ matrix.ref }} + env: + EXPECT_FAIL: ${{ matrix.expect_fail }} + run: | + export TMPFOLDER="$GITHUB_WORKSPACE/xmlsec-logs" + mkdir -p "$TMPFOLDER" + # make check stops at the first failing script, so run each one + for t in keys dsig enc; do + make "check-$t" > "$TMPFOLDER/check-$t.log" 2>&1 || true + if ! grep -- '--- TOTAL' "$TMPFOLDER/check-$t.log"; then + tail -n 50 "$TMPFOLDER/check-$t.log" + echo "::error::check-$t did not finish" + exit 1 + fi + done + # A test name is an unindented line. Its steps are indented and end + # in OK, Fail or Skip. + for t in keys dsig enc; do + awk '/^--- TOTAL/ {exit} /^[^ \t]/ {name = $0} /[ \t]Fail$/ {print name}' "$TMPFOLDER/check-$t.log" + done | sort -u > "$TMPFOLDER/failed.txt" + # Tests are matched by name: a listed test that fails for another + # reason is not caught. + echo "$EXPECT_FAIL" | tr ' ' '\n' | sed '/^$/d' | sort -u > "$TMPFOLDER/expected.txt" + if ! diff -u "$TMPFOLDER/expected.txt" "$TMPFOLDER/failed.txt"; then + echo "::error::Failed tests differ from expect_fail (- passes now, + new failure)" + exit 1 + fi + + - name: Upload test logs + if: ${{ failure() }} + uses: actions/upload-artifact@v6 + with: + name: xmlsec-${{ matrix.ref }}-logs + path: xmlsec-logs + retention-days: 5 From eec68d22368deb4aab5d92a5b74767e4457566a9 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 11:57:12 +0000 Subject: [PATCH 24/31] Pass no CRL issuer or check time from the new CheckCertCRLFromCm callers Upstream added callers of the old three-argument form. NULL issuer and check time keep the handshake's CRL checks. --- src/internal.c | 4 ++-- src/ssl_certman.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/internal.c b/src/internal.c index 94a86daecb3..1df8ee410b9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -17598,7 +17598,7 @@ void DoCrlCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, /* Point to current cm to be able to verify CRL */ userCrl->cm = SSL_CM(ssl); *outRet = CheckCertCRLFromCm(SSL_CM(ssl), userCrl, - args->dCert); + args->dCert, NULL, NULL); } else *outRet = CRL_MISSING; @@ -46091,7 +46091,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #ifdef HAVE_CRL if (ret == 0 && SSL_CM(ssl)->crlEnabled) { ret = CheckCertCRLFromCm(SSL_CM(ssl), SSL_CM(ssl)->crl, - dCert); + dCert, NULL, NULL); } #endif if (ret == 0) { diff --git a/src/ssl_certman.c b/src/ssl_certman.c index 98249fe60f3..9a8bf9627ee 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -952,7 +952,7 @@ int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, if (cm->crlEnabled && ((ret == 0) || (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) || (ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)))) { - crlRet = CheckCertCRLFromCm(cm, cm->crl, cert); + crlRet = CheckCertCRLFromCm(cm, cm->crl, cert, NULL, NULL); if (crlRet != 0) { WOLFSSL_MSG("CheckCertCRL failed"); } @@ -2136,7 +2136,7 @@ int wolfSSL_CertManagerCheckCRL(WOLFSSL_CERT_MANAGER* cm, /* A date error is returned only after the parse completed, * so still answer the revocation question that was asked. */ ret = cm_crl_merge(ret, - CheckCertCRLFromCm(cm, cm->crl, cert)); + CheckCertCRLFromCm(cm, cm->crl, cert, NULL, NULL)); } /* Dispose of dynamically allocated memory. */ From fa9668aaf5a70248e02dc6176136b423d5e3e798 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 11:57:36 +0000 Subject: [PATCH 25/31] Drop the branch's X509_get0_pubkey cache and SetPeerCert Upstream caches the key in key.pkey and keeps the peer cert for the whole handshake. The pubKeyPKey member and the static SetPeerCert are left over and clash with upstream's SetPeerCert. --- src/internal.c | 47 ---------------------------------------------- wolfssl/internal.h | 3 --- 2 files changed, 50 deletions(-) diff --git a/src/internal.c b/src/internal.c index 1df8ee410b9..2f2508e2bd4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6066,8 +6066,6 @@ static void FreeX509Contents(WOLFSSL_X509* x509) XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); x509->extKeyUsageSrc= NULL; } - wolfSSL_EVP_PKEY_free(x509->pubKeyPKey); - x509->pubKeyPKey = NULL; #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ #if defined(OPENSSL_ALL) if (x509->algor.algorithm) { @@ -17170,51 +17168,6 @@ void DoCertFatalAlert(WOLFSSL* ssl, int ret) ssl->options.fatalAlert = 1; } -#ifdef KEEP_PEER_CERT -/* Refill ssl->peerCert in place. Keep its X509_get0_pubkey() key when the - * public key is the same: the app may still hold it. */ -static int SetPeerCert(WOLFSSL* ssl, DecodedCert* dCert) -{ - int ret; -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - WOLFSSL_EVP_PKEY* pkey = ssl->peerCert.pubKeyPKey; - buffer pub = ssl->peerCert.pubKey; - int keyOID = ssl->peerCert.pubKeyOID; -#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ - defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ - defined(WOLFSSL_HAVE_SLHDSA) - word32 curveOID = ssl->peerCert.pkCurveOID; -#endif - - ssl->peerCert.pubKeyPKey = NULL; - ssl->peerCert.pubKey.buffer = NULL; - ssl->peerCert.pubKey.length = 0; -#endif - FreeX509(&ssl->peerCert); - InitX509(&ssl->peerCert, 0, ssl->heap); - ret = CopyDecodedToX509(&ssl->peerCert, dCert); -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - if ((ret == 0) && (pub.buffer != NULL) && - (ssl->peerCert.pubKey.buffer != NULL) && - (keyOID == ssl->peerCert.pubKeyOID) && -#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ - defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ - defined(WOLFSSL_HAVE_SLHDSA) - (curveOID == ssl->peerCert.pkCurveOID) && -#endif - (pub.length == ssl->peerCert.pubKey.length) && - (XMEMCMP(pub.buffer, ssl->peerCert.pubKey.buffer, - pub.length) == 0)) { - ssl->peerCert.pubKeyPKey = pkey; - pkey = NULL; - } - wolfSSL_EVP_PKEY_free(pkey); - XFREE(pub.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); -#endif - return ret; -} -#endif /* KEEP_PEER_CERT */ - int SetupStoreCtxCallback(WOLFSSL_X509_STORE_CTX** store_pt, WOLFSSL* ssl, WOLFSSL_CERT_MANAGER* cm, ProcPeerCertArgs* args, int cert_err, void* heap, int* x509Free) diff --git a/wolfssl/internal.h b/wolfssl/internal.h index da7ef319412..644a2836809 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6389,9 +6389,6 @@ struct WOLFSSL_X509 { #endif buffer pubKey; int pubKeyOID; -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - WOLFSSL_EVP_PKEY* pubKeyPKey; /* owned, from wolfSSL_X509_get0_pubkey */ -#endif DNS_entry* altNamesNext; /* hint for retrieval */ #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \ defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \ From 1ed3adbf9aa6ccc87a627f4a74b4e7bb1d91050f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 11:57:49 +0000 Subject: [PATCH 26/31] Rename the branch's X509_get0_pubkey test and expect a shared key Upstream added a test of the same name. Its X509_get_pubkey returns a new reference to the cached key, not a new key. --- tests/api/test_ossl_x509_pk.c | 6 +++--- tests/api/test_ossl_x509_pk.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/api/test_ossl_x509_pk.c b/tests/api/test_ossl_x509_pk.c index 714d3886fb6..67aa042947a 100644 --- a/tests/api/test_ossl_x509_pk.c +++ b/tests/api/test_ossl_x509_pk.c @@ -346,9 +346,9 @@ static int test_X509_get0_pubkey_file(const char* file) /* The cert owns the key: the same key every time. */ ExpectNotNull(key = X509_get0_pubkey(x509)); ExpectPtrEq(X509_get0_pubkey(x509), key); - /* X509_get_pubkey returns a new key for the caller to free. */ + /* X509_get_pubkey returns a new reference for the caller to free. */ ExpectNotNull(pkey = X509_get_pubkey(x509)); - ExpectPtrNE(pkey, key); + ExpectPtrEq(pkey, key); ExpectIntGT(derSz = i2d_PUBKEY(key, &der), 0); ExpectIntEQ(i2d_PUBKEY(pkey, &der2), derSz); ExpectBufEQ(der2, der, derSz); @@ -374,7 +374,7 @@ static int test_X509_get0_pubkey_file(const char* file) } #endif -int test_wolfSSL_X509_get0_pubkey(void) +int test_wolfSSL_X509_get0_pubkey_files(void) { EXPECT_DECLS; #if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) diff --git a/tests/api/test_ossl_x509_pk.h b/tests/api/test_ossl_x509_pk.h index cfaddfdca7c..35dcb269120 100644 --- a/tests/api/test_ossl_x509_pk.h +++ b/tests/api/test_ossl_x509_pk.h @@ -30,7 +30,7 @@ int test_wolfSSL_X509_PUBKEY_EC(void); int test_wolfSSL_X509_PUBKEY_DSA(void); int test_wolfSSL_X509_PUBKEY_MLDSA(void); int test_wolfSSL_X509_PUBKEY_get(void); -int test_wolfSSL_X509_get0_pubkey(void); +int test_wolfSSL_X509_get0_pubkey_files(void); int test_wolfSSL_X509_get0_pubkey_peer(void); int test_wolfSSL_X509_set_pubkey(void); int test_wolfSSL_X509_get0_pubkey(void); @@ -47,6 +47,7 @@ int test_wolfSSL_X509_PUBKEY_get0_param_fail(void); TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_PUBKEY_get), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_set_pubkey), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey), \ + TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_files), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_peer), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get_pubkey_key_types), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_threads), \ From dfe025a06e33ad238b3814d484af4d634b58971f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 12:04:57 +0000 Subject: [PATCH 27/31] Verify the DER of the EVP_SignFinal DSA signature in the digest length test EVP_PKEY_sign writes and EVP_PKEY_verify takes only DER for DSA, so the upstream test needs a buffer for DER and the raw EVP_SignFinal signature encoded. --- tests/api/test_evp_pkey.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index bcfbf2359e3..bbbbfe27f33 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -2252,11 +2252,18 @@ int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void) WOLFSSL_EVP_MD_CTX* mdCtx = NULL; byte digest[SHA256_DIGEST_LENGTH]; byte other[SHA256_DIGEST_LENGTH]; - byte sig[DSA_MAX_SIG_SIZE]; + /* EVP_PKEY_sign() writes DER, EVP_SignFinal() raw r and s. */ + byte sig[DSA_MAX_SIG_SIZE + 16]; + byte rawSig[DSA_MAX_SIG_SIZE]; byte msg[16]; size_t siglen = sizeof(sig); - unsigned int sigFinalLen = sizeof(sig); + unsigned int sigFinalLen = sizeof(rawSig); SHA256_CTX shaCtx; + DSA_SIG* dsaSig = NULL; + BIGNUM* r = NULL; + BIGNUM* s = NULL; + unsigned char* der = NULL; + int derSz = 0; XMEMSET(msg, 0x33, sizeof(msg)); XMEMSET(digest, 0x5a, sizeof(digest)); @@ -2290,7 +2297,7 @@ int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void) ExpectNotNull(mdCtx = EVP_MD_CTX_new()); ExpectIntEQ(EVP_SignInit(mdCtx, EVP_sha256()), WOLFSSL_SUCCESS); ExpectIntEQ(EVP_SignUpdate(mdCtx, msg, sizeof(msg)), WOLFSSL_SUCCESS); - ExpectIntEQ(EVP_SignFinal(mdCtx, sig, &sigFinalLen, pkey), + ExpectIntEQ(EVP_SignFinal(mdCtx, rawSig, &sigFinalLen, pkey), WOLFSSL_SUCCESS); SHA256_Init(&shaCtx); SHA256_Update(&shaCtx, msg, sizeof(msg)); @@ -2298,8 +2305,24 @@ int test_wolfSSL_EVP_PKEY_sign_dsa_digest_len(void) #ifdef WOLFSSL_SMALL_STACK_CACHE wc_Sha256Free((wc_Sha256*)&shaCtx); #endif - ExpectIntEQ(EVP_PKEY_verify(ctxVerify, sig, (size_t)sigFinalLen, digest, + /* EVP_PKEY_verify() takes only the DER encoding. */ + ExpectIntEQ(sigFinalLen, 2 * SHA256_DIGEST_LENGTH); + ExpectNotNull(dsaSig = DSA_SIG_new()); + ExpectNotNull(r = BN_bin2bn(rawSig, SHA256_DIGEST_LENGTH, NULL)); + ExpectNotNull(s = BN_bin2bn(rawSig + SHA256_DIGEST_LENGTH, + SHA256_DIGEST_LENGTH, NULL)); + ExpectIntEQ(DSA_SIG_set0(dsaSig, r, s), 1); + if (EXPECT_SUCCESS()) { + r = NULL; + s = NULL; + } + ExpectIntGT(derSz = i2d_DSA_SIG(dsaSig, &der), 0); + ExpectIntEQ(EVP_PKEY_verify(ctxVerify, der, (size_t)derSz, digest, sizeof(digest)), WOLFSSL_SUCCESS); + XFREE(der, NULL, DYNAMIC_TYPE_ASN1); + BN_free(r); + BN_free(s); + DSA_SIG_free(dsaSig); EVP_MD_CTX_free(mdCtx); EVP_PKEY_CTX_free(ctxVerify); From d4841a5088a0f0b713bac1c45670272fed4dfa14 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 12:05:04 +0000 Subject: [PATCH 28/31] Expect OpenSSL's verdicts in the X509_verify_cert CRL date callback test As in OpenSSL, the chain reports the trust anchor to the callback too, a leaf without an issuer gets error 21 after 20, and a CRL that is not yet valid is reported before the revocation. --- tests/api/test_ossl_x509_str.c | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 29abd6f7965..4245f4cbae0 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -448,7 +448,8 @@ int test_wolfSSL_X509_verify_cert_crl_date_override(void) #if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) && !defined(NO_RSA) && \ !defined(NO_FILESYSTEM) && !defined(NO_ASN_TIME) && \ !defined(WOLFSSL_CRL_ALLOW_MISSING_CDP) -static int test_crl_date_cb_waive[2]; +/* The third verdict to waive is set by the test itself. */ +static int test_crl_date_cb_waive[3]; static int test_crl_date_cb_seen[4]; static int test_crl_date_cb_calls; #ifdef OPENSSL_ALL @@ -469,7 +470,8 @@ static int test_crl_date_verify_cb(int ok, X509_STORE_CTX* ctx) test_crl_date_cb_seen[test_crl_date_cb_calls] = err; test_crl_date_cb_calls++; return (err == test_crl_date_cb_waive[0]) || - (err == test_crl_date_cb_waive[1]); + (err == test_crl_date_cb_waive[1]) || + (err == test_crl_date_cb_waive[2]); } static int test_crl_date_verify(X509_STORE* store, X509* cert, int waive0, @@ -562,9 +564,12 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) const int noCrl = WOLFSSL_X509_V_ERR_UNABLE_TO_GET_CRL; const int notYet = WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID; const int revokedErr = WOLFSSL_X509_V_ERR_CERT_REVOKED; + const int crlNotYet = WOLFSSL_X509_V_ERR_CRL_NOT_YET_VALID; /* Waives no verdict the store reports. */ const int none = -1; + test_crl_date_cb_waive[2] = none; + ExpectNotNull(store = X509_STORE_new()); ExpectNotNull(ca = wolfSSL_X509_load_certificate_file( "./certs/ca-cert.pem", SSL_FILETYPE_PEM)); @@ -586,11 +591,12 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) ExpectIntEQ(test_crl_date_cb_calls, 1); #ifdef OPENSSL_ALL - /* A certificate that verified is passed to the callback once. */ + /* Each certificate that verified is passed to the callback once, the + * trust anchor included, as in OpenSSL. */ ExpectNotNull(plainStore = X509_STORE_new()); ExpectIntEQ(X509_STORE_add_cert(plainStore, ca), SSL_SUCCESS); ExpectIntEQ(test_crl_date_verify(plainStore, cert, none, none, &err), 1); - ExpectIntEQ(test_crl_date_cb_ok_calls, 1); + ExpectIntEQ(test_crl_date_cb_ok_calls, 2); ExpectIntEQ(test_crl_date_cb_calls, 0); /* A store callback can still reject it. */ if (plainStore != NULL) @@ -686,18 +692,21 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) ExpectIntEQ(test_crl_date_cb_seen[0], noCrl); ExpectIntEQ(test_crl_date_cb_seen[1], notYet); + /* As in OpenSSL, the dates of the trust anchor are checked too. */ ExpectIntEQ(test_crl_date_verify(store, cert, noCrl, notYet, &err), 1); ExpectIntEQ(err, notYet); - ExpectIntEQ(test_crl_date_cb_calls, 2); + ExpectIntEQ(test_crl_date_cb_calls, 3); #ifdef OPENSSL_ALL - /* The dates are still checked once a missing issuer is waived. */ + /* The dates are still checked once a missing issuer is waived. As in + * OpenSSL, the leaf signature that cannot be verified is reported too. */ ExpectNotNull(plainStore = X509_STORE_new()); ExpectIntEQ(test_crl_date_verify(plainStore, cert, - WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, none, &err), 0); + WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + WOLFSSL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, &err), 0); ExpectIntEQ(err, notYet); - ExpectIntEQ(test_crl_date_cb_calls, 2); - ExpectIntEQ(test_crl_date_cb_seen[1], notYet); + ExpectIntEQ(test_crl_date_cb_calls, 3); + ExpectIntEQ(test_crl_date_cb_seen[2], notYet); X509_STORE_free(plainStore); plainStore = NULL; #endif @@ -717,12 +726,17 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) XFCLOSE(fp); ExpectIntEQ(X509_STORE_add_crl(store, crl), SSL_SUCCESS); + /* As in OpenSSL, the CRL is not yet valid either, and that is reported + * first. */ + test_crl_date_cb_waive[2] = crlNotYet; ExpectIntEQ(test_crl_date_verify(store, revoked, revokedErr, notYet, &err), 1); ExpectIntEQ(err, notYet); - ExpectIntEQ(test_crl_date_cb_calls, 2); - ExpectIntEQ(test_crl_date_cb_seen[0], revokedErr); - ExpectIntEQ(test_crl_date_cb_seen[1], notYet); + ExpectIntEQ(test_crl_date_cb_calls, 4); + ExpectIntEQ(test_crl_date_cb_seen[0], crlNotYet); + ExpectIntEQ(test_crl_date_cb_seen[1], revokedErr); + ExpectIntEQ(test_crl_date_cb_seen[2], notYet); + ExpectIntEQ(test_crl_date_cb_seen[3], notYet); /* A CRL set on the context is consulted before the dates as well. */ ExpectTrue((fp = XFOPEN("./certs/crl/crl.revoked", "rb")) != XBADFILE); @@ -744,8 +758,10 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) test_crl_date_cb_calls = 0; ExpectIntNE(X509_verify_cert(storeCtx), SSL_SUCCESS); ExpectIntEQ(X509_STORE_CTX_get_error(storeCtx), revokedErr); - ExpectIntEQ(test_crl_date_cb_calls, 1); + ExpectIntEQ(test_crl_date_cb_calls, 2); + ExpectIntEQ(test_crl_date_cb_seen[0], crlNotYet); X509_STORE_CTX_free(storeCtx); + test_crl_date_cb_waive[2] = none; wc_SetTimeCb(NULL); From 1734b6196ae87420ffa5d636b64f2dbc8b338c15 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 12:05:33 +0000 Subject: [PATCH 29/31] Expect no CertManager callback from X509_verify_cert in the CRL date test The chain is built from X509 objects, not verified with the cert manager, so its verify callback is not asked about the CRL or the dates. --- tests/api/test_ossl_x509_str.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 4245f4cbae0..2dd18a16524 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -613,8 +613,9 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) WOLFSSL_CTX* sslCtx = NULL; X509_STORE* ctxStore = NULL; - /* A CertManager callback on the context's store sees the CRL - * verdict. */ + /* X509_verify_cert() builds the chain itself, as OpenSSL: a + * CertManager callback on the context's store is not asked about + * the CRL verdict. */ ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_client_method())); ExpectIntEQ(wolfSSL_CTX_load_verify_locations(sslCtx, "./certs/ca-cert.pem", NULL), WOLFSSL_SUCCESS); @@ -628,16 +629,11 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) test_crl_date_cm_cb_waive = 1; test_crl_date_cm_cb_calls = 0; - ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 1); - ExpectIntEQ(test_crl_date_cm_cb_calls, 1); - - test_crl_date_cm_cb_waive = 0; - test_crl_date_cm_cb_calls = 0; ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 0); ExpectIntEQ(err, noCrl); - ExpectIntEQ(test_crl_date_cm_cb_calls, 1); + ExpectIntEQ(test_crl_date_cm_cb_calls, 0); - /* The context CRLs still apply after it rejected the missing CRL. */ + /* The context CRLs apply. */ ExpectNotNull(crls = sk_X509_CRL_new_null()); ExpectIntEQ(test_crl_date_read_crl(crls, "./certs/crl/crl.pem"), TEST_SUCCESS); @@ -666,13 +662,13 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) sk_X509_CRL_pop_free(crls, X509_CRL_free); crls = NULL; - /* Waiving the date error still leaves the CRL verdict to reject. */ + /* Nor about the date error. */ ExpectIntEQ(wc_SetTimeCb(test_crl_date_override_time_cb), 0); - test_crl_date_cm_cb_waive = 1; + test_crl_date_cm_cb_waive = 2; test_crl_date_cm_cb_calls = 0; ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 0); ExpectIntEQ(err, noCrl); - ExpectIntEQ(test_crl_date_cm_cb_calls, 2); + ExpectIntEQ(test_crl_date_cm_cb_calls, 0); wc_SetTimeCb(NULL); wolfSSL_CTX_free(sslCtx); From 3d216c62a8220b0b8393775d5e9240126bb01632 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 12:59:50 +0000 Subject: [PATCH 30/31] Use the CertManager verify callback in X509_verify_cert A callback set with wolfSSL_CertManagerSetVerify on the store's CertManager, as an SSL_CTX shares with its X509_STORE, is now asked about each error instead of the X509_STORE_CTX or X509_STORE callback; only one of them is called. Restore the CertManager callback checks of test_wolfSSL_X509_verify_cert_crl_date_callback. As with the store callback, revocation is reported before the dates, so waiving the first error leaves the date error to reject. --- src/x509_str.c | 5 +++++ tests/api/test_ossl_x509_str.c | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/x509_str.c b/src/x509_str.c index f4c9ff128ce..9b68860b6aa 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -537,6 +537,11 @@ static WOLFSSL_X509_STORE_CTX_verify_cb X509StoreGetVerifyCb( if (ctx == NULL) return NULL; + /* A CertManager callback, as of an SSL_CTX's store, is used instead. */ + if (ctx->store != NULL && ctx->store->cm != NULL && + ctx->store->cm->verifyCallback != NULL) + return ctx->store->cm->verifyCallback; + if (ctx->verify_cb != NULL) return ctx->verify_cb; diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 2dd18a16524..24798cc8b45 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -613,9 +613,8 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) WOLFSSL_CTX* sslCtx = NULL; X509_STORE* ctxStore = NULL; - /* X509_verify_cert() builds the chain itself, as OpenSSL: a - * CertManager callback on the context's store is not asked about - * the CRL verdict. */ + /* A CertManager callback on the context's store sees the CRL + * verdict. */ ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_client_method())); ExpectIntEQ(wolfSSL_CTX_load_verify_locations(sslCtx, "./certs/ca-cert.pem", NULL), WOLFSSL_SUCCESS); @@ -629,11 +628,16 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) test_crl_date_cm_cb_waive = 1; test_crl_date_cm_cb_calls = 0; + ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 1); + ExpectIntEQ(test_crl_date_cm_cb_calls, 1); + + test_crl_date_cm_cb_waive = 0; + test_crl_date_cm_cb_calls = 0; ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 0); ExpectIntEQ(err, noCrl); - ExpectIntEQ(test_crl_date_cm_cb_calls, 0); + ExpectIntEQ(test_crl_date_cm_cb_calls, 1); - /* The context CRLs apply. */ + /* The context CRLs still apply after it rejected the missing CRL. */ ExpectNotNull(crls = sk_X509_CRL_new_null()); ExpectIntEQ(test_crl_date_read_crl(crls, "./certs/crl/crl.pem"), TEST_SUCCESS); @@ -662,13 +666,14 @@ int test_wolfSSL_X509_verify_cert_crl_date_callback(void) sk_X509_CRL_pop_free(crls, X509_CRL_free); crls = NULL; - /* Nor about the date error. */ + /* As with the store callback, revocation is reported first, so + * the date error is what is left to reject. */ ExpectIntEQ(wc_SetTimeCb(test_crl_date_override_time_cb), 0); - test_crl_date_cm_cb_waive = 2; + test_crl_date_cm_cb_waive = 1; test_crl_date_cm_cb_calls = 0; ExpectIntEQ(test_crl_date_verify(ctxStore, cert, 0, 0, &err), 0); - ExpectIntEQ(err, noCrl); - ExpectIntEQ(test_crl_date_cm_cb_calls, 0); + ExpectIntEQ(err, notYet); + ExpectIntEQ(test_crl_date_cm_cb_calls, 2); wc_SetTimeCb(NULL); wolfSSL_CTX_free(sslCtx); From e15039f958d0264f62b1dcb8ddca80bf94c96343 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 6 Nov 2026 13:37:51 +0000 Subject: [PATCH 31/31] Keep SSL_get0_verified_chain's own verify callback X509_verify_cert now asks the CertManager callback first, so an app callback set with wolfSSL_CertManagerSetVerify replaced VerifiedChainCb when wolfSSL_get0_verified_chain built the chain. A callback wolfSSL installs for itself wins: VerifiedChainCb is returned ahead of the CertManager callback. Test that the app callback is not called while the chain is built. --- src/ssl_api_cert.c | 2 +- src/x509_str.c | 6 ++++++ tests/api/test_ssl_cert.c | 23 +++++++++++++++++++++++ wolfssl/internal.h | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index d2895e0e281..537a6b444cd 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -3403,7 +3403,7 @@ WOLF_STACK_OF(WOLFSSL_X509)* wolfSSL_set_peer_cert_chain(WOLFSSL* ssl) * @param [in] ctx X509 store context. * @return 1 when verification is to continue, 0 to fail. */ -static int VerifiedChainCb(int ok, WOLFSSL_X509_STORE_CTX* ctx) +int VerifiedChainCb(int ok, WOLFSSL_X509_STORE_CTX* ctx) { if ((!ok) && (ctx->error_depth > 0)) { switch (ctx->error) { diff --git a/src/x509_str.c b/src/x509_str.c index 9b68860b6aa..7784bd34dce 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -537,6 +537,12 @@ static WOLFSSL_X509_STORE_CTX_verify_cb X509StoreGetVerifyCb( if (ctx == NULL) return NULL; +#if defined(SESSION_CERTS) && defined(KEEP_PEER_CERT) + /* An internal callback, as of SSL_get0_verified_chain, comes first. */ + if (ctx->verify_cb == VerifiedChainCb) + return ctx->verify_cb; +#endif + /* A CertManager callback, as of an SSL_CTX's store, is used instead. */ if (ctx->store != NULL && ctx->store->cm != NULL && ctx->store->cm->verifyCallback != NULL) diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index d30d4734235..3805dc404d5 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -1077,6 +1077,20 @@ int test_wolfSSL_set_peer_cert_chain(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \ + defined(OPENSSL_EXTRA) && defined(KEEP_PEER_CERT) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS) +static int test_verified_chain_cm_calls = 0; + +/* CertManager verify callback that counts calls and rejects any error. */ +static int test_verified_chain_cm_cb(int ok, WOLFSSL_X509_STORE_CTX* store) +{ + (void)store; + test_verified_chain_cm_calls++; + return ok; +} +#endif + /* Test getting the verified certificate chain. * * @return TEST_SUCCESS on success. @@ -1094,6 +1108,7 @@ int test_wolfSSL_get0_verified_chain(void) #ifdef WOLFSSL_SIGNER_DER_CERT WOLFSSL_X509_STORE_CTX* storeCtx = NULL; WOLFSSL_X509* peer = NULL; + int calls = 0; #endif ExpectNull(wolfSSL_get0_verified_chain(NULL)); @@ -1105,12 +1120,20 @@ int test_wolfSSL_get0_verified_chain(void) /* Without a peer certificate there is nothing to verify. */ ExpectNull(wolfSSL_get0_verified_chain(ssl_c)); + /* The app's CertManager callback is not used to build the chain. */ + test_verified_chain_cm_calls = 0; + wolfSSL_CertManagerSetVerify(wolfSSL_CTX_GetCertManager(ctx_c), + test_verified_chain_cm_cb); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL); ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); #ifdef WOLFSSL_SIGNER_DER_CERT + calls = test_verified_chain_cm_calls; /* The server's chain verifies against the CA the client loaded. */ ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c)); ExpectIntGT(wolfSSL_sk_X509_num(chain), 0); + ExpectIntEQ(test_verified_chain_cm_calls, calls); + wolfSSL_CertManagerSetVerify(wolfSSL_CTX_GetCertManager(ctx_c), NULL); if (ssl_c != NULL) { ExpectPtrEq(ssl_c->verifiedChain, chain); } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 644a2836809..d088837de44 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3052,6 +3052,10 @@ WOLFSSL_LOCAL void CleanupStoreCtxCallback(WOLFSSL_X509_STORE_CTX* store, WOLFSSL_LOCAL int X509StoreLoadCertBuffer(WOLFSSL_X509_STORE *str, byte *buf, word32 bufLen, int type); WOLFSSL_LOCAL int X509StorePushCertsToCM(WOLFSSL_X509_STORE* store); +#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && \ + defined(KEEP_PEER_CERT) +WOLFSSL_LOCAL int VerifiedChainCb(int ok, WOLFSSL_X509_STORE_CTX* ctx); +#endif #endif /* !defined NO_CERTS */ /* wolfSSL Sock Addr */