Repository navigation
Add OpenSSL compatibility fixes and X509_verify_cert chain building for xmlsec - #12041
Open
julek-wolfssl wants to merge 31 commits into
Open
julek-wolfssl wants to merge 31 commits into
julek-wolfssl wants to merge 31 commits into
Conversation
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Upstream added callers of the old three-argument form. NULL issuer and check time keep the handshake's CRL checks.
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.
Upstream added a test of the same name. Its X509_get_pubkey returns a new reference to the cached key, not a new key.
…h 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.
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.
…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.
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.
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.
|
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The multithreaded fallback has an unsynchronized counter that can return duplicate library numbers, and err.h leaves the new API type and declarations dependent on header include order.
2 open issues
What changed in this PR
This PR improves wolfSSL’s OpenSSL compatibility for xmlsec, focusing on X509 chain verification, cryptographic APIs, and object ownership.
Changes:
- Reworks certificate-chain verification and CRL handling, with trust-anchor fixtures and regression coverage.
- Extends compatibility APIs and fixes encoding, reference counting, and sensitive-data cleanup.
- Updates build options, documentation, and CI dependencies.
| File | Description |
|---|---|
| wolfssl/wolfcrypt/settings.h | Retains signer DER in OpenSSL-extra builds. |
| wolfssl/wolfcrypt/pkcs12.h | Declares parsing support for bag attributes. |
| wolfssl/wolfcrypt/asn.h | Declares signature and name-constraint helpers. |
| wolfssl/test.h | Uses error depth in certificate display callbacks. |
| wolfssl/openssl/rsa.h | Exposes OAEP padding compatibility APIs. |
| wolfssl/openssl/evp.h | Adds RSA key-type compatibility constants. |
| wolfssl/openssl/err.h | Adds error-registration aliases and packing macro. |
| wolfssl/openssl/ec.h | Adds prime-curve short names. |
| wolfssl/openssl/dsa.h | Adds DSA reference counting and size API. |
| wolfssl/openssl/crypto.h | Exposes memory-function retrieval aliases. |
| wolfssl/openssl/compat_types.h | Defines the stack comparator callback type. |
| wolfssl/openssl/asn1.h | Updates ASN1 compatibility aliases. |
| wolfssl/internal.h | Adds verification helpers and object metadata. |
| wolfssl/crl.h | Adds issuer and time parameters for CRL checks. |
| tests/unit-mcdc/test_crl_whitebox.c | Updates CRL helper calls. |
| tests/api/test_x509.h | Registers encoding and object-reuse tests. |
| tests/api/test_tls.h | Registers parameter-isolation coverage. |
| tests/api/test_tls.c | Tests connection flags remaining separate from store flags. |
| tests/api/test_ssl_cert.h | Registers verified-chain anchor coverage. |
| tests/api/test_ossl_x509.c | Updates empty-name hash expectations. |
| tests/api/test_ossl_x509_str.h | Registers chain, CRL, store, and allocation-failure tests. |
| tests/api/test_ossl_x509_pk.h | Registers public-key retrieval tests. |
| tests/api/test_ossl_x509_name.h | Registers name-entry and sorting tests. |
| tests/api/test_ossl_x509_ext.h | Registers subject-key-identifier decoding coverage. |
| tests/api/test_ossl_x509_crypto.h | Registers signature and revoked-serial tests. |
| tests/api/test_ossl_sk.h | Registers stack operations and chain-reference tests. |
| tests/api/test_ossl_rsa.h | Registers OAEP padding tests. |
| tests/api/test_ossl_p7p12.h | Registers PKCS#12 attribute tests. |
| tests/api/test_ossl_obj.c | Tests comparisons between distinct and equivalent OIDs. |
| tests/api/test_ossl_ec.h | Registers curve-name and allocation-failure tests. |
| tests/api/test_ossl_dsa.h | Registers DSA size, digest, key, and reference tests. |
| tests/api/test_ossl_asn1.h | Registers ASN1 integer/string compatibility tests. |
| tests/api/test_evp_pkey.h | Registers key-type, ownership, and encoding tests. |
| tests/api/test_evp_cipher.h | Registers cipher cleanup coverage. |
| tests/api/test_evp_cipher.c | Checks that cleanup clears cipher state. |
| tests/api/test_certman.h | Registers CA key-size coverage. |
| tests/api/test_certman.c | Tests RSA and ECC minimum CA key sizes. |
| src/ssl_sk.c | Implements pointer deletion and comparator-based sorting. |
| src/ssl_err.c | Implements error-library allocation and registration stubs. |
| src/pk_ec.c | Clears private keys and fixes decoding/allocation handling. |
| src/internal.c | Updates verification state, cleanup, and CRL calls. |
| doc/dox_comments/header_files/ssl.h | Documents store behavior and memory-function retrieval. |
| doc/dox_comments/header_files/ecc.h | Documents ECDSA signature allocation semantics. |
| configure.ac | Enables ECDSA signature allocation with opensslall. |
| cmake/options.h.in | Exports the ECDSA signature allocation option. |
| certs/intermediate/trust_anchor/wrongca-root-cert.pem | Adds a root for wrong-issuer tests. |
| certs/intermediate/trust_anchor/wrongca-old-int-cert.pem | Adds an expired wrong-issuer intermediate fixture. |
| certs/intermediate/trust_anchor/wrongca-int-leaf-cert.pem | Adds a leaf for wrong-issuer chain tests. |
| certs/intermediate/trust_anchor/wrongca-int-cert.pem | Adds an intermediate for issuer-selection tests. |
| certs/intermediate/trust_anchor/wrongca-cert.pem | Adds a competing CA fixture. |
| certs/intermediate/trust_anchor/v1-root-leaf-cert.pem | Adds a leaf issued by a version-1 root. |
| certs/intermediate/trust_anchor/v1-root-cert.pem | Adds a version-1 trust-anchor fixture. |
| certs/intermediate/trust_anchor/self-key.pem | Adds a self-signed fixture’s test key. |
| certs/intermediate/trust_anchor/self-copy-cert.pem | Adds a same-key self-signed certificate variant. |
| certs/intermediate/trust_anchor/self-cert.pem | Adds a self-signed trust-anchor fixture. |
| certs/intermediate/trust_anchor/root-key.pem | Adds a trust-anchor test key. |
| certs/intermediate/trust_anchor/root-cert.pem | Adds the base trust-anchor certificate. |
| certs/intermediate/trust_anchor/renewed-root-leaf-cert.pem | Adds a leaf for renewed-root selection tests. |
| certs/intermediate/trust_anchor/renewed-root-cert.pem | Adds a renewed-root fixture. |
| certs/intermediate/trust_anchor/notca-twin-cert.pem | Adds a CA counterpart for non-CA selection tests. |
| certs/intermediate/trust_anchor/notca-root-leaf-cert.pem | Adds a leaf beneath a non-CA root. |
| certs/intermediate/trust_anchor/notca-root-cert.pem | Adds a non-CA trust-anchor fixture. |
| certs/intermediate/trust_anchor/nokid-root-cert.pem | Adds a root without key identifiers. |
| certs/intermediate/trust_anchor/nobc-root-leaf-cert.pem | Adds a leaf for missing-basic-constraints tests. |
| certs/intermediate/trust_anchor/nobc-root-cert.pem | Adds a root without basic constraints. |
| certs/intermediate/trust_anchor/noaki-root-link-cert.pem | Adds a same-name link certificate. |
| certs/intermediate/trust_anchor/noaki-root-leaf-cert.pem | Adds a leaf for authority-key-identifier tests. |
| certs/intermediate/trust_anchor/noaki-root-cert.pem | Adds a root without an authority key identifier. |
| certs/intermediate/trust_anchor/link-noaki-cert.pem | Adds a link without an authority key identifier. |
| certs/intermediate/trust_anchor/link-key.pem | Adds the link certificate’s test key. |
| certs/intermediate/trust_anchor/link-cert.pem | Adds a same-name intermediate link. |
| certs/intermediate/trust_anchor/leaf-key.pem | Adds the base leaf’s test key. |
| certs/intermediate/trust_anchor/leaf-cert.pem | Adds the base chain’s leaf certificate. |
| certs/intermediate/trust_anchor/int-crl.pem | Adds an intermediate-issued CRL fixture. |
| certs/intermediate/trust_anchor/forge-sub-leaf-cert.pem | Adds a leaf for forged-chain rejection tests. |
| certs/intermediate/trust_anchor/forge-sub-cert.pem | Adds a subordinate CA for forged-chain tests. |
| certs/intermediate/trust_anchor/forge-root-cert.pem | Adds the forged-chain test root. |
| certs/intermediate/trust_anchor/forge-q-cert.pem | Adds another CA in the forged-chain fixtures. |
| certs/intermediate/trust_anchor/forge-int-cert.pem | Adds an intermediate with a controlled key identifier. |
| certs/intermediate/trust_anchor/forge-fake-int-leaf-cert.pem | Adds a leaf beneath the fake intermediate. |
| certs/intermediate/trust_anchor/forge-fake-int-cert.pem | Adds a fake intermediate fixture. |
| certs/intermediate/trust_anchor/forge-bad-sub-cert.pem | Adds a subordinate with a mismatched issuer identifier. |
| certs/intermediate/trust_anchor/expired-root-old-link-cert.pem | Adds an expired same-name link. |
| certs/intermediate/trust_anchor/expired-root-nokid-cert.pem | Adds a same-name root without key identifiers. |
| certs/intermediate/trust_anchor/expired-root-link-leaf-cert.pem | Adds a leaf beneath an expired-root link. |
| certs/intermediate/trust_anchor/expired-root-link-cert.pem | Adds a valid link to an expired root. |
| certs/intermediate/trust_anchor/expired-root-leaf-cert.pem | Adds a leaf issued by an expired root. |
| certs/intermediate/trust_anchor/expired-root-int-leaf-cert.pem | Adds a leaf for an expired-root intermediate chain. |
| certs/intermediate/trust_anchor/expired-root-int-cert.pem | Adds an intermediate issued by an expired root. |
| certs/intermediate/trust_anchor/expired-root-cert.pem | Adds an expired trust-anchor fixture. |
| certs/intermediate/trust_anchor/expired-int-cert.pem | Adds an expired intermediate fixture. |
| certs/intermediate/trust_anchor/ec-name-root-cert.pem | Adds a same-name EC root candidate. |
| certs/intermediate/trust_anchor/dated-root-late-cert.pem | Adds a later-validity root variant. |
| certs/intermediate/trust_anchor/dated-root-empty-crl.pem | Adds an empty root-issued CRL. |
| certs/intermediate/trust_anchor/dated-root-crl.pem | Adds a root-issued CRL with a revoked entry. |
| certs/intermediate/trust_anchor/dated-root-cert.pem | Adds a root with fixed validity dates. |
| certs/intermediate/trust_anchor/dated-int-leaf-cert.pem | Adds a leaf with fixed validity dates. |
| certs/intermediate/trust_anchor/dated-int-cert.pem | Adds an intermediate with fixed validity dates. |
| certs/intermediate/trust_anchor/cross-root-late-cert.pem | Adds a later-validity cross-signed root. |
| certs/intermediate/trust_anchor/cross-root-cert.pem | Adds a cross-signed root fixture. |
| certs/intermediate/trust_anchor/badkid-int-leaf-cert.pem | Adds a leaf for key-identifier mismatch tests. |
| certs/intermediate/trust_anchor/badkid-int-cert.pem | Adds an intermediate with a mismatched key identifier. |
| certs/intermediate/trust_anchor/alt-root-cert.pem | Adds an alternate-chain root. |
| certs/intermediate/trust_anchor/alt-old-int-cert.pem | Adds an expired alternate intermediate. |
| certs/intermediate/trust_anchor/alt-notca-int-cert.pem | Adds a non-CA alternate intermediate. |
| certs/intermediate/trust_anchor/alt-int-leaf-cert.pem | Adds a leaf for alternate-chain selection. |
| certs/intermediate/trust_anchor/alt-int-cert.pem | Adds an alternate-chain intermediate. |
| certs/intermediate/trust_anchor/alt-end-int-cert.pem | Adds an intermediate on a dead-end chain. |
| certs/intermediate/trust_anchor/alt-end-cert.pem | Adds a dead-end issuer fixture. |
| certs/intermediate/include.am | Includes trust-anchor fixtures in source distributions. |
| certs/check_dates.sh | Allows intentionally expired test certificates. |
| certs/check_cert_chains.sh | Adds fixture issuer-pair checks. |
| .wolfssl_known_macro_extras | Removes the now-configured allocation macro exception. |
| .github/ci-deps/packages-ubuntu-24.04-full.txt | Adds xmlsec build dependencies. |
Review effort: Balanced
| static int errLibNext = WOLFSSL_ERR_LIB_FIRST_DYNAMIC; | ||
|
|
||
| WOLFSSL_ENTER("wolfSSL_ERR_get_next_error_library"); | ||
| return errLibNext++; |
Comment on lines
+62
to
+65
| #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 |
Member
Author
|
Depends on wolfSSL/osp#368 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Make wolfSSL's OpenSSL compatibility layer behave like OpenSSL 3.0 for the APIs xmlsec relies on, and rework
X509_verify_cert()to build and check chains from X509 objects as OpenSSL does, failing closed where OpenSSL would try another candidate.Chain verification
X509_verify_cert()builds the chain from X509 objects, picking issuers as OpenSSL'sbuild_chain()does. It reports each error at its cert and depth to the verify callback. It covers CA checks, path lengths, CA key sizes, host and IP, keyUsage, signatures, dates, name constraints and revocation.X509_CRL_verifyis implemented, andCheckCertCRLFromCm()takes an issuer and a check time.X509_VERIFY_PARAM_set_depthandX509_STORE_CTX_set0_param. Thedepthandownedmembers ofX509_STORE_CTXare removed, which is an API change.SSL_get0_verified_chain()returns the chainX509_verify_cert()builds, and caches it per peer cert.CheckCAKeySize()compares modulus and curve size, so ECC minimums are now enforced. P-192 CAs no longer load with the default minimum.X509_STORE_set_default_pathsandX509_LOOKUP_add_dirdefault handling,X509_STORE_CTX_get_by_subject, a workingX509_OBJECT_free_contents, andX509_STORE_add_certaccepting expired self-signed certs for a store of its own.WOLFSSL_SIGNER_DER_CERTis now set in everyOPENSSL_EXTRAbuild.Crypto and API changes
DSA_sizeandDSA_up_ref, handles any size of q, rejects r/s longer than q, and leaves the private key NULL for public-only keys.EVP_PKEY_signandEVP_PKEY_verifyuse DER signatures for DSA, so raw signatures no longer verify throughEVP_PKEY_verify.RSA_padding_add/check_PKCS1_OAEP_mgf1,ERR_load_strings,ERR_get_next_error_library,CRYPTO_get_mem_functions,EVP_PKEY_RSA2,EVP_PKEY_RSA_PSS, the prime curve short names andB_ASN1_OCTET_STRING.i2d_PUBKEYfor EC keys, EC PKCS#8 loading, andi2d_ECDSA_SIGallocation, which is now enabled with--enable-opensslall.ASN1, X509 and stacks
ASN1_INTEGERhandling:dup,get,cmp,d2i, and magnitude-basedASN1_STRING_length/get0_data/typedispatch.d2i/PEM read functions with a non-NULL*x.X509_chain_up_refnow references certs instead of copying them.sk_X509_CRL_freefrees only the stack.OBJ_cmp,X509_NAME_oneline,X509_NAME_hashand the subjectKeyIdentifier copy inX509V3_EXT_d2i.X509_NAME_ENTRY_dup, stack sorting,sk_X509_insert,sk_X509_delete_ptr,X509_STORE_CTX_set/get_app_data, and PKCS#12X509_alias_get0/X509_keyid_get0.Tests and CI
certs/intermediate/trust_anchor/with generation scripts, and table-driven tests for chain verification, including an out-of-memory test.--enable-alland compares failures with a known DSA list.X509_get0_pubkeycache in favour of upstream's.