diff --git a/certs/intermediate/include.am b/certs/intermediate/include.am index 4773ef8b50..e58cbb2de9 100644 --- a/certs/intermediate/include.am +++ b/certs/intermediate/include.am @@ -50,4 +50,18 @@ EXTRA_DIST += \ certs/intermediate/ca_false_intermediate/test_int_not_cacert.pem \ certs/intermediate/ca_false_intermediate/test_sign_bynoca_srv.pem \ certs/intermediate/ca_false_intermediate/wolfssl_base.conf \ - certs/intermediate/ca_false_intermediate/wolfssl_srv.conf + certs/intermediate/ca_false_intermediate/wolfssl_srv.conf \ + certs/intermediate/untrusted_anchor/gen_certs.sh \ + certs/intermediate/untrusted_anchor/root-ca-cert.pem \ + certs/intermediate/untrusted_anchor/root-ca-key.pem \ + certs/intermediate/untrusted_anchor/alt-ca-cert.pem \ + certs/intermediate/untrusted_anchor/alt-ca-key.pem \ + certs/intermediate/untrusted_anchor/int-ca-cert.pem \ + certs/intermediate/untrusted_anchor/int-ca-key.pem \ + certs/intermediate/untrusted_anchor/int-ca-tampered-cert.pem \ + certs/intermediate/untrusted_anchor/int-ca2-cert.pem \ + certs/intermediate/untrusted_anchor/int-ca2-key.pem \ + certs/intermediate/untrusted_anchor/leaf-cert.pem \ + certs/intermediate/untrusted_anchor/leaf-key.pem \ + certs/intermediate/untrusted_anchor/leaf-deep-cert.pem \ + certs/intermediate/untrusted_anchor/leaf-deep-key.pem diff --git a/certs/intermediate/untrusted_anchor/alt-ca-cert.pem b/certs/intermediate/untrusted_anchor/alt-ca-cert.pem new file mode 100644 index 0000000000..c8eec80bee --- /dev/null +++ b/certs/intermediate/untrusted_anchor/alt-ca-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIJAJoLyjuEz+RhMA0GCSqGSIb3DQEBCwUAMDExLzAtBgNV +BAMMJndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IEFsdCBSb290MCAXDTI2 +MDYwMzA3Mzk0OVoYDzIwNTYwNjAyMDczOTQ5WjAxMS8wLQYDVQQDDCZ3b2xmU1NM +IFVudHJ1c3RlZC1BbmNob3IgVGVzdCBBbHQgUm9vdDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKnZcJxZ45wE5i7AiCdA6KoNZNvFi/4JqWsi+oVNZqiH +H3D7uN4xSJNgaRu41syWD+qiVTwtwGKNTn5h6oF1PiZYAO0/qUJiGD7XX3RJl2qX +xINixqeJkasTzndr1aIb9fGXtu7A1Kq8lohc8KABv6N+EIpOlfo2mo+L4RoP5ejJ +g6iCGtTJrAGXLDCfJtutN1ufVCnOvx1sci4UXdscUT/NryqKEMimkF9SyqlL4W9l +SwnS3P619PXKeY8W6O6a2TDVGBr3kKOkoeXyEJH4vkqYLnS5XJk6GNeudFMEbkKD +rbUTrpisEwy8QDpxwRSFDqDwyyFTOdyFnO/EybIzI0sCAwEAAaMjMCEwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBAHO6 +Ye/rbzYKhAdY0q96M3JtIjIowsg0t7WlabOmf2qu3LijhPutJl7m3d+AAOau9lmz +uvn8vS8awGcIUwe3C+9JMiGqmxbCrhe+UseMfKt+lOJ7qrXdyohhSjHgonfr8mxb +G5UaoSJpNQPf8p667SiUcY6UP5WS9oSjSr01anRqi0WDSwjLPce3v7gv9ZjxNKS5 +ssWfj8mOSIjMEl2BKc49k3H+52pzzh63Wd7mD2aaUYplG/RkelyaunnIaM2wAR6i +03TQk7G9RFnppp5UAeswOaS9GEnD3euufIaT55T1o5hJvI/BCzWuBGe+Edn7OXDo +64g1iCxA+aeoQhRmXUQ= +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/alt-ca-key.pem b/certs/intermediate/untrusted_anchor/alt-ca-key.pem new file mode 100644 index 0000000000..658dd742fd --- /dev/null +++ b/certs/intermediate/untrusted_anchor/alt-ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqdlwnFnjnATmLsCIJ0Doqg1k28WL/gmpayL6hU1mqIcfcPu4 +3jFIk2BpG7jWzJYP6qJVPC3AYo1OfmHqgXU+JlgA7T+pQmIYPtdfdEmXapfEg2LG +p4mRqxPOd2vVohv18Ze27sDUqryWiFzwoAG/o34Qik6V+jaaj4vhGg/l6MmDqIIa +1MmsAZcsMJ8m2603W59UKc6/HWxyLhRd2xxRP82vKooQyKaQX1LKqUvhb2VLCdLc +/rX09cp5jxbo7prZMNUYGveQo6Sh5fIQkfi+SpgudLlcmToY1650UwRuQoOttROu +mKwTDLxAOnHBFIUOoPDLIVM53IWc78TJsjMjSwIDAQABAoIBAQCb9e+zgc7Aarc6 +YswizzVVQOtF6oV7hT+uAvZrBQGo6jpyspG0ZSixOywIqpeCUKDY4KrHkXNAi2Ry +JFMDALdK2jAvqe8v54c/3N/nldOVqzplMoQbPvUlVBCYE8qdCnOxnY/6d9JP3M+U +81J4emKQK6fgd/y7Pvx5pwXRuptwPmqlR2RQERANdP1nFPgjGQhxWBJwulUE13Dp +VtbqJIJ8mpWLWBEk6t4ga4wEV4XboWeo7Bs7oPU1TF5AHZnRVGjMSvpLm4CQHnhX +ccQ/xDWnpkGvIVs1kBRFt6kHepjhaPkUrJJZwjmGOFJXEZdDvJVB5B74jOVDjdyu +ZK3vQ4NBAoGBANcwleVaSd1qxdyUAyye2t3c93d4VhnRbfVo2xV4HUaey4H66ZHA +iVzodDSLXLoj4ecJFRW7IDPjZ/3dFxTlwgBvFzhXgkpIpbJmIUF3mx6ElRWPCtiT +U5kZsupSZqL8vmNejWI7+B0KsHy38NIqUlPoXJUSQNtzAwbC1k8iDvORAoGBAMoP +lnl+mCO+kk4N8apyysMnt8O+fm6pUUFSL9ig9GL/bUAOqxHX2Py57oDQKeapn9g/ +YP85DRf47idpfnaG006xaDh5kx7YcXpgks/UAqZIY/b4Mwt5+rTXjLfOHSrK+Vc/ +CzLnjXcDqcPepYZwNbP2rHSf4Ut/KrsmjZ8JmMMbAoGABd4aSD21A+eUa5ZRm9bd +Cu2qhcRvPJb8U5O/XY9/5NwRmoK3+bRxSmpAOOqP5bdywnT58TTABQovXLm5lmVJ +a++bh3rDX7kpY3rrbziOrz9YPVVAK3Wg8uzDdyY2DD2uB1Gds08FTe1rsIrncyOa +SRVt6NatlA5Hx9hqNZAtLjECgYAxTxaAdZU4+9OGOr7jwnmaoEGnAgCmjqkmkKDe +c4DP+9c0T6ANjagFHHaIdsQS5wf75JOOFOUOGZA8i/DxibtdM8vkJD7zwwwGOjT5 +hJpU68uBRFZokY7NvOA5JpJVlAy+7sKT3I/YIEu4YcfxA8cHMMYq+60mGFVcMG9V +BSmDSwKBgAqhhym6R1RgquV5n6kqy1Y1UFwZhrb2tvn04rDg9FVTsAXdw7Bs276D +daeDdUDTeMOfiAk+VYtmriG46TEexmrQmGDHomVVZsRov0Q/76hyR+KZc+8TEJYm +gcauogYNIeeu+713QbfWq8LxlB1b/E8YQEVfAT+Tl+mG+Sg6WQXN +-----END RSA PRIVATE KEY----- diff --git a/certs/intermediate/untrusted_anchor/gen_certs.sh b/certs/intermediate/untrusted_anchor/gen_certs.sh new file mode 100755 index 0000000000..431d831c90 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/gen_certs.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +# Regenerate the certificate set used by test_X509_verify_cert_untrusted_inter +# (tests/api/test_ossl_x509_str.c). +# +# The set lets the test verify an end-entity certificate together with +# caller-supplied untrusted intermediates through the OpenSSL compatibility +# path (X509_STORE_CTX_init "chain" argument + X509_verify_cert) and check that +# such a chain is accepted only when it terminates at a trusted anchor. +# +# Two trust chains are produced (both end-entities use the same hostname so a +# downstream hostname check is meaningful): +# +# single intermediate : leaf <- int-ca <- root-ca +# two intermediates : leaf-deep <- int-ca2 <- int-ca <- root-ca +# +# Plus: +# alt-ca an unrelated self-signed root (a populated but wrong +# trust anchor) +# int-ca-tampered int-ca with the final byte of its signatureValue +# flipped (valid TBSCertificate, broken outer signature) +# +# The certificates intentionally omit subjectKeyIdentifier / +# authorityKeyIdentifier; the test relies on this, so the script aborts at the +# end if they were added back. OpenSSL 3.x adds them automatically with no +# option to suppress them, so regenerate with a tool that does not (e.g. +# OPENSSL=/usr/bin/openssl on macOS). +# +# RSA-2048 / SHA-256, ~30 year validity so the regression does not expire. +# +# Requires: openssl (or a compatible LibreSSL, see above) and python3 (used to +# flip a signature byte for the tampered intermediate). With set -e a missing +# python3 aborts regeneration partway through, leaving a half-written fixture +# set; install python3 (or replace the byte-flip step) before regenerating. + +set -e +cd "$(dirname "$0")" + +OPENSSL="${OPENSSL:-openssl}" +DAYS=10957 +RSA_BITS=2048 + +CA_EXT=$(mktemp) +LEAF_EXT=$(mktemp) +trap 'rm -f "$CA_EXT" "$LEAF_EXT" *.csr *.srl' EXIT + +# No pathlen so the first intermediate can still issue the second one in the +# two-intermediate positive control; no key identifiers (see header). +cat > "$CA_EXT" <<'EOF' +basicConstraints = critical, CA:TRUE +keyUsage = critical, digitalSignature, keyCertSign, cRLSign +EOF + +cat > "$LEAF_EXT" <<'EOF' +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth +subjectAltName = DNS:www.example.test +EOF + +genkey() { "$OPENSSL" genrsa -out "$1" "$RSA_BITS" 2>/dev/null; } + +# Self-sign a root CA: genroot +genroot() { + "$OPENSSL" req -x509 -new -key "$1" -sha256 -subj "/CN=$3" -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" -CAcreateserial \ + -sha256 -days "$DAYS" -extfile "$5" -out "$2" 2>/dev/null +} + +# Roots ---------------------------------------------------------------------- +genkey root-ca-key.pem +genroot root-ca-key.pem root-ca-cert.pem "wolfSSL Untrusted-Anchor Test Root" + +genkey alt-ca-key.pem +genroot alt-ca-key.pem alt-ca-cert.pem "wolfSSL Untrusted-Anchor Test Alt Root" + +# Intermediate signed by the root -------------------------------------------- +genkey int-ca-key.pem +"$OPENSSL" req -new -key int-ca-key.pem -sha256 \ + -subj "/CN=wolfSSL Untrusted-Anchor Test Intermediate" -out int-ca.csr +signcert int-ca.csr int-ca-cert.pem root-ca-cert.pem root-ca-key.pem "$CA_EXT" + +# Leaf signed by the intermediate (single-intermediate chain) ---------------- +genkey leaf-key.pem +"$OPENSSL" req -new -key leaf-key.pem -sha256 \ + -subj "/CN=www.example.test" -out leaf.csr +signcert leaf.csr leaf-cert.pem int-ca-cert.pem int-ca-key.pem "$LEAF_EXT" + +# Second-level intermediate signed by the first intermediate ----------------- +genkey int-ca2-key.pem +"$OPENSSL" req -new -key int-ca2-key.pem -sha256 \ + -subj "/CN=wolfSSL Untrusted-Anchor Test Intermediate 2" -out int-ca2.csr +signcert int-ca2.csr int-ca2-cert.pem int-ca-cert.pem int-ca-key.pem "$CA_EXT" + +# Leaf signed by the second-level intermediate (two-intermediate chain) ------ +genkey leaf-deep-key.pem +"$OPENSSL" req -new -key leaf-deep-key.pem -sha256 \ + -subj "/CN=www.example.test" -out leaf-deep.csr +signcert leaf-deep.csr leaf-deep-cert.pem int-ca2-cert.pem int-ca2-key.pem \ + "$LEAF_EXT" + +# Tampered intermediate: flip the final byte of the DER (last byte of the +# signatureValue) so the TBSCertificate stays valid but the outer signature no +# longer verifies. +"$OPENSSL" x509 -in int-ca-cert.pem -outform DER -out int-ca.der +python3 - <<'PY' +d = open("int-ca.der", "rb").read() +d = d[:-1] + bytes([d[-1] ^ 0x01]) +open("int-ca-tampered.der", "wb").write(d) +PY +"$OPENSSL" x509 -inform DER -in int-ca-tampered.der -out int-ca-tampered-cert.pem +rm -f int-ca.der int-ca-tampered.der + +# Guard: these test certificates must not carry key identifiers (see header). +for c in root-ca-cert.pem alt-ca-cert.pem int-ca-cert.pem int-ca2-cert.pem \ + leaf-cert.pem leaf-deep-cert.pem; do + if "$OPENSSL" x509 -in "$c" -noout -text \ + | grep -q "Key Identifier"; then + echo "ERROR: $c carries a subject/authority key identifier." >&2 + echo "Use an OpenSSL/LibreSSL that does not auto-add them" >&2 + echo "(e.g. OPENSSL=/usr/bin/openssl $0)." >&2 + exit 1 + fi +done + +echo "Completed" diff --git a/certs/intermediate/untrusted_anchor/int-ca-cert.pem b/certs/intermediate/untrusted_anchor/int-ca-cert.pem new file mode 100644 index 0000000000..1ec87cda16 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/int-ca-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIJAPTQ79oIkv6aMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNV +BAMMIndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IFJvb3QwIBcNMjYwNjAz +MDczOTQ5WhgPMjA1NjA2MDIwNzM5NDlaMDUxMzAxBgNVBAMMKndvbGZTU0wgVW50 +cnVzdGVkLUFuY2hvciBUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANUAGLPvY/JtFj7JCaHHUafA7rCnvf/t143a8tFrrn79 +1QF9+4BBZlS3xLNEFVZl8LB90kHdT2ws6lX0W6Q98+xebix9L0uAIevoAn1dPXAV +/aY6i8au5azybGRL86g4yoTSt4Jg+hRJzdirsoq/sBYTvQTVOfeZNimb9RCdEq66 +MyRtnTREYVWwS8AzxeanTnKC2Yw/m7CB5oPQ2TKNSOhQVdzqcEb3jyxL5JLPi47y +AEAPvcqF/HONwDSvtHchzgCowFQOfCpicrJSRZKop1XDjdgN3+Bv9dYQscVGV9Mh +TA7fduxMAIIcYSsiw4LSRijuenP5IY4C5t6FMLq/9Y0CAwEAAaMjMCEwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBADlM +TxZoEdjtXQpju84oBxscHyhNJU2OqN93B1tgTUi3B9j2ciHrRKV89/OiPSMgxGZ9 +DmijQgvKV7SEB22eXJDb0l4sEspPQQcuxB1m0bLDPh7xpYxvXTPxI/HuAxBp95kt +ke1t3/3NOhWIP44Evmmo1W5NZBdjPGJQSXHKS6HyENVu2vrTj3TmLuYg57nldxAg +kAF2D14InFJJwIGKeNaP710bN2tUkN7npWvNl7vu1TrDgeWdF67uVu1f647IST5i +9O1f4s4yvhLh5gDKuM3IEDt+3XP1qLegSKIwWrlxHcjhs5XO+52aLulSVkT6RR5G +tL6izMt6DXTOy/LUBkM= +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/int-ca-key.pem b/certs/intermediate/untrusted_anchor/int-ca-key.pem new file mode 100644 index 0000000000..d7efa8aed1 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/int-ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1QAYs+9j8m0WPskJocdRp8DusKe9/+3Xjdry0Wuufv3VAX37 +gEFmVLfEs0QVVmXwsH3SQd1PbCzqVfRbpD3z7F5uLH0vS4Ah6+gCfV09cBX9pjqL +xq7lrPJsZEvzqDjKhNK3gmD6FEnN2Kuyir+wFhO9BNU595k2KZv1EJ0SrrozJG2d +NERhVbBLwDPF5qdOcoLZjD+bsIHmg9DZMo1I6FBV3OpwRvePLEvkks+LjvIAQA+9 +yoX8c43ANK+0dyHOAKjAVA58KmJyslJFkqinVcON2A3f4G/11hCxxUZX0yFMDt92 +7EwAghxhKyLDgtJGKO56c/khjgLm3oUwur/1jQIDAQABAoIBAAKw6Z78W0rozesl +JxYAKqvv6BQbSm89VgfYyFCVB7NbCaHnMZJBQUW4vKd3KL3as9vG+y0R2rsHJj7H +w5Cjp71IxCOTwVE24TbVy5JB51DPNlEvVCzCcOxqc6wguYdakFR1RRREnWQ8OnmO +Uccm/NaKkUzKVN0n9mM4MTRwh5flhG6K9YoDxk75g12+fD+DQKf61iqcUh9Hqy3y +ZBFdgKzRowq0UNK8OmqkATkHIBkOOZRWXz7t1+TOq+DlNizkjmUQXQM5qQ4Vrf01 +jjt+PfnjPQcVYgvanXSFZbinjgTa4+E5QQTBkSyFaal0GbbRKX7bxAu7aHHCMd5P +PqkSUYECgYEA+Di097OJZLNQDgx7RNUGSPFiPhuLEg6wVhmBAtz1DM6UKYD2el7N +AQ1kmpHC0rGbzeQoJQ/fcuxXQ7w7S0ycvNNTYW+kcUrK0h0L64JnUC6UVVrB+ax+ +3CLfM+qVo1Z1gFr+teiadCS1AHXUlVzz4BkkVTBOZosEo6k+s4Ol9GkCgYEA26zW +TWUd0vkASkyAPG6JeMGX56tMbU+kSC+zY2UvuEp9JNVZhGwrb411jbREBrjA+eok +UxIe3M4mMmbNvdlJ7M+6I6g5uVQ8FmohbEo78HcyB4onr2et6bw0giqupEhyrfx+ +XDgSpu1Mu1bFKYknRKn9MIzNTdHyAr404A38w4UCgYEAynElVuf8ZD7CSdLwLkE2 +8QK9Rz4bfEyykGYYjAc9bIaG3Bqr6z2qIPOVW2MJ6+Ci25b7Ds8VRJtwyHOaQF1p +b69Cz7LIAQYoyJicAiXGsORsYfi1PzXp+QwP0j2+cQqwplCQcDgW0Can4Io5KOA4 +nkqjET9mkcdLr1b3Jl12WhECgYEAp6jzauCI8bNP0GUw3m6zB3IiIRPxYeCODvYx +IORilnJrrwgSqWnxgNNjbAKwhLzPtC5LCQfkfDvulTs3PfWwYUht1bcYT2WF8smP +ttm1g6NFkNGV1l74MlONc+dloUcWF8qFGpdFTRgCH11rX3cpfFONRVfBfeqFnihT +rMmgKA0CgYEA0a4G8rqeUBL/nU4ALLdnvhx4Qus3cJ7kCF+7ZBi+CCrFQun1kTTF +23uOeAIabjAvm8ur8k73FzD9bg5EhlOsmWcAOSSWgq1ONr51sS+8IrmAx/95AvtP +ztWQJywOMcA3mjIlJDuA+tkI9gQObtxnNOVy/OUk8aNE755UXLFG53k= +-----END RSA PRIVATE KEY----- diff --git a/certs/intermediate/untrusted_anchor/int-ca-tampered-cert.pem b/certs/intermediate/untrusted_anchor/int-ca-tampered-cert.pem new file mode 100644 index 0000000000..3f9eb4c894 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/int-ca-tampered-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCjCCAfKgAwIBAgIJAPTQ79oIkv6aMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNV +BAMMIndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IFJvb3QwIBcNMjYwNjAz +MDczOTQ5WhgPMjA1NjA2MDIwNzM5NDlaMDUxMzAxBgNVBAMMKndvbGZTU0wgVW50 +cnVzdGVkLUFuY2hvciBUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANUAGLPvY/JtFj7JCaHHUafA7rCnvf/t143a8tFrrn79 +1QF9+4BBZlS3xLNEFVZl8LB90kHdT2ws6lX0W6Q98+xebix9L0uAIevoAn1dPXAV +/aY6i8au5azybGRL86g4yoTSt4Jg+hRJzdirsoq/sBYTvQTVOfeZNimb9RCdEq66 +MyRtnTREYVWwS8AzxeanTnKC2Yw/m7CB5oPQ2TKNSOhQVdzqcEb3jyxL5JLPi47y +AEAPvcqF/HONwDSvtHchzgCowFQOfCpicrJSRZKop1XDjdgN3+Bv9dYQscVGV9Mh +TA7fduxMAIIcYSsiw4LSRijuenP5IY4C5t6FMLq/9Y0CAwEAAaMjMCEwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBADlM +TxZoEdjtXQpju84oBxscHyhNJU2OqN93B1tgTUi3B9j2ciHrRKV89/OiPSMgxGZ9 +DmijQgvKV7SEB22eXJDb0l4sEspPQQcuxB1m0bLDPh7xpYxvXTPxI/HuAxBp95kt +ke1t3/3NOhWIP44Evmmo1W5NZBdjPGJQSXHKS6HyENVu2vrTj3TmLuYg57nldxAg +kAF2D14InFJJwIGKeNaP710bN2tUkN7npWvNl7vu1TrDgeWdF67uVu1f647IST5i +9O1f4s4yvhLh5gDKuM3IEDt+3XP1qLegSKIwWrlxHcjhs5XO+52aLulSVkT6RR5G +tL6izMt6DXTOy/LUBkI= +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/int-ca2-cert.pem b/certs/intermediate/untrusted_anchor/int-ca2-cert.pem new file mode 100644 index 0000000000..7df37d56b7 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/int-ca2-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFDCCAfygAwIBAgIJAIpSt1mJH/iOMA0GCSqGSIb3DQEBCwUAMDUxMzAxBgNV +BAMMKndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IEludGVybWVkaWF0ZTAg +Fw0yNjA2MDMwNzM5NTBaGA8yMDU2MDYwMjA3Mzk1MFowNzE1MDMGA1UEAwwsd29s +ZlNTTCBVbnRydXN0ZWQtQW5jaG9yIFRlc3QgSW50ZXJtZWRpYXRlIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoVXfEVO7xHX3hjTbTdwD3YM7sOX3e +BQvZQkmOxHYH4nr+3D8d0U9LNQPL+nQ07G9D71g6mW7fVScvoeyNIptknoS/MBKl +g9ZfptEYbKJsggaSxw+CLVsprTtOXd2vq7OXNXJqlmuEEjiOn0lQxUF2JiS+cYqY +N1+iNE8zYB0GXpQPFoSGudgl9lRFZTKtqR5O9Vs50I1r2JQfRybVN61W6QUdZMtv +nfdcD0AcQxPnkdQ03h1X57DtIDKh2TjBXbR69wViMtDKhvAWRmgvH0AZFTQrUBIJ +QAUiTYLyzOnedl+bY7Tl+h5EIUH7l5qu4O2HzxArQVz7O/Ad7uYLSxZrAgMBAAGj +IzAhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQC1MBh4c9iTDKBHzR5IqDSASBtc8yBA/jxaX6aXYlNkgNv2Nnpz3uan +mbi4uQuUycI63RfUPPNS28x7j90rVGPxHlM6vYRHarzvgKlGIlJhHAFmzNlALbSM +yM94m9iXDELQV5f494PbyGsBcnI1qJQZXdwpRiukICTOr0E8AlVULxJAGks9L0tZ +pJ3im3ay8yZPFVwhpDBXU2oeqNNsFQro8mZEzlYcyBlvf8uZbXy/7RHsRDr2CFyz +ECZnGZUJ3GdFo8T0IfW7ztLP6kBS0qTXOKEbVoGo7vystiu6dw9VAXukIGFSI+Md +tvdsR8KWuQeoWo/CfAI6MIyghQBGaCNg +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/int-ca2-key.pem b/certs/intermediate/untrusted_anchor/int-ca2-key.pem new file mode 100644 index 0000000000..9e7b371b8a --- /dev/null +++ b/certs/intermediate/untrusted_anchor/int-ca2-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAqFV3xFTu8R194Y0203cA92DO7Dl93gUL2UJJjsR2B+J6/tw/ +HdFPSzUDy/p0NOxvQ+9YOplu31UnL6HsjSKbZJ6EvzASpYPWX6bRGGyibIIGkscP +gi1bKa07Tl3dr6uzlzVyapZrhBI4jp9JUMVBdiYkvnGKmDdfojRPM2AdBl6UDxaE +hrnYJfZURWUyrakeTvVbOdCNa9iUH0cm1TetVukFHWTLb533XA9AHEMT55HUNN4d +V+ew7SAyodk4wV20evcFYjLQyobwFkZoLx9AGRU0K1ASCUAFIk2C8szp3nZfm2O0 +5foeRCFB+5earuDth88QK0Fc+zvwHe7mC0sWawIDAQABAoIBAFuFnIhyZTdTAY4Q +aS6wFSZqzBZDa9u6gqatE7E7v7CpwpWuyeI8WxBY0qeklGnx4szc5Ot3YICsm5Ga +SDK0Dii2xxXr3TeAZp265RSSe/zi9Q/4isYMQvR16zjAcDeC8zHTLVImVm6IOZfR +otr3ZJAITRH+SYxZDvXx2t3j8+Pxxlp6hzG3sXw8MWa2Ra3mIhDjzCBIaRXq25Mj +8DUTi/8p5OupEd7mt6HZmaWk71KC6R4efNNBtl2p2WCiw0VQNZ+cFLPyYeL8+f32 +HrWPqTinzCR8LXkX2GTWdZuC8cJIKX/8olTJZaTKsmD73H2dGj+VsBjdx4SRr6aW +SYp6boECgYEA1aZq8XofRXid7ZIKeDkmhWhMcrx+4QIqKU5YKMYocuFGeeRla0pz +LGunt+bs2V/LuUVDiOvbQ8RuaDB4mmJiYnRag28iRBpoJPm5nGELv7h3vxiG5tKJ +n52HCzvi2CI4dEXTH3cwhIis+Uu26Udhhiri08m6rl50/qE9STYr8WUCgYEAybN/ +iRNHcGSYyrBPfSfAcTEbbDI45C1NgFOjccBnnpBgaqatUSTgJDImCpH/v5efDXqk +YxhCJ7tfbxhREmpWSNS1prvzih7T+xVOoWsdDSKIgF2PcsqeXFikQGh0ffu9UT0m +lJ4tw7P615q9MNGlKrERmQsYyM4GmMyLGj2V048CgYBWzBpMakHEFoGKn7czKny0 +3C+auWuOfDOmvlZgkkiii1T3dkuhsAhkdoQX2XBFy35XkYUjXjahLG9yUqbcibXQ +q9aN6RtxsYy34OCAYIjGZen4L722jrgsqXHQpY6+IgDvc+KWuPR0E5a6XQE9eqtr +N4cZZa464tMDE3xzfteRZQKBgQCQZR0nP5MEBjBP4lp1ibC+F96+3VFXIIt8E+RN +eeV0YX10vHAVSCXiI7iSFqUVPvFRj/wBKQurL/uJJ8paOaAdsZF9lM4rkhhFhqJs +8qawkYlRBCm+jwlBqP+lUGIdEswcTX/CI1813DH2icNpIJxybKLhgk0y7DNSzhPD +LFWHRwKBgQC67X7ZML+st35dOvPRg7djtcCQTr0lcywKLUUo2B3VZVZL7pObe/V3 +y+CK8HEvw9wTbsQ7VnqM8v/KfQmk8gHlOpppYg0LGeTDjz97TGlJGVE+w3zHkL3h +QNEYZpApBmMiThPPsTb+5L7xJkuEmFVsUrVaJcr2cIXKCEnnKzDYag== +-----END RSA PRIVATE KEY----- diff --git a/certs/intermediate/untrusted_anchor/leaf-cert.pem b/certs/intermediate/untrusted_anchor/leaf-cert.pem new file mode 100644 index 0000000000..2503d2c720 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/leaf-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDJzCCAg+gAwIBAgIJAIpSt1mJH/iNMA0GCSqGSIb3DQEBCwUAMDUxMzAxBgNV +BAMMKndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IEludGVybWVkaWF0ZTAg +Fw0yNjA2MDMwNzM5NTBaGA8yMDU2MDYwMjA3Mzk1MFowGzEZMBcGA1UEAwwQd3d3 +LmV4YW1wbGUudGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIN +u32XXq4VzV/WKmfgaLZlkPbtYG8Ic9LOYqE0/y/F34wkYLWThYSleLglbFG9Vtcx +FhAPuutZSML8j3QGdvRPHF/w03+BZ4SVnHQiBvJSCyY0TYhgSHZaNuWR7VVhlTsg +XQNfx1ZCYsBiAgaJnhC/O2id6qjS3bnnhme/gvoKMl1SolgJgPalqRfhlHXs68Q+ +P2Mj5TdhqcwxGfBRGuaavTwwYBJYHwD8FvXtfGuoVtZb2gNU64m0ZpVPPUT6yBEy +PJwZnrfZIDW9IrByz0kVRqmlDbMvgOs8Rxh0kvuuAtdun02AyOyzIuupHSEMoJmi +oxqj026GgqEYnDCuZD8CAwEAAaNSMFAwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8E +BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGwYDVR0RBBQwEoIQd3d3LmV4YW1w +bGUudGVzdDANBgkqhkiG9w0BAQsFAAOCAQEAZLly5fb9QVkI6rb0MuzlGLr44Umq +wI9bNzbd/EEt1vIymTpagf24PmvTtltQthhRWS5TK1P/mdfZEIhoX2lLU2k5xCOR +pnCrgjJy9VSrW/gUfP0hnNR90Ig9OwUdQesJ4/7xcApox0BWD5P4M2HyRYmAkCop +7Gho954vAuDpcNuXwudr1DwPlaK0EFvCOrFjeRhigYCI1EBVHrB6lgpOf1YXoA3d +dlM9D/VOBj3FUta/brU5dDGpFYEhoOX62Z0RdTFIgGuvQm37UsbNEeEjqN7vL5EG +Fyysmns+uGjkoNFufyJ7G53jvA7bG1N6f3+BLnFgYJk79Y0BGu2WrKtn/Q== +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/leaf-deep-cert.pem b/certs/intermediate/untrusted_anchor/leaf-deep-cert.pem new file mode 100644 index 0000000000..e08758707d --- /dev/null +++ b/certs/intermediate/untrusted_anchor/leaf-deep-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKTCCAhGgAwIBAgIJAPO6YaSaV9OBMA0GCSqGSIb3DQEBCwUAMDcxNTAzBgNV +BAMMLHdvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IEludGVybWVkaWF0ZSAy +MCAXDTI2MDYwMzA3Mzk1MFoYDzIwNTYwNjAyMDczOTUwWjAbMRkwFwYDVQQDDBB3 +d3cuZXhhbXBsZS50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +3hHicb9tF7nQG1+cJ0S1mUaYAgl7tAxkrFKes1MFmVfkSyOf1neOmZqRQIY6NG7n +W2gnmR+vM5/mtNwmaKZDOoVXS+EROX4JaPe588HQQVNaHd9gD69gAUjyhmW+vo06 +7DtYOI9IxIVBAQEkkBo3bEhTrkUKJut5bY2YLbjcpcmE39vtNcwfJCJHGFX4d0RG +p2tvvKyGRnMiCQUBOkhkqL2iqEkq/E20WoVwkANaA7ZdnYufc5q6nRau8CWCyX2L ++l9ZYioExTf1KQ/kkb3oa2jNIF3murjIMhYQVGOwfXSgaQWD8imKAAETG7jAwKmr +7wEcmXy63X0IZKIjX/y+MwIDAQABo1IwUDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB +/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAbBgNVHREEFDASghB3d3cuZXhh +bXBsZS50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQARUfcAhA6W05FNevlk529AaMOa +AEq24kOmFgYwYFn5+raia6fyDM1C5g4bT5eWsrOHN3PhchOiIZJ0s1Nq/vgdOBX9 +SK7grD4MKD1q9gD7DBdMM576gy64gDvBftJIxgYlDAU2PUU/oAjACCMLsb2mePhd +HTopLABBWM0NMSiRXay+cizxxlbl/fafPDC1fPRq2TU8z4JCS6HCPR8ukjSCp+S5 +sEJmc3VV3kk3qd2tHcWXai8JZPxhtCaBrVRmk9lcp6Er/Jf9LCkLzhWZWcBaQRoQ +s03H1GJHwbcDzVZrZzI05y3KZO5ZWl+d1f0RvIkHp/bsvz0d6qRCLe1O/WNj +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/leaf-deep-key.pem b/certs/intermediate/untrusted_anchor/leaf-deep-key.pem new file mode 100644 index 0000000000..f4cb0d5b6c --- /dev/null +++ b/certs/intermediate/untrusted_anchor/leaf-deep-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA3hHicb9tF7nQG1+cJ0S1mUaYAgl7tAxkrFKes1MFmVfkSyOf +1neOmZqRQIY6NG7nW2gnmR+vM5/mtNwmaKZDOoVXS+EROX4JaPe588HQQVNaHd9g +D69gAUjyhmW+vo067DtYOI9IxIVBAQEkkBo3bEhTrkUKJut5bY2YLbjcpcmE39vt +NcwfJCJHGFX4d0RGp2tvvKyGRnMiCQUBOkhkqL2iqEkq/E20WoVwkANaA7ZdnYuf +c5q6nRau8CWCyX2L+l9ZYioExTf1KQ/kkb3oa2jNIF3murjIMhYQVGOwfXSgaQWD +8imKAAETG7jAwKmr7wEcmXy63X0IZKIjX/y+MwIDAQABAoIBAQClxvwgpiKuf4wX +ozxTJDvc/JIlkybBti7BZbwLaoLXgoFuhl2gIQhXKsgcPxfcZJ5Z4lsNOFX2V/xG +L8KMhPHTEg+lGZNeM/SaY7Rxf9ITskGn40U28FjfHLHQGsxWO+As0fB96JkN+9wW +/99no+qT7zpo8ikt/MNdQULFfydYqr9NB4RfLRnG7w5wqXBttkKOiB4Nu3/I/dlv +6d9giFSyAKNfDNmYBitjOiz5MR7Ivx2EKt/iz+3mqfASx8ur8Kx9uNpT599X1x5p +pynnRkfoPpYU4xZeKZvKpDaMCstawyRx5d9iUvLR2iOF+aEYTYPy99CMpCtEznkl +NWK1ImcBAoGBAPb6+5oa+4fYOMhE7kqIiUUepX6YYHpHQzU3+ZzeGCj19Jp3JHD6 +neD2N7x9Hp1B6gY0dUX5Img+5MxtPMLtMsiqLQRjkbD0HgRoO1s5AdXwFIAZrAcx +VARsXTJ240Z0Ra6MkvnYZd56/lAIz0cwhzcruT5ip9GzoDgPcg4rR77TAoGBAOYu +A2ro00PeIK6HF+MO8wOvmyg4kg0ag4eGaQR8dvNfHpFEVeOHKU8artyDwqRM7whB +YVfLjjC4HOCU/mZM9W1m+5lD/SHbfamAhhIX3szP5WY6qLBKSdviBSGC0vIuHM1f +u1mFJWi59bG40knvqxatk75K++I6/tYh1Q6OySchAoGBAMZAu7x9UlE+OH5SDrHX +ndDVA+V17WPaVlGe6AiKovwmSr2/S4pBMoBFRIJSMUPsiC2I6GZN0Ne7PK/4M+EI +xE4dhFtUjbtsibfh71uPjDCuMdaORO/VIesXDUyX8GI6rUCq6MQTd9o6AnA+UhyE +ENYxi9ZPHQUQ2liF1XkYNtQvAoGBAKXkacAmdwTF1aYTZSrW/lwctuVhCBn7juog +/BUoow458qzdpE0sf6AsafQx3hlN/URk4oRFB4CjYOSXXVZbhtLHUvOeJZS+PS3p +nDb0DzZrazindCZCFEMt/Waug9vZUhbONReKt3Bn/eSNgLmayyK4DPAr2KeuvzWT +ApvQWrchAoGAXpKPOEWOcHFR7AtXsURty7z/nJlM8+/oJe0c/E3vclzrVQU7YXh3 +KIxvWh0o6CMGqkB/+xm/nkhAW3uEJv0oG6lMJbMWTVBJ3ygedQDoHqjGFePE11Ni +cjM8qD4ybBuecU82HB8USVXzUF6pqfYNyWnvqD5oQREoNkIvWCt31aI= +-----END RSA PRIVATE KEY----- diff --git a/certs/intermediate/untrusted_anchor/leaf-key.pem b/certs/intermediate/untrusted_anchor/leaf-key.pem new file mode 100644 index 0000000000..2e3ef8ea7a --- /dev/null +++ b/certs/intermediate/untrusted_anchor/leaf-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAwg27fZderhXNX9YqZ+BotmWQ9u1gbwhz0s5ioTT/L8XfjCRg +tZOFhKV4uCVsUb1W1zEWEA+661lIwvyPdAZ29E8cX/DTf4FnhJWcdCIG8lILJjRN +iGBIdlo25ZHtVWGVOyBdA1/HVkJiwGICBomeEL87aJ3qqNLdueeGZ7+C+goyXVKi +WAmA9qWpF+GUdezrxD4/YyPlN2GpzDEZ8FEa5pq9PDBgElgfAPwW9e18a6hW1lva +A1TribRmlU89RPrIETI8nBmet9kgNb0isHLPSRVGqaUNsy+A6zxHGHSS+64C126f +TYDI7LMi66kdIQygmaKjGqPTboaCoRicMK5kPwIDAQABAoIBAQCMllZXTusRREpe +PFLMnxA91KeJvcA3sO/4sf1SvYqDmd+zMEfARPheeWNURgiLz5iynqVVTZAmIbFN +Upy7elOD/CyadWdE4QOWUq3elShjNn1NWGczfk4BMKrE90vyp5fuFixM3X8VR1Mz +/c1p3xndGC56RK5VUS/CF7KQR3cknipiRXdVYVZrDfGHd1b5Vzzfs2B0EwP/mo5f +h8bPCGh28thOSoxyJTNVFhgFEOYJl9ipicaoTCEcoHq5nR/i3Nqg1I5o84yYYNCZ +6Xn2H+y1e8GmdzGd03W2vGNcix9gis5/YfeTvdp6mHbLQKN0+LXnEx+FGu3mL1r5 +3p0SpnGBAoGBAPMdVGSoiSI8TfvQxveUNDIhkFVMRtZqMSuOpUqUoOe3M6BQ9oqt +En64KH+YR2GBTmkoEr5nk8F30JpjsRHyXF/iER8dvV5td1/w4vJ8FsuLveWm2Nyo +d/chumLAN7SVe6dfnF+IkvgwXBDWihAobA37p/y00aBCMu2sMuINWeW5AoGBAMxW +ubxPvY5/I6HyjoWsB4pdywdsYSIymupdsN76pCAJg2mw78IAzdtSkS47ORbC2hxC +DKILtxtFVw4XWZ8NGruICGdPKUewPC0UyjU10NllY4EXMgfHW4Oxg3oDtBHhomGf +2Nqs9dkO4fRX2kJMMsgEoeNNOLaNm70blHfrqxW3AoGAb7EQ3bHkVts3xLIVRxdK +p6Ft6xJBFS7yah196PbBudMcH9IygmGjNp6q1HwEh1Jd0Mf9XIa/hknih5u3dRRQ +xihZT19dae2Gw4gq69aAroED/GccCLxJaTuQot/Gd+uZRLsTX3yicO3ezkmSYnv/ +sKjmc54rFKJ1PWY4dkxF+dkCgYBKFbN3m+2dCWmQ4NFdk5aUSxc+VMQO0wwppthm +r7bryqcznav/yazZNOFgQqabIwBTOHs+EUNNBuHeQQcETIsBrPtnAWN1E2dt7Ni3 +XBChkUn3VsKT4WrDn4uMpGUYCpeGD59fAVCNZwDzRxrh6KCMtmk/cRL71PG/KY21 +wOMhlwKBgG8peBo9LQ/qwoXUerY1g1lXfr5AssL2ADWB9v36tTx90UbXe++fLAyH +6Hbv3qIPxDPsnI/I3BNCF6DTJnblWgpbN9ueofadczocswaEQqHzNsh720Y4MWvr +afrgpbE5nRtiQjJ6UMKG+lmjCmQwqJVpiYfXnT8v0VThBL9GlF2V +-----END RSA PRIVATE KEY----- diff --git a/certs/intermediate/untrusted_anchor/root-ca-cert.pem b/certs/intermediate/untrusted_anchor/root-ca-cert.pem new file mode 100644 index 0000000000..47374c4b64 --- /dev/null +++ b/certs/intermediate/untrusted_anchor/root-ca-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIJAOr8+UHaQN8hMA0GCSqGSIb3DQEBCwUAMC0xKzApBgNV +BAMMIndvbGZTU0wgVW50cnVzdGVkLUFuY2hvciBUZXN0IFJvb3QwIBcNMjYwNjAz +MDczOTQ5WhgPMjA1NjA2MDIwNzM5NDlaMC0xKzApBgNVBAMMIndvbGZTU0wgVW50 +cnVzdGVkLUFuY2hvciBUZXN0IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDBvzveobCp4fO1nzmCC//WnKqT335MKM+6MuMViX9kIPUodEXcLuqA +rU/NrhLme+BRYr8g6wz4zKZ+ozqIgOnlCgJVBukoVLNbF2koDeMKrBBiLVDhhuEn +GGLk24fbRBRtPzSe/bQcZnv7jhqXXcacPxNLGsIVZLOhyTRIUZO/71O/0cLJt7ii +UtimYi2gwq0hh3TgVJgwWIGV1A2Yo9PIvXzhKvkLJ+iL0l+3L4PpMUSOK+cwlMK5 +vgzg+c1KHpcHj1Q0otJqnCNwcKKUj2Kk/KTHkaMa9+gG3zJVZ0uH/S/Y88iemdvk +HTxBD3/wkCdThfUrpbvzTb4gaQsya63dAgMBAAGjIzAhMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQClsQKzHBisTsxz ++uqmzSfnNMkyTJ5O+WbyD8eHnBiE5Tc5ixN5o5FmgvJEI6Q0ZwyDRSlMV57VMRDN +2QytxEM9N2m/DuKPga9ZtFxcucPTamgS/hIhVFQ71GTeFHgZmD8R+FZn7y9X7Wdk +E+Z+9gqo3VbEKm4GTz+11O2DI5G73N7GyHvr6+PDxf578nMphZgbM8+BlNmzvLat +NUzZM7azlCNEcNEcYShnYxXVIEfD3w7tmlG6GhUX1LkK332wUWATAp6PJ1q/tJML +fkKbjH6EXLZGiLC4ZoPX2IxCZvUpu78o5+39FmQ4QPFW+gCLXg54WLGdi1xVGc78 +hEYkrip8 +-----END CERTIFICATE----- diff --git a/certs/intermediate/untrusted_anchor/root-ca-key.pem b/certs/intermediate/untrusted_anchor/root-ca-key.pem new file mode 100644 index 0000000000..dab8a8b68e --- /dev/null +++ b/certs/intermediate/untrusted_anchor/root-ca-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAwb873qGwqeHztZ85ggv/1pyqk99+TCjPujLjFYl/ZCD1KHRF +3C7qgK1Pza4S5nvgUWK/IOsM+MymfqM6iIDp5QoCVQbpKFSzWxdpKA3jCqwQYi1Q +4YbhJxhi5NuH20QUbT80nv20HGZ7+44al13GnD8TSxrCFWSzock0SFGTv+9Tv9HC +ybe4olLYpmItoMKtIYd04FSYMFiBldQNmKPTyL184Sr5Cyfoi9Jfty+D6TFEjivn +MJTCub4M4PnNSh6XB49UNKLSapwjcHCilI9ipPykx5GjGvfoBt8yVWdLh/0v2PPI +npnb5B08QQ9/8JAnU4X1K6W7802+IGkLMmut3QIDAQABAoIBAQCR7EZbV8yHJvc3 +Q8U8fW0jZrvSw/GLyvEpfEosXnLkJy5+WZSEUZGteNsyPnldvs8kfQsW7/HFMd30 +Y1wik3WWXAOFpr7U8XZklS1OTadC0nVEfHz+X2gU2fkiBXY9XvlDjudDGDIfcdDP +lifQ+YAtDhSp7G1hT4c7wx7tmQN++qJmsnxRbSwYsxS94ndzJ88UUSpKyLzYJ4fg +GRZ2Jh5A+OWVZBiI9nGvZdxxpf+uZg54DKkibXsWCzxalx4Tq51OLd67pg7MDgdY +xRk5aKGRas4Bi7R8AunBs2MWvgteQUdCTssQX+euaAwRZ0zZilR0ocWsbPXI+Vbc +WbIoLSrBAoGBAOn36sd/+CzwIUmEv19MVa0gq4otUS5eg3Ksg8ZItjzPfNU0HJB5 +NBRKCRCVZH2ZrTmhoBIrEHLhFBXsnEFzMaq1YzDe+vlqxxx//2swOa7sZmFiUa29 +LXGSIxZMOIiyPfwXScBT5+7MvGgQvQRGAyjbtaugbXJDJeZ/as9fWkibAoGBANP9 +u3u5ZJwt117p6uPLAtqudNwyykg1DmYWCTY4yzZYeqAiLUnhyV65UxTBr+HxDWT4 +MGEu5ADUO6s+nc/+tOe0x77gdcFWpeAmuySlTNlXw/1LZFs6fC480dN0+HrTW5hP +8n1NC2wQE8RfhYayz84YRNLjf86YQuU1XMIUdh7nAoGBALqSpVlDdf832gOLZSQ0 +dhnh9IjyjEsiuUWnxklHUHN7rftCXAjlbh9Dzqi3yPPTqWxMs53pU2uYivDUxuH2 +X0PW82tUVOSyPmc+tsqeIVGZWCcORT37npJzS6GLVIXFRWKSm5BQGKK4BwIhXula +f0iEtAFRpBU68K03LFCDpDZDAoGBALN+CPpvNPLQFaU1pj6Nq7MBN80h7AhLdA+X +5ODWIam4LMvdZD9EP75GaEQQ4x0Jfu/Y/Q2sKD1Tddo+wFxWK1JszRue7dVvyi8K +XDZhB7qXB0k2Rpi/4lk8AeVrCuDkiI9kUcsqKtuqmTISNvqf+DdmcQ+mtJZ2cV2l +Ww6xSHpNAoGAT83Fpen+X02RQK4k40yd5/8PfbPk2jbX3tPrsZ3SbghnUpw6165n +OaqqszOWGxn4FJ3NuCQvM/NimZxdnO2awpP6PqIlvduMqAENMwrfTKDHxhgB/nFS +hRpgml2QPaKDeGBpdYkiUpoCwNs8fTaKhNHOGuAn6xhCsibIJv4GXFg= +-----END RSA PRIVATE KEY----- diff --git a/src/x509_str.c b/src/x509_str.c index abdb42b4e2..9d1d1f1107 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -531,6 +531,36 @@ static int X509StoreMoveCert(WOLFSSL_STACK *certs_stack, return WOLFSSL_FAILURE; } +/* Remove the first node referencing `cert` (by pointer identity) from `stack`. + * The certificate object itself is not freed - the stack only holds a borrowed + * reference. Returns WOLFSSL_SUCCESS if a node was removed, WOLFSSL_FAILURE if + * `cert` was not present, or WOLFSSL_FATAL_ERROR if `stack`/`cert` is NULL. + * The only caller performs best-effort cleanup and intentionally ignores the + * return value. + * + * Walks the linked list once (O(n)) rather than indexing with + * wolfSSL_sk_X509_value() per position (which would re-walk from the head each + * time, O(n^2)). */ +static int X509StoreRemoveCert(WOLFSSL_STACK *stack, WOLFSSL_X509 *cert) { + WOLFSSL_STACK* node; + int idx; + int num; + + if (stack == NULL || cert == NULL) + return WOLFSSL_FATAL_ERROR; + + num = wolfSSL_sk_X509_num(stack); + for (node = stack, idx = 0; idx < num && node != NULL; + node = node->next, idx++) { + if (node->data.x509 == cert) { + (void)wolfSSL_sk_pop_node(stack, idx); + return WOLFSSL_SUCCESS; + } + } + + return WOLFSSL_FAILURE; +} + /* Current certificate failed, but it is possible there is an * alternative cert with the same subject key which will work. @@ -608,7 +638,6 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) int done = 0; int added = 0; int i = 0; - int numInterAdd = 0; int numFailedCerts = 0; int depth = 0; int origDepth = 0; @@ -666,8 +695,9 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } } /* Add the intermediates provided on init to the list of untrusted - * intermediates to be used */ - ret = addAllButSelfSigned(certs, ctx->ctxIntermediates, &numInterAdd); + * intermediates to be used. They are removed again from `certs` in the + * exit cleanup (by identity, recomputed from ctxIntermediates). */ + ret = addAllButSelfSigned(certs, ctx->ctxIntermediates, NULL); } if (ret != WOLFSSL_SUCCESS) { goto exit; @@ -677,10 +707,19 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) wolfSSL_sk_X509_free(ctx->chain); } ctx->chain = wolfSSL_sk_X509_new_null(); + if (ctx->chain == NULL) { + ret = WOLFSSL_FAILURE; + goto exit; + } failedCerts = wolfSSL_sk_X509_new_null(); - if (!failedCerts) + 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; goto exit; + } if (ctx->depth > 0) { depth = ctx->depth + 1; @@ -751,7 +790,24 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) } 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 */ + * 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. Leave `added` set: they are still + * loaded, so the exit cleanup makes a final attempt to drop + * them. */ + ret = WOLFSSL_FATAL_ERROR; + goto exit; + } + added = 0; ret = X509StoreVerifyCert(ctx); if (ret != WOLFSSL_SUCCESS) { /* WOLFSSL_PARTIAL_CHAIN may only terminate the chain at a @@ -762,7 +818,8 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) * chains that never reach a trust anchor. Verify that * ctx->current_cert is itself in the original trust set. */ if (((ctx->flags & WOLFSSL_PARTIAL_CHAIN) || - (ctx->store->param->flags & WOLFSSL_PARTIAL_CHAIN)) && + (ctx->store->param != NULL && + (ctx->store->param->flags & WOLFSSL_PARTIAL_CHAIN))) && X509StoreCertIsTrusted(ctx->store, ctx->current_cert, origTrustedSk)) { wolfSSL_sk_X509_push(ctx->chain, ctx->current_cert); @@ -809,6 +866,18 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx) depth--; } + /* Success requires the path to have reached a configured trust anchor + * (done == 1) or to have terminated at a caller-trusted self-signed + * certificate via the break above (done == 0 with depth still > 0). A + * loop that instead ran out of its depth budget (depth <= 0) without + * completing must fail closed: ret may still be WOLFSSL_SUCCESS from the + * last link, but no trust anchor was reached. */ + if (ret == WOLFSSL_SUCCESS && done == 0 && depth <= 0) { + SetupStoreCtxError_ex(ctx, WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG, + wolfSSL_sk_X509_num(ctx->chain)); + ret = WOLFSSL_FAILURE; + } + exit: /* Copy back failed certs. */ numFailedCerts = wolfSSL_sk_X509_num(failedCerts); @@ -818,10 +887,30 @@ exit: } wolfSSL_sk_X509_pop_free(failedCerts, NULL); - /* Remove additional intermediates from init from the store */ - if (ctx != NULL && numInterAdd > 0) { - for (i = 0; i < numInterAdd; i++) { - wolfSSL_sk_X509_pop(ctx->store->certs); + /* Remove the caller-supplied intermediates that addAllButSelfSigned + * appended to `certs` during chain building, restoring it to its original + * contents. Remove them by pointer identity from the same stack they were + * added to (store->certs in the common case, or the caller's setTrustedSk + * via X509_STORE_CTX_set0_trusted_stack), recomputed from ctxIntermediates + * with the same self-signed filter as the add. + * + * Identity removal - not a saved count + positional pop - is required: + * X509VerifyCertSetupRetry reorders `certs` during chain building, so + * popping N entries off the top could drop a legitimate trusted entry and + * leave an injected intermediate behind, which a later verification reusing + * this store/ctx would then snapshot as a trust anchor. certsToUse is the + * throwaway certs==NULL path and is freed wholesale below, so skip it. */ + if (ctx != NULL && certsToUse == NULL && certs != NULL && + ctx->ctxIntermediates != NULL) { + int n = wolfSSL_sk_X509_num(ctx->ctxIntermediates); + for (i = 0; i < n; i++) { + WOLFSSL_X509* inter = + wolfSSL_sk_X509_value(ctx->ctxIntermediates, i); + if (inter != NULL && + wolfSSL_X509_NAME_cmp(&inter->issuer, &inter->subject) + != 0) { + X509StoreRemoveCert(certs, inter); + } } } /* Remove intermediates that were added to CM */ @@ -1027,7 +1116,7 @@ int wolfSSL_X509_STORE_CTX_set_ex_data_with_cleanup( } #endif /* HAVE_EX_DATA_CLEANUP_HOOKS */ -#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_ALL) +#if defined(WOLFSSL_APACHE_HTTPD) || defined(OPENSSL_EXTRA) void wolfSSL_X509_STORE_CTX_set_depth(WOLFSSL_X509_STORE_CTX* ctx, int depth) { WOLFSSL_ENTER("wolfSSL_X509_STORE_CTX_set_depth"); diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index 96218daafa..2bb871255a 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -1026,6 +1026,487 @@ int test_wolfSSL_X509_STORE_CTX_ex(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +/* Regression: an untrusted intermediate supplied to wolfSSL_X509_verify_cert() + * via the X509_STORE_CTX_init "chain" argument must never be treated as a + * trust anchor. Verification may only succeed when the chain terminates at a + * certificate the caller actually trusts. + * + * leaf (CN=www.example.test) <- int-ca (CA) <- root-ca (self-signed) + * + * The attack sub-cases (empty trust store, a populated-but-wrong trust anchor, + * and a tampered intermediate signature) must all be rejected; the single- and + * two-intermediate chains that genuinely reach the trusted root must still + * verify. Certificates live in certs/intermediate/untrusted_anchor/. + */ +static X509* untrusted_inter_load(const char* file) +{ + return X509_load_certificate_file(file, SSL_FILETYPE_PEM); +} + +/* Positive control: leaf <- int-ca <- root with the genuine self-signed root + * trusted. Must verify - guards against a test that merely rejects + * everything. */ +static int test_untrusted_inter_sanity(X509* leaf, X509* inter, X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + 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); + /* Chain reaches the trusted root -> must verify. */ + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Positive control with two untrusted intermediates: + * leaf-deep <- int-ca2 <- int-ca <- root (root trusted). + * A legitimate multi-level chain that genuinely reaches the trusted root must + * still verify; guards against over-rejecting deeper chains. */ +static int test_untrusted_inter_two_level(X509* leafDeep, X509* inter, + X509* inter2, X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, inter), 0); + ExpectIntGT(sk_X509_push(untrusted, inter2), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leafDeep, untrusted), 1); + /* Two-level chain reaches the trusted root -> must verify. */ + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Empty trust store. The untrusted intermediate must not anchor the path - + * with nothing trusted, verification must fail. */ +static int test_untrusted_inter_empty_store(X509* leaf, X509* inter) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + /* Intentionally empty: no trusted certificates added. */ + 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); + /* No trust anchor exists -> rejected with "issuer not found". */ + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Populated but wrong trust store. An unrelated self-signed root is trusted; + * it did not issue the intermediate, so verification must fail. */ +static int test_untrusted_inter_wrong_anchor(X509* leaf, X509* inter, + X509* wrongRoot) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, wrongRoot), 1); + 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); + /* Trusted root did not issue the intermediate -> "issuer not found". */ + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Tampered intermediate signature. The real root is trusted but the + * intermediate's outer signature is corrupt, so it is not authentically + * signed - verification must fail. */ +static int test_untrusted_inter_tampered(X509* leaf, X509* tamperedInter, + X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, tamperedInter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, untrusted), 1); + /* Intermediate is not authentically signed -> rejected. Assert only the + * rejection (error != X509_V_OK), not a specific code: wolfSSL currently + * surfaces the corrupt intermediate as "no valid issuer" rather than a + * signature-failure error, and pinning that quirk would turn a future + * error-reporting improvement into a spurious test failure. */ + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntNE(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Reuse one store across a rejected verification followed immediately by a + * good one. The rejected attempt temporarily loads an intermediate into the + * store's CertManager and pushes the caller intermediates onto its working + * list; a failed check must clean both up so the store is left in a pristine + * state. Verifying a genuine chain on the same store right afterwards proves + * nothing stale was left behind. */ +static int test_untrusted_inter_reused_store(X509* leaf, X509* inter, + X509* tamperedInter, X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* badChain = NULL; + STACK_OF(X509)* goodChain = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + + /* First: a chain that must be rejected (tampered intermediate signature). + * This is the path that loads the intermediate into the store. */ + ExpectNotNull(badChain = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(badChain, tamperedInter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, badChain), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntNE(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Then: reuse the SAME store for the genuine chain. If the failed check + * left stale state behind (a temporarily-loaded intermediate or + * unrestored working list), this would misbehave. */ + ExpectNotNull(goodChain = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(goodChain, inter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, goodChain), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(badChain); + sk_X509_free(goodChain); + return EXPECT_RESULT(); +} + +/* A verification that loads an intermediate must not leave it behind to anchor + * a later chain. Verify a genuine chain on the store (which temporarily loads + * the intermediate), then reuse the SAME store to verify the leaf alone with + * NO intermediate supplied: the leaf's issuer is not in the trusted store, so + * it must be rejected. If the first check left the intermediate loaded, this + * second one would wrongly succeed. */ +static int test_untrusted_inter_no_stale_anchor(X509* leaf, X509* inter, + X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* chain = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + + /* First: genuine chain verifies (temporarily loads the intermediate). */ + ExpectNotNull(chain = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(chain, inter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, chain), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Then: reuse the SAME store, leaf alone, NO intermediate supplied. The + * leaf's issuer is not trusted, so this must be rejected. A stale + * intermediate left behind by the first check would wrongly anchor it. */ + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, NULL), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntNE(X509_STORE_CTX_get_error(ctx), X509_V_OK); + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(chain); + return EXPECT_RESULT(); +} + +/* Depth exhaustion: a chain that cannot be walked to the trusted anchor within + * the configured path-building depth budget must be rejected,. + * + * leaf-deep <- int-ca2 <- int-ca <- root (root trusted) + * + * The chain is genuine and verifies at the default depth (covered by + * test_untrusted_inter_two_level); here the depth is capped below the chain + * length so the budget is consumed before the trusted root is reached. The + * fix must report it as "certificate chain too long". */ +static int test_untrusted_inter_depth_exhaustion(X509* leafDeep, X509* inter, + X509* inter2, X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectIntEQ(X509_STORE_add_cert(store, root), 1); + ExpectNotNull(untrusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(untrusted, inter), 0); + ExpectIntGT(sk_X509_push(untrusted, inter2), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leafDeep, untrusted), 1); + /* Cap the path-building budget below the chain length so the walk runs + * out of depth before it can reach the trusted root. */ + 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); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + return EXPECT_RESULT(); +} + +/* Intermediate-stack cleanup: the caller-supplied intermediates that the + * verifier temporarily appends to its working cert list must be removed from + * the exact stack they were added to once verification finishes. When a + * trusted_stack is in use (X509_STORE_CTX_set0_trusted_stack), they are + * appended to that caller-owned stack; if they are not removed again, a later + * verification reusing the stack/ctx would snapshot them as trust anchors. + * + * leaf <- int-ca <- root, with root supplied via the trusted_stack. + * + * Verify the chain (which reaches root in the trusted stack), then assert the + * trusted stack is left exactly as the caller supplied it: only root, with the + * injected intermediate removed again. */ +static int test_untrusted_inter_trusted_stack_cleanup(X509* leaf, X509* inter, + X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* trusted = NULL; + STACK_OF(X509)* untrusted = NULL; + + ExpectNotNull(store = X509_STORE_new()); + ExpectNotNull(trusted = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(trusted, root), 0); + 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); + /* Chain reaches root in the trusted stack -> verifies. */ + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + /* The trusted stack must be restored: the injected intermediate appended + * during verification must have been removed, leaving only root. */ + ExpectIntEQ(sk_X509_num(trusted), 1); + ExpectPtrEq(sk_X509_value(trusted, 0), root); + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(untrusted); + sk_X509_free(trusted); + return EXPECT_RESULT(); +} + +/* 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. */ +static int untrusted_inter_retry_one(X509_STORE* store, X509* leaf, + X509* first, X509* second) +{ + EXPECT_DECLS; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* mixed = NULL; + + ExpectNotNull(mixed = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(mixed, first), 0); + 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); + 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 + * 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. */ +static int test_untrusted_inter_retry(X509* leaf, X509* inter, + X509* tamperedInter, X509* root) +{ + EXPECT_DECLS; + X509_STORE* store = NULL; + X509_STORE_CTX* ctx = NULL; + STACK_OF(X509)* goodOnly = NULL; + STACK_OF(X509)* badOnly = NULL; + + 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); + + /* 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. */ + ExpectNotNull(badOnly = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(badOnly, tamperedInter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, badOnly), 1); + ExpectIntEQ(X509_verify_cert(ctx), 0); + ExpectIntNE(X509_STORE_CTX_get_error(ctx), X509_V_OK); + X509_STORE_CTX_free(ctx); + ctx = NULL; + + /* Reuse the SAME store with the genuine intermediate: must still succeed, + * proving the retry left no stale state that breaks later verifications. */ + ExpectNotNull(goodOnly = sk_X509_new_null()); + ExpectIntGT(sk_X509_push(goodOnly, inter), 0); + ExpectNotNull(ctx = X509_STORE_CTX_new()); + ExpectIntEQ(X509_STORE_CTX_init(ctx, store, leaf, goodOnly), 1); + ExpectIntEQ(X509_verify_cert(ctx), 1); + ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK); + + X509_STORE_CTX_free(ctx); + X509_STORE_free(store); + sk_X509_free(goodOnly); + sk_X509_free(badOnly); + return EXPECT_RESULT(); +} +#endif /* OPENSSL_EXTRA && !NO_RSA && !NO_CERTS && !NO_FILESYSTEM */ + +int test_X509_verify_cert_untrusted_inter(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +#define UA_CERT_DIR "./certs/intermediate/untrusted_anchor/" + X509* leaf = NULL; + X509* leafDeep = NULL; + X509* inter = NULL; + X509* inter2 = NULL; + X509* tamperedInter = NULL; + X509* root = NULL; + X509* wrongRoot = NULL; + int sanityRes = 0; + int twoLevelRes = 0; + int emptyStoreRes = 0; + int wrongAnchorRes = 0; + int tamperedRes = 0; + int reusedStoreRes = 0; + int noStaleRes = 0; + int depthExhaustRes = 0; + int trustedStackCleanupRes = 0; + int retryRes = 0; + + ExpectNotNull(leaf = untrusted_inter_load(UA_CERT_DIR "leaf-cert.pem")); + ExpectNotNull(leafDeep = + untrusted_inter_load(UA_CERT_DIR "leaf-deep-cert.pem")); + ExpectNotNull(inter = untrusted_inter_load(UA_CERT_DIR "int-ca-cert.pem")); + ExpectNotNull(inter2 = + untrusted_inter_load(UA_CERT_DIR "int-ca2-cert.pem")); + ExpectNotNull(tamperedInter = + untrusted_inter_load(UA_CERT_DIR "int-ca-tampered-cert.pem")); + ExpectNotNull(root = untrusted_inter_load(UA_CERT_DIR "root-ca-cert.pem")); + ExpectNotNull(wrongRoot = + untrusted_inter_load(UA_CERT_DIR "alt-ca-cert.pem")); + + /* Run every sub-case unconditionally - each reports its own result - so a + * regression in one does not mask the others. */ + if (leaf != NULL && leafDeep != NULL && inter != NULL && inter2 != NULL && + tamperedInter != NULL && root != NULL && wrongRoot != NULL) { + sanityRes = test_untrusted_inter_sanity(leaf, inter, root); + twoLevelRes = test_untrusted_inter_two_level(leafDeep, inter, + inter2, root); + emptyStoreRes = test_untrusted_inter_empty_store(leaf, inter); + wrongAnchorRes = test_untrusted_inter_wrong_anchor(leaf, inter, + wrongRoot); + tamperedRes = test_untrusted_inter_tampered(leaf, tamperedInter, + root); + reusedStoreRes = test_untrusted_inter_reused_store(leaf, inter, + tamperedInter, root); + noStaleRes = test_untrusted_inter_no_stale_anchor(leaf, inter, + root); + depthExhaustRes = test_untrusted_inter_depth_exhaustion(leafDeep, + inter, inter2, root); + trustedStackCleanupRes = test_untrusted_inter_trusted_stack_cleanup( + leaf, inter, root); + retryRes = test_untrusted_inter_retry(leaf, inter, tamperedInter, root); + ExpectIntEQ(sanityRes, 1); + ExpectIntEQ(twoLevelRes, 1); + ExpectIntEQ(emptyStoreRes, 1); + ExpectIntEQ(wrongAnchorRes, 1); + ExpectIntEQ(tamperedRes, 1); + ExpectIntEQ(reusedStoreRes, 1); + ExpectIntEQ(noStaleRes, 1); + ExpectIntEQ(depthExhaustRes, 1); + ExpectIntEQ(trustedStackCleanupRes, 1); + ExpectIntEQ(retryRes, 1); + } + + X509_free(leaf); + X509_free(leafDeep); + X509_free(inter); + X509_free(inter2); + X509_free(tamperedInter); + X509_free(root); + X509_free(wrongRoot); +#undef UA_CERT_DIR +#endif /* OPENSSL_EXTRA && !NO_RSA && !NO_CERTS && !NO_FILESYSTEM */ + 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) diff --git a/tests/api/test_ossl_x509_str.h b/tests/api/test_ossl_x509_str.h index a3fe8bfebd..e1024deadd 100644 --- a/tests/api/test_ossl_x509_str.h +++ b/tests/api/test_ossl_x509_str.h @@ -29,6 +29,7 @@ int test_wolfSSL_X509_STORE_check_time(void); int test_wolfSSL_X509_STORE_CTX_get0_store(void); int test_wolfSSL_X509_STORE_CTX(void); int test_wolfSSL_X509_STORE_CTX_ex(void); +int test_X509_verify_cert_untrusted_inter(void); int test_X509_STORE_untrusted(void); int test_X509_STORE_InvalidCa(void); int test_X509_STORE_InvalidCa_NoCallback(void); @@ -51,6 +52,7 @@ int test_wolfSSL_CTX_set_cert_store(void); test_wolfSSL_X509_STORE_CTX_get0_store), \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_CTX), \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_CTX_ex), \ + TEST_DECL_GROUP("ossl_x509_store", test_X509_verify_cert_untrusted_inter), \ 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_NoCallback), \