summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/src/lib/x509
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/src/lib/x509')
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp265
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_alt_name.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/asn1_attribute.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/cert_status.cpp125
-rw-r--r--comm/third_party/botan/src/lib/x509/cert_status.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor.cpp233
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor.h165
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp148
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h77
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp335
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h119
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sql/info.txt3
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp19
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h34
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp70
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h42
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system/info.txt8
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp470
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h81
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt15
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp258
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h70
-rw-r--r--comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt16
-rw-r--r--comm/third_party/botan/src/lib/x509/crl_ent.cpp140
-rw-r--r--comm/third_party/botan/src/lib/x509/crl_ent.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/datastor.cpp205
-rw-r--r--comm/third_party/botan/src/lib/x509/datastor.h85
-rw-r--r--comm/third_party/botan/src/lib/x509/info.txt12
-rw-r--r--comm/third_party/botan/src/lib/x509/key_constraint.cpp106
-rw-r--r--comm/third_party/botan/src/lib/x509/key_constraint.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/name_constraint.cpp273
-rw-r--r--comm/third_party/botan/src/lib/x509/name_constraint.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp.cpp363
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp.h282
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp_types.cpp105
-rw-r--r--comm/third_party/botan/src/lib/x509/ocsp_types.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/pkcs10.cpp304
-rw-r--r--comm/third_party/botan/src/lib/x509/pkcs10.h148
-rw-r--r--comm/third_party/botan/src/lib/x509/pkix_enums.h143
-rw-r--r--comm/third_party/botan/src/lib/x509/pkix_types.h613
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_attribute.cpp58
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ca.cpp338
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ca.h261
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_crl.cpp268
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_crl.h209
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn.cpp428
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn.h11
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp60
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ext.cpp1023
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_ext.h529
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_obj.cpp424
-rw-r--r--comm/third_party/botan/src/lib/x509/x509_obj.h144
-rw-r--r--comm/third_party/botan/src/lib/x509/x509cert.cpp956
-rw-r--r--comm/third_party/botan/src/lib/x509/x509cert.h461
-rw-r--r--comm/third_party/botan/src/lib/x509/x509opt.cpp100
-rw-r--r--comm/third_party/botan/src/lib/x509/x509path.cpp1088
-rw-r--r--comm/third_party/botan/src/lib/x509/x509path.h475
-rw-r--r--comm/third_party/botan/src/lib/x509/x509self.cpp152
-rw-r--r--comm/third_party/botan/src/lib/x509/x509self.h222
61 files changed, 12636 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp b/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp
new file mode 100644
index 0000000000..a347a3114c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_alt_name.cpp
@@ -0,0 +1,265 @@
+/*
+* AlternativeName
+* (C) 1999-2007 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/internal/stl_util.h>
+#include <botan/parsing.h>
+#include <botan/loadstor.h>
+
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create an AlternativeName
+*/
+AlternativeName::AlternativeName(const std::string& email_addr,
+ const std::string& uri,
+ const std::string& dns,
+ const std::string& ip)
+ {
+ add_attribute("RFC822", email_addr);
+ add_attribute("DNS", dns);
+ add_attribute("URI", uri);
+ add_attribute("IP", ip);
+ }
+
+/*
+* Add an attribute to an alternative name
+*/
+void AlternativeName::add_attribute(const std::string& type,
+ const std::string& value)
+ {
+ if(type.empty() || value.empty())
+ return;
+
+ auto range = m_alt_info.equal_range(type);
+ for(auto j = range.first; j != range.second; ++j)
+ if(j->second == value)
+ return;
+
+ multimap_insert(m_alt_info, type, value);
+ }
+
+/*
+* Add an OtherName field
+*/
+void AlternativeName::add_othername(const OID& oid, const std::string& value,
+ ASN1_Tag type)
+ {
+ if(value.empty())
+ return;
+ multimap_insert(m_othernames, oid, ASN1_String(value, type));
+ }
+
+/*
+* Return all of the alternative names
+*/
+std::multimap<std::string, std::string> AlternativeName::contents() const
+ {
+ std::multimap<std::string, std::string> names;
+
+ for(auto i = m_alt_info.begin(); i != m_alt_info.end(); ++i)
+ {
+ multimap_insert(names, i->first, i->second);
+ }
+
+ for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+ {
+ multimap_insert(names, i->first.to_formatted_string(), i->second.value());
+ }
+
+ return names;
+ }
+
+bool AlternativeName::has_field(const std::string& attr) const
+ {
+ auto range = m_alt_info.equal_range(attr);
+ return (range.first != range.second);
+ }
+
+std::string AlternativeName::get_first_attribute(const std::string& attr) const
+ {
+ auto i = m_alt_info.lower_bound(attr);
+ if(i != m_alt_info.end() && i->first == attr)
+ return i->second;
+
+ return "";
+ }
+
+std::vector<std::string> AlternativeName::get_attribute(const std::string& attr) const
+ {
+ std::vector<std::string> results;
+ auto range = m_alt_info.equal_range(attr);
+ for(auto i = range.first; i != range.second; ++i)
+ results.push_back(i->second);
+ return results;
+ }
+
+X509_DN AlternativeName::dn() const
+ {
+ X509_DN dn;
+ auto range = m_alt_info.equal_range("DN");
+
+ for(auto i = range.first; i != range.second; ++i)
+ {
+ std::istringstream strm(i->second);
+ strm >> dn;
+ }
+
+ return dn;
+ }
+
+/*
+* Return if this object has anything useful
+*/
+bool AlternativeName::has_items() const
+ {
+ return (m_alt_info.size() > 0 || m_othernames.size() > 0);
+ }
+
+namespace {
+
+/*
+* DER encode an AlternativeName entry
+*/
+void encode_entries(DER_Encoder& encoder,
+ const std::multimap<std::string, std::string>& attr,
+ const std::string& type, ASN1_Tag tagging)
+ {
+ auto range = attr.equal_range(type);
+
+ for(auto i = range.first; i != range.second; ++i)
+ {
+ if(type == "RFC822" || type == "DNS" || type == "URI")
+ {
+ ASN1_String asn1_string(i->second, IA5_STRING);
+ encoder.add_object(tagging, CONTEXT_SPECIFIC, asn1_string.value());
+ }
+ else if(type == "IP")
+ {
+ const uint32_t ip = string_to_ipv4(i->second);
+ uint8_t ip_buf[4] = { 0 };
+ store_be(ip, ip_buf);
+ encoder.add_object(tagging, CONTEXT_SPECIFIC, ip_buf, 4);
+ }
+ else if (type == "DN")
+ {
+ std::stringstream ss(i->second);
+ X509_DN dn;
+ ss >> dn;
+ encoder.encode(dn);
+ }
+ }
+ }
+
+}
+
+/*
+* DER encode an AlternativeName extension
+*/
+void AlternativeName::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE);
+
+ encode_entries(der, m_alt_info, "RFC822", ASN1_Tag(1));
+ encode_entries(der, m_alt_info, "DNS", ASN1_Tag(2));
+ encode_entries(der, m_alt_info, "DN", ASN1_Tag(4));
+ encode_entries(der, m_alt_info, "URI", ASN1_Tag(6));
+ encode_entries(der, m_alt_info, "IP", ASN1_Tag(7));
+
+ for(auto i = m_othernames.begin(); i != m_othernames.end(); ++i)
+ {
+ der.start_explicit(0)
+ .encode(i->first)
+ .start_explicit(0)
+ .encode(i->second)
+ .end_explicit()
+ .end_explicit();
+ }
+
+ der.end_cons();
+ }
+
+/*
+* Decode a BER encoded AlternativeName
+*/
+void AlternativeName::decode_from(BER_Decoder& source)
+ {
+ BER_Decoder names = source.start_cons(SEQUENCE);
+
+ // FIXME this is largely a duplication of GeneralName::decode_from
+
+ while(names.more_items())
+ {
+ BER_Object obj = names.get_next_object();
+
+ if(obj.is_a(0, CONTEXT_SPECIFIC))
+ {
+ BER_Decoder othername(obj);
+
+ OID oid;
+ othername.decode(oid);
+ if(othername.more_items())
+ {
+ BER_Object othername_value_outer = othername.get_next_object();
+ othername.verify_end();
+
+ if(othername_value_outer.is_a(0, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+ throw Decoding_Error("Invalid tags on otherName value");
+
+ BER_Decoder othername_value_inner(othername_value_outer);
+
+ BER_Object value = othername_value_inner.get_next_object();
+ othername_value_inner.verify_end();
+
+ if(ASN1_String::is_string_type(value.type()) && value.get_class() == UNIVERSAL)
+ {
+ add_othername(oid, ASN1::to_string(value), value.type());
+ }
+ }
+ }
+ if(obj.is_a(1, CONTEXT_SPECIFIC))
+ {
+ add_attribute("RFC822", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(2, CONTEXT_SPECIFIC))
+ {
+ add_attribute("DNS", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(6, CONTEXT_SPECIFIC))
+ {
+ add_attribute("URI", ASN1::to_string(obj));
+ }
+ else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+ {
+ BER_Decoder dec(obj);
+ X509_DN dn;
+ std::stringstream ss;
+
+ dec.decode(dn);
+ ss << dn;
+
+ add_attribute("DN", ss.str());
+ }
+ else if(obj.is_a(7, CONTEXT_SPECIFIC))
+ {
+ if(obj.length() == 4)
+ {
+ const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
+ add_attribute("IP", ipv4_to_string(ip));
+ }
+ }
+
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/asn1_alt_name.h b/comm/third_party/botan/src/lib/x509/asn1_alt_name.h
new file mode 100644
index 0000000000..b0ca1d87e2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_alt_name.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_ALT_NAME_H_
+#define BOTAN_X509_ALT_NAME_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(asn1_alt_name.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/asn1_attribute.h b/comm/third_party/botan/src/lib/x509/asn1_attribute.h
new file mode 100644
index 0000000000..0139c18c70
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/asn1_attribute.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_ASN1_ATTRIBUTE_H_
+#define BOTAN_ASN1_ATTRIBUTE_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(asn1_attribute.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/cert_status.cpp b/comm/third_party/botan/src/lib/x509/cert_status.cpp
new file mode 100644
index 0000000000..e22d6e1be1
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/cert_status.cpp
@@ -0,0 +1,125 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_enums.h>
+
+namespace Botan {
+
+const char* to_string(Certificate_Status_Code code)
+ {
+ switch(code)
+ {
+ case Certificate_Status_Code::VERIFIED:
+ return "Verified";
+ case Certificate_Status_Code::OCSP_RESPONSE_GOOD:
+ return "OCSP response accepted as affirming unrevoked status for certificate";
+ case Certificate_Status_Code::OCSP_SIGNATURE_OK:
+ return "Signature on OCSP response was found valid";
+ case Certificate_Status_Code::VALID_CRL_CHECKED:
+ return "Valid CRL examined";
+
+ case Certificate_Status_Code::CERT_SERIAL_NEGATIVE:
+ return "Certificate serial number is negative";
+ case Certificate_Status_Code::DN_TOO_LONG:
+ return "Distinguished name too long";
+ case Certificate_Status_Code::OCSP_NO_REVOCATION_URL:
+ return "OCSP URL not available";
+ case Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE:
+ return "OCSP server not available";
+
+ case Certificate_Status_Code::NO_REVOCATION_DATA:
+ return "No revocation data";
+ case Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK:
+ return "Signature method too weak";
+ case Certificate_Status_Code::UNTRUSTED_HASH:
+ return "Hash function used is considered too weak for security";
+
+ case Certificate_Status_Code::CERT_NOT_YET_VALID:
+ return "Certificate is not yet valid";
+ case Certificate_Status_Code::CERT_HAS_EXPIRED:
+ return "Certificate has expired";
+ case Certificate_Status_Code::OCSP_NOT_YET_VALID:
+ return "OCSP is not yet valid";
+ case Certificate_Status_Code::OCSP_HAS_EXPIRED:
+ return "OCSP response has expired";
+ case Certificate_Status_Code::OCSP_IS_TOO_OLD:
+ return "OCSP response is too old";
+ case Certificate_Status_Code::CRL_NOT_YET_VALID:
+ return "CRL response is not yet valid";
+ case Certificate_Status_Code::CRL_HAS_EXPIRED:
+ return "CRL has expired";
+
+ case Certificate_Status_Code::CERT_ISSUER_NOT_FOUND:
+ return "Certificate issuer not found";
+ case Certificate_Status_Code::CANNOT_ESTABLISH_TRUST:
+ return "Cannot establish trust";
+ case Certificate_Status_Code::CERT_CHAIN_LOOP:
+ return "Loop in certificate chain";
+ case Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT:
+ return "Certificate chain does not end in a CA certificate";
+ case Certificate_Status_Code::CHAIN_NAME_MISMATCH:
+ return "Certificate issuer does not match subject of issuing cert";
+
+ case Certificate_Status_Code::POLICY_ERROR:
+ return "Certificate policy error";
+ case Certificate_Status_Code::DUPLICATE_CERT_POLICY:
+ return "Certificate contains duplicate policy";
+ case Certificate_Status_Code::INVALID_USAGE:
+ return "Certificate does not allow the requested usage";
+ case Certificate_Status_Code::CERT_CHAIN_TOO_LONG:
+ return "Certificate chain too long";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER:
+ return "CA certificate not allowed to issue certs";
+ case Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER:
+ return "CA certificate not allowed to issue CRLs";
+ case Certificate_Status_Code::NO_MATCHING_CRLDP:
+ return "No CRL with matching distribution point for certificate";
+ case Certificate_Status_Code::OCSP_CERT_NOT_LISTED:
+ return "OCSP cert not listed";
+ case Certificate_Status_Code::OCSP_BAD_STATUS:
+ return "OCSP bad status";
+ case Certificate_Status_Code::CERT_NAME_NOMATCH:
+ return "Certificate does not match provided name";
+ case Certificate_Status_Code::NAME_CONSTRAINT_ERROR:
+ return "Certificate does not pass name constraint";
+ case Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION:
+ return "Unknown critical extension encountered";
+ case Certificate_Status_Code::DUPLICATE_CERT_EXTENSION:
+ return "Duplicate certificate extension encountered";
+ case Certificate_Status_Code::EXT_IN_V1_V2_CERT:
+ return "Encountered extension in certificate with version that does not allow it";
+ case Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT:
+ return "Encountered v2 identifiers in v1 certificate";
+ case Certificate_Status_Code::OCSP_SIGNATURE_ERROR:
+ return "OCSP signature error";
+ case Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND:
+ return "Unable to find certificate issusing OCSP response";
+ case Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE:
+ return "OCSP issuer's keyusage prohibits OCSP";
+ case Certificate_Status_Code::OCSP_RESPONSE_INVALID:
+ return "OCSP parsing valid";
+ case Certificate_Status_Code::OCSP_NO_HTTP:
+ return "OCSP requests not available, no HTTP support compiled in";
+ case Certificate_Status_Code::CERT_IS_REVOKED:
+ return "Certificate is revoked";
+ case Certificate_Status_Code::CRL_BAD_SIGNATURE:
+ return "CRL bad signature";
+ case Certificate_Status_Code::SIGNATURE_ERROR:
+ return "Signature error";
+ case Certificate_Status_Code::CERT_PUBKEY_INVALID:
+ return "Certificate public key invalid";
+ case Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN:
+ return "Certificate signed with unknown/unavailable algorithm";
+ case Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS:
+ return "Certificate signature has invalid parameters";
+
+ // intentionally no default so we are warned if new enum values are added
+ }
+
+ return nullptr;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/cert_status.h b/comm/third_party/botan/src/lib/x509/cert_status.h
new file mode 100644
index 0000000000..f330ff348f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/cert_status.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PATH_RESULT_H_
+#define BOTAN_X509_PATH_RESULT_H_
+
+#include <botan/pkix_enums.h>
+BOTAN_DEPRECATED_HEADER(cert_status.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor.cpp b/comm/third_party/botan/src/lib/x509/certstor.cpp
new file mode 100644
index 0000000000..a066f208d4
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor.cpp
@@ -0,0 +1,233 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor.h>
+#include <botan/pkix_types.h>
+#include <botan/internal/filesystem.h>
+#include <botan/hash.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+Certificate_Store::~Certificate_Store() {}
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ const auto certs = find_all_certs(subject_dn, key_id);
+
+ if(certs.empty())
+ {
+ return nullptr; // certificate not found
+ }
+
+ // `count` might be greater than 1, but we'll just select the first match
+ return certs.front();
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store::find_crl_for(const X509_Certificate&) const
+ {
+ return {};
+ }
+
+void Certificate_Store_In_Memory::add_certificate(const X509_Certificate& cert)
+ {
+ for(const auto& c : m_certs)
+ if(*c == cert)
+ return;
+
+ m_certs.push_back(std::make_shared<const X509_Certificate>(cert));
+ }
+
+void Certificate_Store_In_Memory::add_certificate(std::shared_ptr<const X509_Certificate> cert)
+ {
+ for(const auto& c : m_certs)
+ if(*c == *cert)
+ return;
+
+ m_certs.push_back(cert);
+ }
+
+std::vector<X509_DN> Certificate_Store_In_Memory::all_subjects() const
+ {
+ std::vector<X509_DN> subjects;
+ for(const auto& cert : m_certs)
+ subjects.push_back(cert->subject_dn());
+ return subjects;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ for(const auto& cert : m_certs)
+ {
+ // Only compare key ids if set in both call and in the cert
+ if(key_id.size())
+ {
+ std::vector<uint8_t> skid = cert->subject_key_id();
+
+ if(skid.size() && skid != key_id) // no match
+ continue;
+ }
+
+ if(cert->subject_dn() == subject_dn)
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_In_Memory::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> matches;
+
+ for(const auto& cert : m_certs)
+ {
+ if(key_id.size())
+ {
+ std::vector<uint8_t> skid = cert->subject_key_id();
+
+ if(skid.size() && skid != key_id) // no match
+ continue;
+ }
+
+ if(cert->subject_dn() == subject_dn)
+ matches.push_back(cert);
+ }
+
+ return matches;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_pubkey_sha1 invalid hash");
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-1"));
+
+ for(const auto& cert : m_certs){
+ hash->update(cert->subject_public_key_bitstring());
+ if(key_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ if(subject_hash.size() != 32)
+ throw Invalid_Argument("Certificate_Store_In_Memory::find_cert_by_raw_subject_dn_sha256 invalid hash");
+
+ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-256"));
+
+ for(const auto& cert : m_certs){
+ hash->update(cert->raw_subject_dn());
+ if(subject_hash == hash->final_stdvec()) //final_stdvec also clears the hash to initial state
+ return cert;
+ }
+
+ return nullptr;
+ }
+
+void Certificate_Store_In_Memory::add_crl(const X509_CRL& crl)
+ {
+ std::shared_ptr<const X509_CRL> crl_s = std::make_shared<const X509_CRL>(crl);
+ return add_crl(crl_s);
+ }
+
+void Certificate_Store_In_Memory::add_crl(std::shared_ptr<const X509_CRL> crl)
+ {
+ X509_DN crl_issuer = crl->issuer_dn();
+
+ for(auto& c : m_crls)
+ {
+ // Found an update of a previously existing one; replace it
+ if(c->issuer_dn() == crl_issuer)
+ {
+ if(c->this_update() <= crl->this_update())
+ c = crl;
+ return;
+ }
+ }
+
+ // Totally new CRL, add to the list
+ m_crls.push_back(crl);
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const
+ {
+ const std::vector<uint8_t>& key_id = subject.authority_key_id();
+
+ for(const auto& c : m_crls)
+ {
+ // Only compare key ids if set in both call and in the CRL
+ if(key_id.size())
+ {
+ std::vector<uint8_t> akid = c->authority_key_id();
+
+ if(akid.size() && akid != key_id) // no match
+ continue;
+ }
+
+ if(c->issuer_dn() == subject.issuer_dn())
+ return c;
+ }
+
+ return {};
+ }
+
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const X509_Certificate& cert)
+ {
+ add_certificate(cert);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+Certificate_Store_In_Memory::Certificate_Store_In_Memory(const std::string& dir)
+ {
+ if(dir.empty())
+ return;
+
+ std::vector<std::string> maybe_certs = get_files_recursive(dir);
+
+ if(maybe_certs.empty())
+ {
+ maybe_certs.push_back(dir);
+ }
+
+ for(auto&& cert_file : maybe_certs)
+ {
+ try
+ {
+ DataSource_Stream src(cert_file, true);
+ while(!src.end_of_data())
+ {
+ try
+ {
+ m_certs.push_back(std::make_shared<X509_Certificate>(src));
+ }
+ catch(std::exception&)
+ {
+ // stop searching for other certificate at first exception
+ break;
+ }
+ }
+ }
+ catch(std::exception&)
+ {
+ }
+ }
+ }
+#endif
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor.h b/comm/third_party/botan/src/lib/x509/certstor.h
new file mode 100644
index 0000000000..6901589d26
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor.h
@@ -0,0 +1,165 @@
+/*
+* Certificate Store
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_H_
+#define BOTAN_CERT_STORE_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+
+namespace Botan {
+
+/**
+* Certificate Store Interface
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store
+ {
+ public:
+ virtual ~Certificate_Store();
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @param subject_dn the subject's distinguished name
+ * @param key_id an optional key id
+ * @return a matching certificate or nullptr otherwise
+ * If more than one certificate in the certificate store matches, then
+ * a single value is selected arbitrarily.
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ virtual std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const = 0;
+
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key. Used for OCSP.
+ * @param key_hash SHA-1 hash of the subject's public key
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const = 0;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-256 hash of
+ * raw subject name. Used for OCSP.
+ * @param subject_hash SHA-256 hash of the subject's raw name
+ * @return a matching certificate or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const = 0;
+
+ /**
+ * Finds a CRL for the given certificate
+ * @param subject the subject certificate
+ * @return the CRL for subject or nullptr otherwise
+ */
+ virtual std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const;
+
+ /**
+ * @return whether the certificate is known
+ * @param cert certififcate to be searched
+ */
+ bool certificate_known(const X509_Certificate& cert) const
+ {
+ return find_cert(cert.subject_dn(), cert.subject_key_id()) != nullptr;
+ }
+
+ // remove this (used by TLS::Server)
+ virtual std::vector<X509_DN> all_subjects() const = 0;
+ };
+
+/**
+* In Memory Certificate Store
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_Memory final : public Certificate_Store
+ {
+ public:
+ /**
+ * Attempt to parse all files in dir (including subdirectories)
+ * as certificates. Ignores errors.
+ */
+ explicit Certificate_Store_In_Memory(const std::string& dir);
+
+ /**
+ * Adds given certificate to the store.
+ */
+ explicit Certificate_Store_In_Memory(const X509_Certificate& cert);
+
+ /**
+ * Create an empty store.
+ */
+ Certificate_Store_In_Memory() = default;
+
+ /**
+ * Add a certificate to the store.
+ * @param cert certificate to be added
+ */
+ void add_certificate(const X509_Certificate& cert);
+
+ /**
+ * Add a certificate already in a shared_ptr to the store.
+ * @param cert certificate to be added
+ */
+ void add_certificate(std::shared_ptr<const X509_Certificate> cert);
+
+ /**
+ * Add a certificate revocation list (CRL) to the store.
+ * @param crl CRL to be added
+ */
+ void add_crl(const X509_CRL& crl);
+
+ /**
+ * Add a certificate revocation list (CRL) to the store as a shared_ptr
+ * @param crl CRL to be added
+ */
+ void add_crl(std::shared_ptr<const X509_CRL> crl);
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /*
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /*
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Finds a CRL for the given certificate
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+ private:
+ // TODO: Add indexing on the DN and key id to avoid linear search
+ std::vector<std::shared_ptr<const X509_Certificate>> m_certs;
+ std::vector<std::shared_ptr<const X509_CRL>> m_crls;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
new file mode 100644
index 0000000000..9804759b96
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.cpp
@@ -0,0 +1,148 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_flatfile.h>
+#include <botan/pkix_types.h>
+#include <botan/data_src.h>
+#include <botan/pem.h>
+#include <stdexcept>
+
+namespace Botan {
+namespace {
+std::vector<std::vector<uint8_t>> decode_all_certificates(DataSource& source)
+ {
+ std::vector<std::vector<uint8_t>> pems;
+
+ while(!source.end_of_data())
+ {
+ std::string label;
+ std::vector<uint8_t> cert;
+ try
+ {
+ cert = unlock(PEM_Code::decode(source, label));
+
+ if(label == "CERTIFICATE" || label == "X509 CERTIFICATE" || label == "TRUSTED CERTIFICATE")
+ {
+ pems.push_back(cert);
+ }
+ }
+ catch(const Decoding_Error&) {}
+ }
+
+ return pems;
+ }
+}
+
+Flatfile_Certificate_Store::Flatfile_Certificate_Store(const std::string& file, bool ignore_non_ca)
+ {
+ if(file.empty())
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store invalid file path");
+ }
+
+ DataSource_Stream file_stream(file);
+
+ for(const std::vector<uint8_t>& der : decode_all_certificates(file_stream))
+ {
+ std::shared_ptr<const X509_Certificate> cert = std::make_shared<const X509_Certificate>(der.data(), der.size());
+
+ /*
+ * Various weird or misconfigured system roots include intermediate certificates,
+ * or even stranger certificates which are not valid for cert issuance at all.
+ * Previously this code would error on such cases as an obvious misconfiguration,
+ * but we cannot fix the trust store. So instead just ignore any such certificate.
+ */
+ if(cert->is_self_signed() && cert->is_CA_cert())
+ {
+ m_all_subjects.push_back(cert->subject_dn());
+ m_dn_to_cert[cert->subject_dn()].push_back(cert);
+ m_pubkey_sha1_to_cert.emplace(cert->subject_public_key_bitstring_sha1(), cert);
+ m_subject_dn_sha256_to_cert.emplace(cert->raw_subject_dn_sha256(), cert);
+ }
+ else if(!ignore_non_ca)
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store received non CA cert " + cert->subject_dn().to_string());
+ }
+ }
+
+ if(m_all_subjects.empty())
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::Flatfile_Certificate_Store cert file is empty");
+ }
+ }
+
+std::vector<X509_DN> Flatfile_Certificate_Store::all_subjects() const
+ {
+ return m_all_subjects;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Flatfile_Certificate_Store::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> found_certs;
+ try
+ {
+ const auto certs = m_dn_to_cert.at(subject_dn);
+
+ for(auto cert : certs)
+ {
+ if(key_id.empty() || key_id == cert->subject_key_id())
+ {
+ found_certs.push_back(cert);
+ }
+ }
+ }
+ catch(const std::out_of_range&)
+ {
+ return {};
+ }
+
+ return found_certs;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ {
+ throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_pubkey_sha1 invalid hash");
+ }
+
+ auto found_cert = m_pubkey_sha1_to_cert.find(key_hash);
+
+ if(found_cert != m_pubkey_sha1_to_cert.end())
+ {
+ return found_cert->second;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ if(subject_hash.size() != 32)
+ { throw Invalid_Argument("Flatfile_Certificate_Store::find_cert_by_raw_subject_dn_sha256 invalid hash"); }
+
+ auto found_cert = m_subject_dn_sha256_to_cert.find(subject_hash);
+
+ if(found_cert != m_subject_dn_sha256_to_cert.end())
+ {
+ return found_cert->second;
+ }
+
+ return nullptr;
+ }
+
+std::shared_ptr<const X509_CRL> Flatfile_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+ {
+ BOTAN_UNUSED(subject);
+ return {};
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
new file mode 100644
index 0000000000..1608aaa118
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/certstor_flatfile.h
@@ -0,0 +1,77 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_FLATFILE_H_
+#define BOTAN_CERT_STORE_FLATFILE_H_
+
+#include <botan/certstor.h>
+
+#include <vector>
+#include <memory>
+#include <map>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by a file of PEMs of trusted CAs.
+*/
+class BOTAN_PUBLIC_API(2, 11) Flatfile_Certificate_Store final : public Certificate_Store
+ {
+ public:
+ /**
+ * Construct a new Certificate_Store given a file path to a file including
+ * PEMs of trusted self-signed CAs.
+ *
+ * @param file the name of the file to read certificates from
+ * @param ignore_non_ca if true, certs that are not self-signed CA certs will
+ * be ignored. Otherwise (if false), an exception will be thrown instead.
+ */
+ Flatfile_Certificate_Store(const std::string& file, bool ignore_non_ca = false);
+
+ Flatfile_Certificate_Store(const Flatfile_Certificate_Store&) = default;
+ Flatfile_Certificate_Store(Flatfile_Certificate_Store&&) = default;
+ Flatfile_Certificate_Store& operator=(const Flatfile_Certificate_Store&) = default;
+ Flatfile_Certificate_Store& operator=(Flatfile_Certificate_Store&&) = default;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Fetching CRLs is not supported by this certificate store. This will
+ * always return an empty list.
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ private:
+ std::vector<X509_DN> m_all_subjects;
+ std::map<X509_DN, std::vector<std::shared_ptr<const X509_Certificate>>> m_dn_to_cert;
+ std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_pubkey_sha1_to_cert;
+ std::map<std::vector<uint8_t>, std::shared_ptr<const X509_Certificate>> m_subject_dn_sha256_to_cert;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt b/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt
new file mode 100644
index 0000000000..902997be73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_flatfile/info.txt
@@ -0,0 +1,12 @@
+<defines>
+CERTSTOR_FLATFILE -> 20190410
+</defines>
+
+<os_features>
+filesystem
+</os_features>
+
+<header:public>
+certstor_flatfile.h
+</header:public>
+
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
new file mode 100644
index 0000000000..ab4b8e64a7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.cpp
@@ -0,0 +1,335 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+* (C) 2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sql.h>
+#include <botan/pk_keys.h>
+#include <botan/ber_dec.h>
+#include <botan/pkcs8.h>
+#include <botan/data_src.h>
+#include <botan/pkix_types.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQL::Certificate_Store_In_SQL(std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ m_rng(rng),
+ m_database(db),
+ m_prefix(table_prefix),
+ m_password(passwd)
+ {
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " +
+ m_prefix + "certificates ( \
+ fingerprint BLOB PRIMARY KEY, \
+ subject_dn BLOB, \
+ key_id BLOB, \
+ priv_fingerprint BLOB, \
+ certificate BLOB UNIQUE NOT NULL\
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "keys (\
+ fingerprint BLOB PRIMARY KEY, \
+ key BLOB UNIQUE NOT NULL \
+ )");
+ m_database->create_table("CREATE TABLE IF NOT EXISTS " + m_prefix + "revoked (\
+ fingerprint BLOB PRIMARY KEY, \
+ reason BLOB NOT NULL, \
+ time BLOB NOT NULL \
+ )");
+ }
+
+// Certificate handling
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1 LIMIT 1");
+ stmt->bind(1, dn_encoding);
+ }
+ else
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+ subject_dn == ?1 AND (key_id == NULL OR key_id == ?2) LIMIT 1");
+ stmt->bind(1, dn_encoding);
+ stmt->bind(2,key_id);
+ }
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ return std::make_shared<X509_Certificate>(std::vector<uint8_t>(blob.first, blob.first + blob.second));
+ }
+
+ return std::shared_ptr<const X509_Certificate>();
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ std::vector<std::shared_ptr<const X509_Certificate>> certs;
+
+ std::shared_ptr<SQL_Database::Statement> stmt;
+
+ const std::vector<uint8_t> dn_encoding = subject_dn.BER_encode();
+
+ if(key_id.empty())
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE subject_dn == ?1");
+ stmt->bind(1, dn_encoding);
+ }
+ else
+ {
+ stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE\
+ subject_dn == ?1 AND (key_id == NULL OR key_id == ?2)");
+ stmt->bind(1, dn_encoding);
+ stmt->bind(2, key_id);
+ }
+
+ std::shared_ptr<const X509_Certificate> cert;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ certs.push_back(std::make_shared<X509_Certificate>(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+ }
+
+ return certs;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& /*key_hash*/) const
+ {
+ throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_pubkey_sha1");
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& /*subject_hash*/) const
+ {
+ throw Not_Implemented("Certificate_Store_In_SQL::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL>
+Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const
+ {
+ auto all_crls = generate_crls();
+
+ for(auto crl: all_crls)
+ {
+ if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn())
+ return std::shared_ptr<X509_CRL>(new X509_CRL(crl));
+ }
+
+ return std::shared_ptr<X509_CRL>();
+ }
+
+std::vector<X509_DN> Certificate_Store_In_SQL::all_subjects() const
+ {
+ std::vector<X509_DN> ret;
+ auto stmt = m_database->new_statement("SELECT subject_dn FROM " + m_prefix + "certificates");
+
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ BER_Decoder dec(blob.first,blob.second);
+ X509_DN dn;
+
+ dn.decode_from(dec);
+
+ ret.push_back(dn);
+ }
+
+ return ret;
+ }
+
+bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert)
+ {
+ const std::vector<uint8_t> dn_encoding = cert.subject_dn().BER_encode();
+ const std::vector<uint8_t> cert_encoding = cert.BER_encode();
+
+ auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " +
+ m_prefix + "certificates (\
+ fingerprint, \
+ subject_dn, \
+ key_id, \
+ priv_fingerprint, \
+ certificate \
+ ) VALUES ( ?1, ?2, ?3, ?4, ?5 )");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->bind(2,dn_encoding);
+ stmt->bind(3,cert.subject_key_id());
+ stmt->bind(4,std::vector<uint8_t>());
+ stmt->bind(5,cert_encoding);
+ stmt->spin();
+
+ return true;
+ }
+
+
+bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert)
+ {
+ if(!find_cert(cert.subject_dn(),cert.subject_key_id()))
+ return false;
+
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+
+ return true;
+ }
+
+// Private key handling
+std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const
+ {
+ auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix + "keys "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "keys.fingerprint == " + m_prefix + "certificates.priv_fingerprint "
+ "WHERE " + m_prefix + "certificates.fingerprint == ?1");
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+
+ std::shared_ptr<const Private_Key> key;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ DataSource_Memory src(blob.first,blob.second);
+ key.reset(PKCS8::load_key(src, m_rng, m_password));
+ }
+
+ return key;
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+Certificate_Store_In_SQL::find_certs_for_key(const Private_Key& key) const
+ {
+ auto fpr = key.fingerprint_private("SHA-256");
+ auto stmt = m_database->new_statement("SELECT certificate FROM " + m_prefix + "certificates WHERE priv_fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+
+ std::vector<std::shared_ptr<const X509_Certificate>> certs;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ certs.push_back(std::make_shared<X509_Certificate>(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second)));
+ }
+
+ return certs;
+ }
+
+bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) {
+ insert_cert(cert);
+
+ if(find_key(cert))
+ return false;
+
+ auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password);
+ auto fpr = key.fingerprint_private("SHA-256");
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )");
+
+ stmt1->bind(1,fpr);
+ stmt1->bind(2,pkcs8.data(),pkcs8.size());
+ stmt1->spin();
+
+ auto stmt2 = m_database->new_statement(
+ "UPDATE " + m_prefix + "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2");
+
+ stmt2->bind(1,fpr);
+ stmt2->bind(2,cert.fingerprint("SHA-256"));
+ stmt2->spin();
+
+ return true;
+ }
+
+void Certificate_Store_In_SQL::remove_key(const Private_Key& key)
+ {
+ auto fpr = key.fingerprint_private("SHA-256");
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "keys WHERE fingerprint == ?1");
+
+ stmt->bind(1,fpr);
+ stmt->spin();
+ }
+
+// Revocation
+void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time)
+ {
+ insert_cert(cert);
+
+ auto stmt1 = m_database->new_statement(
+ "INSERT OR REPLACE INTO " + m_prefix + "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )");
+
+ stmt1->bind(1,cert.fingerprint("SHA-256"));
+ stmt1->bind(2,code);
+
+ if(time.time_is_set())
+ {
+ stmt1->bind(3, time.BER_encode());
+ }
+ else
+ {
+ stmt1->bind(3, static_cast<size_t>(-1));
+ }
+
+ stmt1->spin();
+ }
+
+void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert)
+ {
+ auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1");
+
+ stmt->bind(1,cert.fingerprint("SHA-256"));
+ stmt->spin();
+ }
+
+std::vector<X509_CRL> Certificate_Store_In_SQL::generate_crls() const
+ {
+ auto stmt = m_database->new_statement(
+ "SELECT certificate,reason,time FROM " + m_prefix + "revoked "
+ "JOIN " + m_prefix + "certificates ON " +
+ m_prefix + "certificates.fingerprint == " + m_prefix + "revoked.fingerprint");
+
+ std::map<X509_DN,std::vector<CRL_Entry>> crls;
+ while(stmt->step())
+ {
+ auto blob = stmt->get_blob(0);
+ auto cert = X509_Certificate(
+ std::vector<uint8_t>(blob.first,blob.first + blob.second));
+ auto code = static_cast<CRL_Code>(stmt->get_size_t(1));
+ auto ent = CRL_Entry(cert,code);
+
+ auto i = crls.find(cert.issuer_dn());
+ if(i == crls.end())
+ {
+ crls.insert(std::make_pair(cert.issuer_dn(),std::vector<CRL_Entry>({ent})));
+ }
+ else
+ {
+ i->second.push_back(ent);
+ }
+ }
+
+ std::vector<X509_CRL> ret;
+ X509_Time t(std::chrono::system_clock::now());
+
+ for(auto p: crls)
+ {
+ ret.push_back(X509_CRL(p.first,t,t,p.second));
+ }
+
+ return ret;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
new file mode 100644
index 0000000000..fd80eb1919
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/certstor_sql.h
@@ -0,0 +1,119 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQL_H_
+#define BOTAN_CERT_STORE_SQL_H_
+
+#include <botan/certstor.h>
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/database.h>
+#include <botan/mutex.h>
+
+namespace Botan {
+
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+ * Certificate and private key store backed by an SQL database.
+ */
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQL : public Certificate_Store
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db underlying database storage
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ explicit Certificate_Store_In_SQL(const std::shared_ptr<SQL_Database> db,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+
+ /**
+ * Returns the first certificate with matching subject DN and optional key ID.
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /*
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Returns all subject DNs known to the store instance.
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Inserts "cert" into the store, returns false if the certificate is
+ * already known and true if insertion was successful.
+ */
+ bool insert_cert(const X509_Certificate& cert);
+
+ /**
+ * Removes "cert" from the store. Returns false if the certificate could not
+ * be found and true if removal was successful.
+ */
+ bool remove_cert(const X509_Certificate& cert);
+
+ /// Returns the private key for "cert" or an empty shared_ptr if none was found.
+ std::shared_ptr<const Private_Key> find_key(const X509_Certificate&) const;
+
+ /// Returns all certificates for private key "key".
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_certs_for_key(const Private_Key& key) const;
+
+ /**
+ * Inserts "key" for "cert" into the store, returns false if the key is
+ * already known and true if insertion was successful.
+ */
+ bool insert_key(const X509_Certificate& cert, const Private_Key& key);
+
+ /// Removes "key" from the store.
+ void remove_key(const Private_Key& key);
+
+ /// Marks "cert" as revoked starting from "time".
+ void revoke_cert(const X509_Certificate&, CRL_Code, const X509_Time& time = X509_Time());
+
+ /// Reverses the revokation for "cert".
+ void affirm_cert(const X509_Certificate&);
+
+ /**
+ * Generates Certificate Revocation Lists for all certificates marked as revoked.
+ * A CRL is returned for each unique issuer DN.
+ */
+ std::vector<X509_CRL> generate_crls() const;
+
+ /**
+ * Generates a CRL for all certificates issued by the given issuer.
+ */
+ std::shared_ptr<const X509_CRL>
+ find_crl_for(const X509_Certificate& issuer) const override;
+
+ private:
+ RandomNumberGenerator& m_rng;
+ std::shared_ptr<SQL_Database> m_database;
+ std::string m_prefix;
+ std::string m_password;
+ mutex_type m_mutex;
+ };
+
+}
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt b/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt
new file mode 100644
index 0000000000..a6046e5d73
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sql/info.txt
@@ -0,0 +1,3 @@
+<defines>
+CERTSTOR_SQL -> 20160818
+</defines>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
new file mode 100644
index 0000000000..b7c066483d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.cpp
@@ -0,0 +1,19 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_sqlite.h>
+#include <botan/sqlite3.h>
+
+namespace Botan {
+
+Certificate_Store_In_SQLite::Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix) :
+ Certificate_Store_In_SQL(std::make_shared<Sqlite3_Database>(db_path), passwd, rng, table_prefix)
+ {}
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
new file mode 100644
index 0000000000..6d4187e148
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/certstor_sqlite.h
@@ -0,0 +1,34 @@
+/*
+* Certificate Store in SQL
+* (C) 2016 Kai Michaelis, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SQLITE_H_
+#define BOTAN_CERT_STORE_SQLITE_H_
+
+#include <botan/certstor_sql.h>
+
+namespace Botan {
+
+/**
+* Certificate and private key store backed by an sqlite (https://sqlite.org) database.
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Store_In_SQLite final : public Certificate_Store_In_SQL
+ {
+ public:
+ /**
+ * Create/open a certificate store.
+ * @param db_path path to the database file
+ * @param passwd password to encrypt private keys in the database
+ * @param rng used for encrypting keys
+ * @param table_prefix optional prefix for db table names
+ */
+ Certificate_Store_In_SQLite(const std::string& db_path,
+ const std::string& passwd,
+ RandomNumberGenerator& rng,
+ const std::string& table_prefix = "");
+ };
+}
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt
new file mode 100644
index 0000000000..39b8c579b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_sqlite3/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CERTSTOR_SQLITE3 -> 20160818
+</defines>
+
+<requires>
+certstor_sql
+sqlite3
+</requires>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
new file mode 100644
index 0000000000..0fca1a9757
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.cpp
@@ -0,0 +1,70 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_system.h>
+#include <botan/pkix_types.h>
+#include <botan/x509cert.h>
+
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+ #include <botan/certstor_macos.h>
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+ #include <botan/certstor_windows.h>
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+ #include <botan/certstor_flatfile.h>
+#endif
+
+namespace Botan {
+
+System_Certificate_Store::System_Certificate_Store()
+ {
+#if defined(BOTAN_HAS_CERTSTOR_MACOS)
+ m_system_store = std::make_shared<Certificate_Store_MacOS>();
+#elif defined(BOTAN_HAS_CERTSTOR_WINDOWS)
+ m_system_store = std::make_shared<Certificate_Store_Windows>();
+#elif defined(BOTAN_HAS_CERTSTOR_FLATFILE) && defined(BOTAN_SYSTEM_CERT_BUNDLE)
+ m_system_store = std::make_shared<Flatfile_Certificate_Store>(BOTAN_SYSTEM_CERT_BUNDLE, true);
+#else
+ throw Not_Implemented("No system certificate store available in this build");
+#endif
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const
+ {
+ return m_system_store->find_cert(subject_dn, key_id);
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>>
+System_Certificate_Store::find_all_certs(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ return m_system_store->find_all_certs(subject_dn, key_id);
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ return m_system_store->find_cert_by_pubkey_sha1(key_hash);
+ }
+
+std::shared_ptr<const X509_Certificate>
+System_Certificate_Store::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ return m_system_store->find_cert_by_raw_subject_dn_sha256(subject_hash);
+ }
+
+std::shared_ptr<const X509_CRL>
+System_Certificate_Store::find_crl_for(const X509_Certificate& subject) const
+ {
+ return m_system_store->find_crl_for(subject);
+ }
+
+std::vector<X509_DN> System_Certificate_Store::all_subjects() const
+ {
+ return m_system_store->all_subjects();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
new file mode 100644
index 0000000000..3a0fc6153a
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/certstor_system.h
@@ -0,0 +1,42 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SYSTEM_CERT_STORE_H_
+#define BOTAN_SYSTEM_CERT_STORE_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class BOTAN_PUBLIC_API(2,11) System_Certificate_Store final : public Certificate_Store
+ {
+ public:
+
+ System_Certificate_Store();
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::vector<std::shared_ptr<const X509_Certificate>>
+ find_all_certs(const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ std::vector<X509_DN> all_subjects() const override;
+
+ private:
+ std::shared_ptr<Certificate_Store> m_system_store;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system/info.txt
new file mode 100644
index 0000000000..c9b3ca73ef
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system/info.txt
@@ -0,0 +1,8 @@
+<defines>
+CERTSTOR_SYSTEM -> 20190411
+</defines>
+
+<os_features>
+apple_keychain
+filesystem
+</os_features>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
new file mode 100644
index 0000000000..1836f3da4b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.cpp
@@ -0,0 +1,470 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019-2020 René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <algorithm>
+#include <array>
+
+#include <botan/ber_dec.h>
+#include <botan/certstor_macos.h>
+#include <botan/data_src.h>
+#include <botan/der_enc.h>
+#include <botan/exceptn.h>
+#include <botan/pkix_types.h>
+
+#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+ * Abstract RAII wrapper for CFTypeRef-style object handles
+ * All of those xxxRef types are eventually typedefs to void*
+ */
+template<typename T>
+class scoped_CFType
+ {
+ public:
+ explicit scoped_CFType(T value)
+ : m_value(value)
+ {
+ }
+
+ scoped_CFType(const scoped_CFType<T>& rhs) = delete;
+ scoped_CFType(scoped_CFType<T>&& rhs) :
+ m_value(std::move(rhs.m_value))
+ {
+ rhs.m_value = nullptr;
+ }
+
+ ~scoped_CFType()
+ {
+ if(m_value)
+ {
+ CFRelease(m_value);
+ }
+ }
+
+ operator bool() const { return m_value != nullptr; }
+
+ void assign(T value)
+ {
+ BOTAN_ASSERT(m_value == nullptr, "scoped_CFType was not set yet");
+ m_value = value;
+ }
+
+ T& get() { return m_value; }
+ const T& get() const { return m_value; }
+
+ private:
+ T m_value;
+ };
+
+/**
+ * Apple's DN parser "normalizes" ASN1 'PrintableString' into upper-case values
+ * and strips leading, trailing as well as multiple white spaces.
+ * See: opensource.apple.com/source/Security/Security-55471/sec/Security/SecCertificate.c.auto.html
+ */
+X509_DN normalize(const X509_DN& dn)
+ {
+ X509_DN result;
+
+ for(const auto& rdn : dn.dn_info())
+ {
+ // TODO: C++14 - use std::get<ASN1_String>(), resp. std::get<OID>()
+ const auto oid = rdn.first;
+ auto str = rdn.second;
+
+ if(str.tagging() == ASN1_Tag::PRINTABLE_STRING)
+ {
+ std::string normalized;
+ normalized.reserve(str.value().size());
+ for(const char c : str.value())
+ {
+ if(c != ' ')
+ {
+ // store all 'normal' characters as upper case
+ normalized.push_back(::toupper(c));
+ }
+ else if(!normalized.empty() && normalized.back() != ' ')
+ {
+ // remove leading and squash multiple white spaces
+ normalized.push_back(c);
+ }
+ }
+
+ if(normalized.back() == ' ')
+ {
+ // remove potential remaining single trailing white space char
+ normalized.erase(normalized.end() - 1);
+ }
+
+ str = ASN1_String(normalized, str.tagging());
+ }
+
+ result.add_attribute(oid, str);
+ }
+
+ return result;
+ }
+
+std::vector<uint8_t> normalizeAndSerialize(const X509_DN& dn)
+ {
+ std::vector<uint8_t> result_dn;
+ DER_Encoder encoder(result_dn);
+ normalize(dn).encode_into(encoder);
+ return result_dn;
+ }
+
+std::string to_string(const CFStringRef cfstring)
+ {
+ const char* ccstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8);
+
+ if(ccstr != nullptr)
+ {
+ return std::string(ccstr);
+ }
+
+ auto utf16_pairs = CFStringGetLength(cfstring);
+ auto max_utf8_bytes = CFStringGetMaximumSizeForEncoding(utf16_pairs, kCFStringEncodingUTF8);
+
+ std::vector<char> cstr(max_utf8_bytes, '\0');
+ auto result = CFStringGetCString(cfstring,
+ cstr.data(), cstr.size(),
+ kCFStringEncodingUTF8);
+
+ return (result) ? std::string(cstr.data()) : std::string();
+ }
+
+std::string to_string(const OSStatus status)
+ {
+ scoped_CFType<CFStringRef> eCFString(
+ SecCopyErrorMessageString(status, nullptr));
+ return to_string(eCFString.get());
+ }
+
+void check_success(const OSStatus status, const std::string context)
+ {
+ if(errSecSuccess == status)
+ {
+ return;
+ }
+
+ throw Internal_Error(
+ std::string("failed to " + context + ": " + to_string(status)));
+ }
+
+template <typename T>
+void check_notnull(const T& value, const std::string context)
+ {
+ if(value)
+ {
+ return;
+ }
+
+ throw Internal_Error(std::string("failed to ") + context);
+ }
+
+} // namespace
+
+/**
+ * Internal class implementation (i.e. Pimpl) to keep the required platform-
+ * dependent members of Certificate_Store_MacOS contained in this compilation
+ * unit.
+ */
+class Certificate_Store_MacOS_Impl
+ {
+ private:
+ static constexpr const char* system_roots =
+ "/System/Library/Keychains/SystemRootCertificates.keychain";
+ static constexpr const char* system_keychain =
+ "/Library/Keychains/System.keychain";
+
+ public:
+ /**
+ * Wraps a list of search query parameters that are later passed into
+ * Apple's certifificate store API. The class provides some convenience
+ * functionality and handles the query paramenter's data lifetime.
+ */
+ class Query
+ {
+ public:
+ Query() = default;
+ ~Query() = default;
+ Query(Query&& other) = default;
+ Query& operator=(Query&& other) = default;
+
+ Query(const Query& other) = delete;
+ Query& operator=(const Query& other) = delete;
+
+ public:
+ void addParameter(CFStringRef key, CFTypeRef value)
+ {
+ m_keys.emplace_back(key);
+ m_values.emplace_back(value);
+ }
+
+ void addParameter(CFStringRef key, std::vector<uint8_t> value)
+ {
+ // TODO C++17: std::vector::emplace_back will return the reference
+ // to the inserted object straight away.
+ m_data_store.emplace_back(std::move(value));
+ const auto& data = m_data_store.back();
+
+ m_data_refs.emplace_back(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
+ data.data(),
+ data.size(),
+ kCFAllocatorNull));
+ const auto& data_ref = m_data_refs.back();
+ check_notnull(data_ref, "create CFDataRef of search object failed");
+
+ addParameter(key, data_ref.get());
+ }
+
+ /**
+ * Amends the user-provided search query with generic filter rules
+ * for the associated system keychains and transforms it into a
+ * representation that can be passed to the Apple keychain API.
+ */
+ scoped_CFType<CFDictionaryRef> prepare(const CFArrayRef& keychains,
+ const SecPolicyRef& policy)
+ {
+ addParameter(kSecClass, kSecClassCertificate);
+ addParameter(kSecReturnRef, kCFBooleanTrue);
+ addParameter(kSecMatchLimit, kSecMatchLimitAll);
+ addParameter(kSecMatchTrustedOnly, kCFBooleanTrue);
+ addParameter(kSecMatchSearchList, keychains);
+ addParameter(kSecMatchPolicy, policy);
+
+ BOTAN_ASSERT_EQUAL(m_keys.size(), m_values.size(), "valid key-value pairs");
+
+ auto query = scoped_CFType<CFDictionaryRef>(CFDictionaryCreate(
+ kCFAllocatorDefault, (const void**)m_keys.data(),
+ (const void**)m_values.data(), m_keys.size(),
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ check_notnull(query, "create search query");
+
+ return query;
+ }
+
+ private:
+ using Data = std::vector<std::vector<uint8_t>>;
+ using DataRefs = std::vector<scoped_CFType<CFDataRef>>;
+ using Keys = std::vector<CFStringRef>;
+ using Values = std::vector<CFTypeRef>;
+
+ Data m_data_store; //! makes sure that data parameters are kept alive
+ DataRefs m_data_refs; //! keeps track of CFDataRef objects refering into \p m_data_store
+ Keys m_keys; //! ordered list of search parameter keys
+ Values m_values; //! ordered list of search parameter values
+ };
+
+ public:
+ Certificate_Store_MacOS_Impl() :
+ m_policy(SecPolicyCreateBasicX509()),
+ m_system_roots(nullptr),
+ m_system_chain(nullptr),
+ m_keychains(nullptr)
+ {
+ check_success(SecKeychainOpen(system_roots, &m_system_roots.get()),
+ "open system root certificates");
+ check_success(SecKeychainOpen(system_keychain, &m_system_chain.get()),
+ "open system keychain");
+ check_notnull(m_system_roots, "open system root certificate chain");
+ check_notnull(m_system_chain, "open system certificate chain");
+
+ // m_keychains is merely a convenience list view into all open keychain
+ // objects. This list is required in prepareQuery().
+ std::array<const void*, 2> keychains{{
+ m_system_roots.get(),
+ m_system_chain.get()
+ }};
+
+ m_keychains.assign(
+ CFArrayCreate(kCFAllocatorDefault,
+ keychains.data(),
+ keychains.size(),
+ &kCFTypeArrayCallBacks));
+ check_notnull(m_keychains, "initialize keychain array");
+ }
+
+ std::shared_ptr<const X509_Certificate> findOne(Query query) const
+ {
+ query.addParameter(kSecMatchLimit, kSecMatchLimitOne);
+
+ scoped_CFType<CFTypeRef> result(nullptr);
+ search(std::move(query), &result.get());
+
+ return (result) ? readCertificate(result.get()) : nullptr;
+ }
+
+ std::vector<std::shared_ptr<const X509_Certificate>> findAll(Query query) const
+ {
+ query.addParameter(kSecMatchLimit, kSecMatchLimitAll);
+
+ scoped_CFType<CFArrayRef> result(nullptr);
+ search(std::move(query), (CFTypeRef*)&result.get());
+
+ std::vector<std::shared_ptr<const X509_Certificate>> output;
+
+ if(result)
+ {
+ const auto count = CFArrayGetCount(result.get());
+ BOTAN_ASSERT(count > 0, "certificate result list contains data");
+
+ for(unsigned int i = 0; i < count; ++i)
+ {
+ auto cert = CFArrayGetValueAtIndex(result.get(), i);
+ output.emplace_back(readCertificate(cert));
+ }
+ }
+
+ return output;
+ }
+
+ protected:
+ void search(Query query, CFTypeRef* result) const
+ {
+ scoped_CFType<CFDictionaryRef> fullQuery(query.prepare(keychains(), policy()));
+
+ auto status = SecItemCopyMatching(fullQuery.get(), result);
+
+ if(errSecItemNotFound == status)
+ {
+ return; // no matches
+ }
+
+ check_success(status, "look up certificate");
+ check_notnull(result, "look up certificate (invalid result value)");
+ }
+
+ /**
+ * Convert a CFTypeRef object into a Botan::X509_Certificate
+ */
+ std::shared_ptr<const X509_Certificate> readCertificate(CFTypeRef object) const
+ {
+ if(!object || CFGetTypeID(object) != SecCertificateGetTypeID())
+ {
+ throw Internal_Error("cannot convert CFTypeRef to SecCertificateRef");
+ }
+
+ auto cert = static_cast<SecCertificateRef>(const_cast<void*>(object));
+
+ scoped_CFType<CFDataRef> derData(SecCertificateCopyData(cert));
+ check_notnull(derData, "read extracted certificate");
+
+ const auto data = CFDataGetBytePtr(derData.get());
+ const auto length = CFDataGetLength(derData.get());
+
+ DataSource_Memory ds(data, length);
+ return std::make_shared<Botan::X509_Certificate>(ds);
+ }
+
+ CFArrayRef keychains() const { return m_keychains.get(); }
+ SecPolicyRef policy() const { return m_policy.get(); }
+
+ private:
+ scoped_CFType<SecPolicyRef> m_policy;
+ scoped_CFType<SecKeychainRef> m_system_roots;
+ scoped_CFType<SecKeychainRef> m_system_chain;
+ scoped_CFType<CFArrayRef> m_keychains;
+ };
+
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+// Implementation of Botan::Certificate_Store interface ...
+//
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+//
+
+Certificate_Store_MacOS::Certificate_Store_MacOS() :
+ m_impl(std::make_shared<Certificate_Store_MacOS_Impl>())
+ {
+ }
+
+std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const
+ {
+ // Note: This fetches and parses all certificates in the trust store.
+ // Apple's API provides SecCertificateCopyNormalizedSubjectSequence
+ // which facilitates reading the certificate DN without parsing the
+ // entire certificate via Botan::X509_Certificate. However, this
+ // function applies the same DN "normalization" as stated above.
+ const auto certificates = m_impl->findAll({});
+
+ std::vector<X509_DN> output;
+ std::transform(certificates.cbegin(), certificates.cend(),
+ std::back_inserter(output),
+ [](const std::shared_ptr<const X509_Certificate> cert)
+ {
+ return cert->subject_dn();
+ });
+
+ return output;
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert(const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+ if(!key_id.empty())
+ {
+ query.addParameter(kSecAttrSubjectKeyID, key_id);
+ }
+
+ return m_impl->findOne(std::move(query));
+ }
+
+std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrSubject, normalizeAndSerialize(subject_dn));
+
+ if(!key_id.empty())
+ {
+ query.addParameter(kSecAttrSubjectKeyID, key_id);
+ }
+
+ return m_impl->findAll(std::move(query));
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ {
+ throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");
+ }
+
+ Certificate_Store_MacOS_Impl::Query query;
+ query.addParameter(kSecAttrPublicKeyHash, key_hash);
+
+ return m_impl->findOne(std::move(query));
+ }
+
+std::shared_ptr<const X509_Certificate>
+Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const
+ {
+ BOTAN_UNUSED(subject_hash);
+ throw Not_Implemented("Certificate_Store_MacOS::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_MacOS::find_crl_for(const X509_Certificate& subject) const
+ {
+ BOTAN_UNUSED(subject);
+ return {};
+ }
+
+} // namespace Botan
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
new file mode 100644
index 0000000000..e7416e631c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/certstor_macos.h
@@ -0,0 +1,81 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 René Meusel
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+#define BOTAN_CERT_STORE_SYSTEM_MACOS_H_
+
+#include <memory>
+
+#include <botan/certstor.h>
+
+namespace Botan {
+
+class Certificate_Store_MacOS_Impl;
+
+/**
+* Certificate Store that is backed by the system trust store on macOS. This
+* opens a handle to the macOS keychain and serves certificate queries directly
+* from there.
+*/
+class BOTAN_PUBLIC_API(2, 10) Certificate_Store_MacOS final : public Certificate_Store
+ {
+ public:
+ Certificate_Store_MacOS();
+
+ Certificate_Store_MacOS(const Certificate_Store_MacOS&) = default;
+ Certificate_Store_MacOS(Certificate_Store_MacOS&&) = default;
+ Certificate_Store_MacOS& operator=(const Certificate_Store_MacOS&) = default;
+ Certificate_Store_MacOS& operator=(Certificate_Store_MacOS&&) = default;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ /**
+ * @throws Botan::Not_Implemented
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Fetching CRLs is not supported by the keychain on macOS. This will
+ * always return an empty list.
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+
+ private:
+ std::shared_ptr<Certificate_Store_MacOS_Impl> m_impl;
+ };
+
+}
+
+#endif \ No newline at end of file
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt
new file mode 100644
index 0000000000..723ec61bd9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_macos/info.txt
@@ -0,0 +1,15 @@
+<defines>
+CERTSTOR_MACOS -> 20190207
+</defines>
+
+<os_features>
+apple_keychain
+</os_features>
+
+<header:public>
+certstor_macos.h
+</header:public>
+
+<frameworks>
+macos -> CoreFoundation,Security
+</frameworks>
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
new file mode 100644
index 0000000000..51530954ac
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.cpp
@@ -0,0 +1,258 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2018-2019 Patrik Fiedler, Tim Oesterreich
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/certstor_windows.h>
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+
+#include <array>
+#include <vector>
+
+#define NOMINMAX 1
+#define _WINSOCKAPI_ // stop windows.h including winsock.h
+#include <windows.h>
+#include <wincrypt.h>
+
+#define WINCRYPT_UNUSED_PARAM 0 // for avoiding warnings when passing NULL to unused params in win32 api that accept integer types
+
+namespace Botan {
+namespace {
+
+using Cert_Pointer = std::shared_ptr<const Botan::X509_Certificate>;
+using Cert_Vector = std::vector<Cert_Pointer>;
+const std::array<const char*, 2> cert_store_names{"Root", "CA"};
+
+/**
+ * Abstract RAII wrapper for PCCERT_CONTEXT and HCERTSTORE
+ * The Windows API partly takes care of those pointers destructions itself.
+ * Especially, iteratively calling `CertFindCertificateInStore` with the previous PCCERT_CONTEXT
+ * will free the context and return a new one. In this case, this guard takes care of freeing the context
+ * in case of an exception and at the end of the iterative process.
+ */
+template<class T>
+class Handle_Guard
+ {
+ public:
+ Handle_Guard(T context)
+ : m_context(context)
+ {
+ }
+
+ Handle_Guard(const Handle_Guard<T>& rhs) = delete;
+ Handle_Guard(Handle_Guard<T>&& rhs) :
+ m_context(std::move(rhs.m_context))
+ {
+ rhs.m_context = nullptr;
+ }
+
+ ~Handle_Guard()
+ {
+ close<T>();
+ }
+
+ operator bool() const
+ {
+ return m_context != nullptr;
+ }
+
+ bool assign(T context)
+ {
+ m_context = context;
+ return m_context != nullptr;
+ }
+
+ T& get()
+ {
+ return m_context;
+ }
+
+ const T& get() const
+ {
+ return m_context;
+ }
+
+ T operator->()
+ {
+ return m_context;
+ }
+
+ private:
+ template<class T2 = T>
+ typename std::enable_if<std::is_same<T2, PCCERT_CONTEXT>::value>::type close()
+ {
+ if(m_context)
+ {
+ CertFreeCertificateContext(m_context);
+ }
+ }
+
+ template<class T2 = T>
+ typename std::enable_if<std::is_same<T2, HCERTSTORE>::value>::type close()
+ {
+ if(m_context)
+ {
+ // second parameter is a flag that tells the store how to deallocate memory
+ // using the default "0", this function works like decreasing the reference counter
+ // in a shared_ptr
+ CertCloseStore(m_context, 0);
+ }
+ }
+
+ T m_context;
+ };
+
+HCERTSTORE open_cert_store(const char* cert_store_name)
+ {
+ auto store = CertOpenSystemStoreA(WINCRYPT_UNUSED_PARAM, cert_store_name);
+ if(!store)
+ {
+ throw Botan::Internal_Error(
+ "failed to open windows certificate store '" + std::string(cert_store_name) +
+ "' (Error Code: " +
+ std::to_string(::GetLastError()) + ")");
+ }
+ return store;
+ }
+
+Cert_Vector search_cert_stores(const _CRYPTOAPI_BLOB& blob, const DWORD& find_type,
+ std::function<bool(const Cert_Vector& certs, Cert_Pointer cert)> filter,
+ bool return_on_first_found)
+ {
+ Cert_Vector certs;
+ for(const auto store_name : cert_store_names)
+ {
+ Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+ Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+ while(cert_context.assign(CertFindCertificateInStore(
+ windows_cert_store.get(), PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ WINCRYPT_UNUSED_PARAM, find_type,
+ &blob, cert_context.get())))
+ {
+ auto cert = std::make_shared<X509_Certificate>(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+ if(filter(certs, cert))
+ {
+ if(return_on_first_found)
+ {
+ return {cert};
+ }
+ certs.push_back(cert);
+ }
+ }
+ }
+
+ return certs;
+ }
+
+bool already_contains_certificate(const Cert_Vector& certs, Cert_Pointer cert)
+ {
+ return std::any_of(certs.begin(), certs.end(), [&](std::shared_ptr<const Botan::X509_Certificate> c)
+ {
+ return *c == *cert;
+ });
+ }
+
+Cert_Vector find_cert_by_dn_and_key_id(const Botan::X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id,
+ bool return_on_first_found)
+ {
+ _CRYPTOAPI_BLOB blob;
+ DWORD find_type;
+ std::vector<uint8_t> dn_data;
+
+ // if key_id is available, prefer searching that, as it should be "more unique" than the subject DN
+ if(key_id.empty())
+ {
+ find_type = CERT_FIND_SUBJECT_NAME;
+ DER_Encoder encoder(dn_data);
+ subject_dn.encode_into(encoder);
+ blob.cbData = static_cast<DWORD>(dn_data.size());
+ blob.pbData = reinterpret_cast<BYTE*>(dn_data.data());
+ }
+ else
+ {
+ find_type = CERT_FIND_KEY_IDENTIFIER;
+ blob.cbData = static_cast<DWORD>(key_id.size());
+ blob.pbData = const_cast<BYTE*>(key_id.data());
+ }
+
+ auto filter = [&](const Cert_Vector& certs, Cert_Pointer cert)
+ {
+ return !already_contains_certificate(certs, cert) && (key_id.empty() || cert->subject_dn() == subject_dn);
+ };
+
+ return search_cert_stores(blob, find_type, filter, return_on_first_found);
+ }
+} // namespace
+
+Certificate_Store_Windows::Certificate_Store_Windows() {}
+
+std::vector<X509_DN> Certificate_Store_Windows::all_subjects() const
+ {
+ std::vector<X509_DN> subject_dns;
+ for(const auto store_name : cert_store_names)
+ {
+ Handle_Guard<HCERTSTORE> windows_cert_store = open_cert_store(store_name);
+ Handle_Guard<PCCERT_CONTEXT> cert_context = nullptr;
+
+ // Handle_Guard::assign exchanges the underlying pointer. No RAII is needed here, because the Windows API takes care of
+ // freeing the previous context.
+ while(cert_context.assign(CertEnumCertificatesInStore(windows_cert_store.get(), cert_context.get())))
+ {
+ X509_Certificate cert(cert_context->pbCertEncoded, cert_context->cbCertEncoded);
+ subject_dns.push_back(cert.subject_dn());
+ }
+ }
+
+ return subject_dns;
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert(const Botan::X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ const auto certs = find_cert_by_dn_and_key_id(subject_dn, key_id, true);
+ return certs.empty() ? nullptr : certs.front();
+ }
+
+Cert_Vector Certificate_Store_Windows::find_all_certs(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const
+ {
+ return find_cert_by_dn_and_key_id(subject_dn, key_id, false);
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const
+ {
+ if(key_hash.size() != 20)
+ {
+ throw Invalid_Argument("Certificate_Store_Windows::find_cert_by_pubkey_sha1 invalid hash");
+ }
+
+ CRYPT_HASH_BLOB blob;
+ blob.cbData = static_cast<DWORD>(key_hash.size());
+ blob.pbData = const_cast<BYTE*>(key_hash.data());
+
+ auto filter = [](const Cert_Vector&, Cert_Pointer) { return true; };
+
+ const auto certs = search_cert_stores(blob, CERT_FIND_KEY_IDENTIFIER, filter, true);
+ return certs.empty() ? nullptr : certs.front();
+ }
+
+Cert_Pointer Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256(
+ const std::vector<uint8_t>& subject_hash) const
+ {
+ BOTAN_UNUSED(subject_hash);
+ throw Not_Implemented("Certificate_Store_Windows::find_cert_by_raw_subject_dn_sha256");
+ }
+
+std::shared_ptr<const X509_CRL> Certificate_Store_Windows::find_crl_for(const X509_Certificate& subject) const
+ {
+ // TODO: this could be implemented by using the CertFindCRLInStore function
+ BOTAN_UNUSED(subject);
+ return {};
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
new file mode 100644
index 0000000000..f47e718c85
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/certstor_windows.h
@@ -0,0 +1,70 @@
+/*
+* Certificate Store
+* (C) 1999-2019 Jack Lloyd
+* (C) 2019 Patrick Schmidt
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+#define BOTAN_CERT_STORE_SYSTEM_WINDOWS_H_
+
+#include <botan/certstor.h>
+
+namespace Botan {
+/**
+* Certificate Store that is backed by the system trust store on Windows.
+*/
+class BOTAN_PUBLIC_API(2, 11) Certificate_Store_Windows final : public Certificate_Store
+ {
+ public:
+ Certificate_Store_Windows();
+
+ Certificate_Store_Windows(const Certificate_Store_Windows&) = default;
+ Certificate_Store_Windows(Certificate_Store_Windows&&) = default;
+ Certificate_Store_Windows& operator=(const Certificate_Store_Windows&) = default;
+ Certificate_Store_Windows& operator=(Certificate_Store_Windows&&) = default;
+
+ /**
+ * @return DNs for all certificates managed by the store
+ */
+ std::vector<X509_DN> all_subjects() const override;
+
+ /**
+ * Find a certificate by Subject DN and (optionally) key identifier
+ * @return the first certificate that matches
+ */
+ std::shared_ptr<const X509_Certificate> find_cert(
+ const X509_DN& subject_dn,
+ const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find all certificates with a given Subject DN.
+ * Subject DN and even the key identifier might not be unique.
+ */
+ std::vector<std::shared_ptr<const X509_Certificate>> find_all_certs(
+ const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const override;
+
+ /**
+ * Find a certificate by searching for one with a matching SHA-1 hash of
+ * public key.
+ * @return a matching certificate or nullptr otherwise
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const override;
+
+ /**
+ * @throws Botan::Not_Implemented
+ */
+ std::shared_ptr<const X509_Certificate>
+ find_cert_by_raw_subject_dn_sha256(const std::vector<uint8_t>& subject_hash) const override;
+
+ /**
+ * Not Yet Implemented
+ * @return nullptr;
+ */
+ std::shared_ptr<const X509_CRL> find_crl_for(const X509_Certificate& subject) const override;
+ };
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt b/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt
new file mode 100644
index 0000000000..6d70de8bf2
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/certstor_system_windows/info.txt
@@ -0,0 +1,16 @@
+<defines>
+CERTSTOR_WINDOWS -> 20190430
+</defines>
+
+<os_features>
+win32,certificate_store
+</os_features>
+
+<header:public>
+certstor_windows.h
+</header:public>
+
+<libs>
+windows -> crypt32
+mingw -> crypt32
+</libs>
diff --git a/comm/third_party/botan/src/lib/x509/crl_ent.cpp b/comm/third_party/botan/src/lib/x509/crl_ent.cpp
new file mode 100644
index 0000000000..d83d43d73c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/crl_ent.cpp
@@ -0,0 +1,140 @@
+/*
+* CRL Entry
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509cert.h>
+#include <botan/x509_ext.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+
+namespace Botan {
+
+struct CRL_Entry_Data
+ {
+ std::vector<uint8_t> m_serial;
+ X509_Time m_time;
+ CRL_Code m_reason = UNSPECIFIED;
+ Extensions m_extensions;
+ };
+
+/*
+* Create a CRL_Entry
+*/
+CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why)
+ {
+ m_data.reset(new CRL_Entry_Data);
+ m_data->m_serial = cert.serial_number();
+ m_data->m_time = X509_Time(std::chrono::system_clock::now());
+ m_data->m_reason = why;
+
+ if(why != UNSPECIFIED)
+ {
+ m_data->m_extensions.add(new Cert_Extension::CRL_ReasonCode(why));
+ }
+ }
+
+/*
+* Compare two CRL_Entrys for equality
+*/
+bool operator==(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ if(a1.serial_number() != a2.serial_number())
+ return false;
+ if(a1.expire_time() != a2.expire_time())
+ return false;
+ if(a1.reason_code() != a2.reason_code())
+ return false;
+ return true;
+ }
+
+/*
+* Compare two CRL_Entrys for inequality
+*/
+bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
+ {
+ return !(a1 == a2);
+ }
+
+/*
+* DER encode a CRL_Entry
+*/
+void CRL_Entry::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE)
+ .encode(BigInt::decode(serial_number()))
+ .encode(expire_time())
+ .start_cons(SEQUENCE)
+ .encode(extensions())
+ .end_cons()
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded CRL_Entry
+*/
+void CRL_Entry::decode_from(BER_Decoder& source)
+ {
+ BigInt serial_number_bn;
+
+ std::unique_ptr<CRL_Entry_Data> data(new CRL_Entry_Data);
+
+ BER_Decoder entry = source.start_cons(SEQUENCE);
+
+ entry.decode(serial_number_bn).decode(data->m_time);
+ data->m_serial = BigInt::encode(serial_number_bn);
+
+ if(entry.more_items())
+ {
+ entry.decode(data->m_extensions);
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_ReasonCode>())
+ {
+ data->m_reason = ext->get_reason();
+ }
+ else
+ {
+ data->m_reason = UNSPECIFIED;
+ }
+ }
+
+ entry.end_cons();
+
+ m_data.reset(data.release());
+ }
+
+const CRL_Entry_Data& CRL_Entry::data() const
+ {
+ if(!m_data)
+ {
+ throw Invalid_State("CRL_Entry_Data uninitialized");
+ }
+
+ return *m_data.get();
+ }
+
+const std::vector<uint8_t>& CRL_Entry::serial_number() const
+ {
+ return data().m_serial;
+ }
+
+const X509_Time& CRL_Entry::expire_time() const
+ {
+ return data().m_time;
+ }
+
+CRL_Code CRL_Entry::reason_code() const
+ {
+ return data().m_reason;
+ }
+
+const Extensions& CRL_Entry::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/crl_ent.h b/comm/third_party/botan/src/lib/x509/crl_ent.h
new file mode 100644
index 0000000000..aa60d4172b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/crl_ent.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CRL_ENTRY_H_
+#define BOTAN_CRL_ENTRY_H_
+
+#include <botan/x509_crl.h>
+BOTAN_DEPRECATED_HEADER(crl_ent.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/datastor.cpp b/comm/third_party/botan/src/lib/x509/datastor.cpp
new file mode 100644
index 0000000000..2cdd3458ca
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/datastor.cpp
@@ -0,0 +1,205 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/datastor.h>
+#include <botan/exceptn.h>
+#include <botan/parsing.h>
+#include <botan/hex.h>
+#include <botan/internal/stl_util.h>
+
+namespace Botan {
+
+/*
+* Data_Store Equality Comparison
+*/
+bool Data_Store::operator==(const Data_Store& other) const
+ {
+ return (m_contents == other.m_contents);
+ }
+
+/*
+* Check if this key has at least one value
+*/
+bool Data_Store::has_value(const std::string& key) const
+ {
+ return (m_contents.lower_bound(key) != m_contents.end());
+ }
+
+/*
+* Search based on an arbitrary predicate
+*/
+std::multimap<std::string, std::string> Data_Store::search_for(
+ std::function<bool (std::string, std::string)> predicate) const
+ {
+ std::multimap<std::string, std::string> out;
+
+ for(auto i = m_contents.begin(); i != m_contents.end(); ++i)
+ if(predicate(i->first, i->second))
+ out.insert(std::make_pair(i->first, i->second));
+
+ return out;
+ }
+
+/*
+* Search based on key equality
+*/
+std::vector<std::string> Data_Store::get(const std::string& looking_for) const
+ {
+ std::vector<std::string> out;
+ auto range = m_contents.equal_range(looking_for);
+ for(auto i = range.first; i != range.second; ++i)
+ out.push_back(i->second);
+ return out;
+ }
+
+/*
+* Get a single atom
+*/
+std::string Data_Store::get1(const std::string& key) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ throw Invalid_State("Data_Store::get1: No values set for " + key);
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+ return vals[0];
+ }
+
+std::string Data_Store::get1(const std::string& key,
+ const std::string& default_value) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1: More than one value for " + key);
+
+ if(vals.empty())
+ return default_value;
+
+ return vals[0];
+ }
+
+/*
+* Get a single std::vector atom
+*/
+std::vector<uint8_t>
+Data_Store::get1_memvec(const std::string& key) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ return std::vector<uint8_t>();
+
+ if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1_memvec: Multiple values for " +
+ key);
+
+ return hex_decode(vals[0]);
+ }
+
+/*
+* Get a single uint32_t atom
+*/
+uint32_t Data_Store::get1_uint32(const std::string& key,
+ uint32_t default_val) const
+ {
+ std::vector<std::string> vals = get(key);
+
+ if(vals.empty())
+ return default_val;
+ else if(vals.size() > 1)
+ throw Invalid_State("Data_Store::get1_uint32: Multiple values for " + key);
+
+ return to_u32bit(vals[0]);
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const std::string& val)
+ {
+ multimap_insert(m_contents, key, val);
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, uint32_t val)
+ {
+ add(key, std::to_string(val));
+ }
+
+/*
+* Insert a single key and value
+*/
+void Data_Store::add(const std::string& key, const secure_vector<uint8_t>& val)
+ {
+ add(key, hex_encode(val.data(), val.size()));
+ }
+
+void Data_Store::add(const std::string& key, const std::vector<uint8_t>& val)
+ {
+ add(key, hex_encode(val.data(), val.size()));
+ }
+
+/*
+* Insert a mapping of key/value pairs
+*/
+void Data_Store::add(const std::multimap<std::string, std::string>& in)
+ {
+ std::multimap<std::string, std::string>::const_iterator i = in.begin();
+ while(i != in.end())
+ {
+ m_contents.insert(*i);
+ ++i;
+ }
+ }
+
+/*
+* Create and populate a X509_DN
+*/
+X509_DN create_dn(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key.find("X520.") != std::string::npos);
+ });
+
+ X509_DN dn;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ dn.add_attribute(i->first, i->second);
+
+ return dn;
+ }
+
+/*
+* Create and populate an AlternativeName
+*/
+AlternativeName create_alt_name(const Data_Store& info)
+ {
+ auto names = info.search_for(
+ [](const std::string& key, const std::string&)
+ {
+ return (key == "RFC822" ||
+ key == "DNS" ||
+ key == "URI" ||
+ key == "IP");
+ });
+
+ AlternativeName alt_name;
+
+ for(auto i = names.begin(); i != names.end(); ++i)
+ alt_name.add_attribute(i->first, i->second);
+
+ return alt_name;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/datastor.h b/comm/third_party/botan/src/lib/x509/datastor.h
new file mode 100644
index 0000000000..1ff85c22cf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/datastor.h
@@ -0,0 +1,85 @@
+/*
+* Data Store
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_DATA_STORE_H_
+#define BOTAN_DATA_STORE_H_
+
+#include <botan/pkix_types.h>
+#include <functional>
+#include <string>
+#include <vector>
+#include <map>
+
+BOTAN_FUTURE_INTERNAL_HEADER(datastor.h)
+
+namespace Botan {
+
+/**
+* Data Store
+*
+* This class is used internally by the library, and exposed for ABI
+* reasons. There is no reason for applications to use this type directly.
+* It will be removed in a future major release.
+*/
+class BOTAN_UNSTABLE_API Data_Store final
+ {
+ public:
+ /**
+ * A search function
+ */
+ bool operator==(const Data_Store&) const;
+
+ std::multimap<std::string, std::string> search_for(
+ std::function<bool (std::string, std::string)> predicate) const;
+
+ std::vector<std::string> get(const std::string&) const;
+
+ std::string get1(const std::string& key) const;
+
+ std::string get1(const std::string& key,
+ const std::string& default_value) const;
+
+ std::vector<uint8_t> get1_memvec(const std::string&) const;
+ uint32_t get1_uint32(const std::string&, uint32_t = 0) const;
+
+ bool has_value(const std::string&) const;
+
+ void add(const std::multimap<std::string, std::string>&);
+ void add(const std::string&, const std::string&);
+ void add(const std::string&, uint32_t);
+ void add(const std::string&, const secure_vector<uint8_t>&);
+ void add(const std::string&, const std::vector<uint8_t>&);
+ private:
+ std::multimap<std::string, std::string> m_contents;
+ };
+
+/*
+* Data Store Extraction Operations
+*/
+
+/*
+* Create and populate a X509_DN
+* @param info data store containing DN information
+* @return DN containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) X509_DN
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_dn(const Data_Store& info);
+
+/*
+* Create and populate an AlternativeName
+* @param info data store containing AlternativeName information
+* @return AlternativeName containing attributes from data store
+*/
+BOTAN_PUBLIC_API(2,0) AlternativeName
+BOTAN_DEPRECATED("Avoid roundtripping names through Data_Store")
+create_alt_name(const Data_Store& info);
+
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/info.txt b/comm/third_party/botan/src/lib/x509/info.txt
new file mode 100644
index 0000000000..20a1aa2b08
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/info.txt
@@ -0,0 +1,12 @@
+<defines>
+X509_CERTIFICATES -> 20151023
+X509 -> 20180911
+OCSP -> 20161118
+</defines>
+
+<requires>
+asn1
+pubkey
+sha1
+sha2_32
+</requires>
diff --git a/comm/third_party/botan/src/lib/x509/key_constraint.cpp b/comm/third_party/botan/src/lib/x509/key_constraint.cpp
new file mode 100644
index 0000000000..09a4c059a3
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/key_constraint.cpp
@@ -0,0 +1,106 @@
+/*
+* KeyUsage
+* (C) 1999-2007,2016 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/pk_keys.h>
+#include <vector>
+
+namespace Botan {
+
+std::string key_constraints_to_string(Key_Constraints constraints)
+ {
+ std::vector<std::string> str;
+
+ if(constraints == NO_CONSTRAINTS)
+ return "no_constraints";
+
+ if(constraints & DIGITAL_SIGNATURE)
+ str.push_back("digital_signature");
+
+ if(constraints & NON_REPUDIATION)
+ str.push_back("non_repudiation");
+
+ if(constraints & KEY_ENCIPHERMENT)
+ str.push_back("key_encipherment");
+
+ if(constraints & DATA_ENCIPHERMENT)
+ str.push_back("data_encipherment");
+
+ if(constraints & KEY_AGREEMENT)
+ str.push_back("key_agreement");
+
+ if(constraints & KEY_CERT_SIGN)
+ str.push_back("key_cert_sign");
+
+ if(constraints & CRL_SIGN)
+ str.push_back("crl_sign");
+
+ if(constraints & ENCIPHER_ONLY)
+ str.push_back("encipher_only");
+
+ if(constraints & DECIPHER_ONLY)
+ str.push_back("decipher_only");
+
+ // Not 0 (checked at start) but nothing matched above!
+ if(str.empty())
+ return "other_unknown_constraints";
+
+ if(str.size() == 1)
+ return str[0];
+
+ std::string out;
+ for(size_t i = 0; i < str.size() - 1; ++i)
+ {
+ out += str[i];
+ out += ',';
+ }
+ out += str[str.size() - 1];
+
+ return out;
+ }
+
+/*
+* Make sure the given key constraints are permitted for the given key type
+*/
+void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints)
+ {
+ const std::string name = pub_key.algo_name();
+
+ size_t permitted = 0;
+
+ const bool can_agree = (name == "DH" || name == "ECDH");
+ const bool can_encrypt = (name == "RSA" || name == "ElGamal");
+
+ const bool can_sign =
+ (name == "RSA" || name == "DSA" ||
+ name == "ECDSA" || name == "ECGDSA" || name == "ECKCDSA" || name == "Ed25519" ||
+ name == "GOST-34.10" || name == "GOST-34.10-2012-256" || name == "GOST-34.10-2012-512");
+
+ if(can_agree)
+ {
+ permitted |= KEY_AGREEMENT | ENCIPHER_ONLY | DECIPHER_ONLY;
+ }
+
+ if(can_encrypt)
+ {
+ permitted |= KEY_ENCIPHERMENT | DATA_ENCIPHERMENT;
+ }
+
+ if(can_sign)
+ {
+ permitted |= DIGITAL_SIGNATURE | NON_REPUDIATION | KEY_CERT_SIGN | CRL_SIGN;
+ }
+
+ if(Key_Constraints(constraints & permitted) != constraints)
+ {
+ throw Invalid_Argument("Invalid " + name + " constraints " + key_constraints_to_string(constraints));
+ }
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/key_constraint.h b/comm/third_party/botan/src/lib/x509/key_constraint.h
new file mode 100644
index 0000000000..5d5c7b0832
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/key_constraint.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_KEY_CONSTRAINT_H_
+#define BOTAN_KEY_CONSTRAINT_H_
+
+#include <botan/pkix_enums.h>
+BOTAN_DEPRECATED_HEADER(key_constraint.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/name_constraint.cpp b/comm/third_party/botan/src/lib/x509/name_constraint.cpp
new file mode 100644
index 0000000000..c9045729de
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/name_constraint.cpp
@@ -0,0 +1,273 @@
+/*
+* X.509 Name Constraint
+* (C) 2015 Kai Michaelis
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/ber_dec.h>
+#include <botan/loadstor.h>
+#include <botan/x509cert.h>
+#include <botan/parsing.h>
+#include <sstream>
+
+namespace Botan {
+
+class DER_Encoder;
+
+GeneralName::GeneralName(const std::string& str) : GeneralName()
+ {
+ size_t p = str.find(':');
+
+ if(p != std::string::npos)
+ {
+ m_type = str.substr(0, p);
+ m_name = str.substr(p + 1, std::string::npos);
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralName::encode_into(DER_Encoder&) const
+ {
+ throw Not_Implemented("GeneralName encoding");
+ }
+
+void GeneralName::decode_from(class BER_Decoder& ber)
+ {
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.is_a(1, CONTEXT_SPECIFIC))
+ {
+ m_type = "RFC822";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(2, CONTEXT_SPECIFIC))
+ {
+ m_type = "DNS";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_type = "URI";
+ m_name = ASN1::to_string(obj);
+ }
+ else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+ {
+ m_type = "DN";
+ X509_DN dn;
+ BER_Decoder dec(obj);
+ std::stringstream ss;
+
+ dn.decode_from(dec);
+ ss << dn;
+
+ m_name = ss.str();
+ }
+ else if(obj.is_a(7, CONTEXT_SPECIFIC))
+ {
+ if(obj.length() == 8)
+ {
+ m_type = "IP";
+ m_name = ipv4_to_string(load_be<uint32_t>(obj.bits(), 0)) + "/" +
+ ipv4_to_string(load_be<uint32_t>(obj.bits(), 1));
+ }
+ else if(obj.length() == 32)
+ {
+ throw Decoding_Error("Unsupported IPv6 name constraint");
+ }
+ else
+ {
+ throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
+ }
+ }
+ else
+ {
+ throw Decoding_Error("Found unknown GeneralName type");
+ }
+ }
+
+GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const
+ {
+ std::vector<std::string> nam;
+ std::function<bool(const GeneralName*, const std::string&)> match_fn;
+
+ const X509_DN& dn = cert.subject_dn();
+ const AlternativeName& alt_name = cert.subject_alt_name();
+
+ if(type() == "DNS")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dns);
+
+ nam = alt_name.get_attribute("DNS");
+
+ if(nam.empty())
+ {
+ nam = dn.get_attribute("CN");
+ }
+ }
+ else if(type() == "DN")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_dn);
+
+ nam.push_back(dn.to_string());
+
+ const auto alt_dn = alt_name.dn();
+ if(alt_dn.empty() == false)
+ {
+ nam.push_back(alt_dn.to_string());
+ }
+ }
+ else if(type() == "IP")
+ {
+ match_fn = std::mem_fn(&GeneralName::matches_ip);
+ nam = alt_name.get_attribute("IP");
+ }
+ else
+ {
+ return MatchResult::UnknownType;
+ }
+
+ if(nam.empty())
+ {
+ return MatchResult::NotFound;
+ }
+
+ bool some = false;
+ bool all = true;
+
+ for(const std::string& n: nam)
+ {
+ bool m = match_fn(this, n);
+
+ some |= m;
+ all &= m;
+ }
+
+ if(all)
+ {
+ return MatchResult::All;
+ }
+ else if(some)
+ {
+ return MatchResult::Some;
+ }
+ else
+ {
+ return MatchResult::None;
+ }
+ }
+
+bool GeneralName::matches_dns(const std::string& nam) const
+ {
+ if(nam.size() == name().size())
+ {
+ return tolower_string(nam) == tolower_string(name());
+ }
+ else if(name().size() > nam.size())
+ {
+ // The constraint is longer than the issued name: not possibly a match
+ return false;
+ }
+ else // name.size() < nam.size()
+ {
+ // constr is suffix of nam
+ const std::string constr = name().front() == '.' ? name() : "." + name();
+ const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
+ return tolower_string(constr) == tolower_string(substr);
+ }
+ }
+
+bool GeneralName::matches_dn(const std::string& nam) const
+ {
+ std::stringstream ss(nam);
+ std::stringstream tt(name());
+ X509_DN nam_dn, my_dn;
+
+ ss >> nam_dn;
+ tt >> my_dn;
+
+ auto attr = nam_dn.get_attributes();
+ bool ret = true;
+ size_t trys = 0;
+
+ for(const auto& c: my_dn.dn_info())
+ {
+ auto i = attr.equal_range(c.first);
+
+ if(i.first != i.second)
+ {
+ trys += 1;
+ ret = ret && (i.first->second == c.second.value());
+ }
+ }
+
+ return trys > 0 && ret;
+ }
+
+bool GeneralName::matches_ip(const std::string& nam) const
+ {
+ uint32_t ip = string_to_ipv4(nam);
+ std::vector<std::string> p = split_on(name(), '/');
+
+ if(p.size() != 2)
+ throw Decoding_Error("failed to parse IPv4 address");
+
+ uint32_t net = string_to_ipv4(p.at(0));
+ uint32_t mask = string_to_ipv4(p.at(1));
+
+ return (ip & mask) == net;
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn)
+ {
+ os << gn.type() << ":" << gn.name();
+ return os;
+ }
+
+GeneralSubtree::GeneralSubtree(const std::string& str) : GeneralSubtree()
+ {
+ size_t p0, p1;
+ const auto min = std::stoull(str, &p0, 10);
+ const auto max = std::stoull(str.substr(p0 + 1), &p1, 10);
+ GeneralName gn(str.substr(p0 + p1 + 2));
+
+ if(p0 > 0 && p1 > 0)
+ {
+ m_minimum = static_cast<size_t>(min);
+ m_maximum = static_cast<size_t>(max);
+ m_base = gn;
+ }
+ else
+ {
+ throw Invalid_Argument("Failed to decode Name Constraint");
+ }
+ }
+
+void GeneralSubtree::encode_into(DER_Encoder&) const
+ {
+ throw Not_Implemented("General Subtree encoding");
+ }
+
+void GeneralSubtree::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .decode(m_base)
+ .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
+ .end_cons();
+
+ if(m_minimum != 0)
+ throw Decoding_Error("GeneralSubtree minimum must be 0");
+
+ m_maximum = std::numeric_limits<std::size_t>::max();
+ }
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
+ {
+ os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+ return os;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/name_constraint.h b/comm/third_party/botan/src/lib/x509/name_constraint.h
new file mode 100644
index 0000000000..4f56f89eda
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/name_constraint.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_NAME_CONSTRAINT_H_
+#define BOTAN_NAME_CONSTRAINT_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(name_constraint.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/ocsp.cpp b/comm/third_party/botan/src/lib/x509/ocsp.cpp
new file mode 100644
index 0000000000..1ca8232634
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp.cpp
@@ -0,0 +1,363 @@
+/*
+* OCSP
+* (C) 2012,2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/certstor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/oids.h>
+#include <botan/base64.h>
+#include <botan/pubkey.h>
+#include <botan/parsing.h>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+ #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+namespace OCSP {
+
+namespace {
+
+// TODO: should this be in a header somewhere?
+void decode_optional_list(BER_Decoder& ber,
+ ASN1_Tag tag,
+ std::vector<X509_Certificate>& output)
+ {
+ BER_Object obj = ber.get_next_object();
+
+ if(obj.is_a(tag, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+ {
+ ber.push_back(obj);
+ return;
+ }
+
+ BER_Decoder list(obj);
+
+ while(list.more_items())
+ {
+ BER_Object certbits = list.get_next_object();
+ X509_Certificate cert(certbits.bits(), certbits.length());
+ output.push_back(std::move(cert));
+ }
+ }
+
+}
+
+Request::Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert) :
+ m_issuer(issuer_cert),
+ m_certid(m_issuer, BigInt::decode(subject_cert.serial_number()))
+ {
+ if(subject_cert.issuer_dn() != issuer_cert.subject_dn())
+ throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)");
+ }
+
+Request::Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial) :
+ m_issuer(issuer_cert),
+ m_certid(m_issuer, subject_serial)
+ {
+ }
+
+std::vector<uint8_t> Request::BER_encode() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(static_cast<size_t>(0)) // version #
+ .end_explicit()
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(m_certid)
+ .end_cons()
+ .end_cons()
+ .end_cons()
+ .end_cons();
+
+ return output;
+ }
+
+std::string Request::base64_encode() const
+ {
+ return Botan::base64_encode(BER_encode());
+ }
+
+Response::Response(Certificate_Status_Code status)
+ {
+ m_status = Response_Status_Code::Successful;
+ m_dummy_response_status = status;
+ }
+
+Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
+ m_response_bits(response_bits, response_bits + response_bits_len)
+ {
+ m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE);
+
+ size_t resp_status = 0;
+
+ response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
+
+ m_status = static_cast<Response_Status_Code>(resp_status);
+
+ if(m_status != Response_Status_Code::Successful)
+ { return; }
+
+ if(response_outer.more_items())
+ {
+ BER_Decoder response_bytes =
+ response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
+
+ response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"),
+ "Unknown response type in OCSP response");
+
+ BER_Decoder basicresponse =
+ BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
+
+ basicresponse.start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .decode(m_sig_algo)
+ .decode(m_signature, BIT_STRING);
+ decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
+
+ size_t responsedata_version = 0;
+ Extensions extensions;
+
+ BER_Decoder(m_tbs_bits)
+ .decode_optional(responsedata_version, ASN1_Tag(0),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional(m_signer_name, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode_optional_string(m_key_hash, OCTET_STRING, 2,
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+
+ .decode(m_produced_at)
+
+ .decode_list(m_responses)
+
+ .decode_optional(extensions, ASN1_Tag(1),
+ ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ }
+
+ response_outer.end_cons();
+ }
+
+Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const
+ {
+ if (m_responses.empty())
+ return m_dummy_response_status;
+
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key());
+
+ const std::vector<std::string> sig_info =
+ split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+ if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ std::string padding = sig_info[1];
+ const Signature_Format format = pub_key->default_x509_signature_format();
+
+ PK_Verifier verifier(*pub_key, padding, format);
+
+ if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature))
+ return Certificate_Status_Code::OCSP_SIGNATURE_OK;
+ else
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ catch(Exception&)
+ {
+ return Certificate_Status_Code::OCSP_SIGNATURE_ERROR;
+ }
+ }
+
+Certificate_Status_Code Response::check_signature(const std::vector<Certificate_Store*>& trusted_roots,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& ee_cert_path) const
+ {
+ if (m_responses.empty())
+ return m_dummy_response_status;
+
+ std::shared_ptr<const X509_Certificate> signing_cert;
+
+ for(size_t i = 0; i != trusted_roots.size(); ++i)
+ {
+ if(m_signer_name.empty() && m_key_hash.empty())
+ return Certificate_Status_Code::OCSP_RESPONSE_INVALID;
+
+ if(!m_signer_name.empty())
+ {
+ signing_cert = trusted_roots[i]->find_cert(m_signer_name, std::vector<uint8_t>());
+ if(signing_cert)
+ {
+ break;
+ }
+ }
+
+ if(m_key_hash.size() > 0)
+ {
+ signing_cert = trusted_roots[i]->find_cert_by_pubkey_sha1(m_key_hash);
+ if(signing_cert)
+ {
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert && ee_cert_path.size() > 1)
+ {
+ // End entity cert is not allowed to sign their own OCSP request :)
+ for(size_t i = 1; i < ee_cert_path.size(); ++i)
+ {
+ // Check all CA certificates in the (assumed validated) EE cert path
+ if(!m_signer_name.empty() && ee_cert_path[i]->subject_dn() == m_signer_name)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+
+ if(m_key_hash.size() > 0 && ee_cert_path[i]->subject_public_key_bitstring_sha1() == m_key_hash)
+ {
+ signing_cert = ee_cert_path[i];
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert && m_certs.size() > 0)
+ {
+ for(size_t i = 0; i < m_certs.size(); ++i)
+ {
+ // Check all CA certificates in the (assumed validated) EE cert path
+ if(!m_signer_name.empty() && m_certs[i].subject_dn() == m_signer_name)
+ {
+ signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+ break;
+ }
+
+ if(m_key_hash.size() > 0 && m_certs[i].subject_public_key_bitstring_sha1() == m_key_hash)
+ {
+ signing_cert = std::make_shared<const X509_Certificate>(m_certs[i]);
+ break;
+ }
+ }
+ }
+
+ if(!signing_cert)
+ return Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND;
+
+ if(!signing_cert->allowed_usage(CRL_SIGN) &&
+ !signing_cert->allowed_extended_usage("PKIX.OCSPSigning"))
+ {
+ return Certificate_Status_Code::OCSP_RESPONSE_MISSING_KEYUSAGE;
+ }
+
+ return this->verify_signature(*signing_cert);
+ }
+
+Certificate_Status_Code Response::status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_age) const
+ {
+ if(m_responses.empty())
+ { return m_dummy_response_status; }
+
+ for(const auto& response : m_responses)
+ {
+ if(response.certid().is_id_for(issuer, subject))
+ {
+ X509_Time x509_ref_time(ref_time);
+
+ if(response.cert_status() == 1)
+ { return Certificate_Status_Code::CERT_IS_REVOKED; }
+
+ if(response.this_update() > x509_ref_time)
+ { return Certificate_Status_Code::OCSP_NOT_YET_VALID; }
+
+ if(response.next_update().time_is_set())
+ {
+ if(x509_ref_time > response.next_update())
+ { return Certificate_Status_Code::OCSP_HAS_EXPIRED; }
+ }
+ else if(max_age > std::chrono::seconds::zero() && ref_time - response.this_update().to_std_timepoint() > max_age)
+ { return Certificate_Status_Code::OCSP_IS_TOO_OLD; }
+
+ if(response.cert_status() == 0)
+ { return Certificate_Status_Code::OCSP_RESPONSE_GOOD; }
+ else
+ { return Certificate_Status_Code::OCSP_BAD_STATUS; }
+ }
+ }
+
+ return Certificate_Status_Code::OCSP_CERT_NOT_LISTED;
+ }
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+Response online_check(const X509_Certificate& issuer,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout)
+ {
+ if(ocsp_responder.empty())
+ throw Invalid_Argument("No OCSP responder specified");
+
+ OCSP::Request req(issuer, subject_serial);
+
+ auto http = HTTP::POST_sync(ocsp_responder,
+ "application/ocsp-request",
+ req.BER_encode(),
+ 1,
+ timeout);
+
+ http.throw_unless_ok();
+
+ // Check the MIME type?
+
+ OCSP::Response response(http.body());
+
+ std::vector<Certificate_Store*> trusted_roots_vec;
+ trusted_roots_vec.push_back(trusted_roots);
+
+ if(trusted_roots)
+ response.check_signature(trusted_roots_vec);
+
+ return response;
+ }
+
+
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout)
+ {
+ if(subject.issuer_dn() != issuer.subject_dn())
+ throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)");
+
+ return online_check(issuer,
+ BigInt::decode(subject.serial_number()),
+ subject.ocsp_responder(),
+ trusted_roots,
+ timeout);
+ }
+
+#endif
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/ocsp.h b/comm/third_party/botan/src/lib/x509/ocsp.h
new file mode 100644
index 0000000000..5522456442
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp.h
@@ -0,0 +1,282 @@
+/*
+* OCSP
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_H_
+#define BOTAN_OCSP_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_types.h>
+#include <botan/x509cert.h>
+#include <botan/bigint.h>
+#include <chrono>
+
+namespace Botan {
+
+class Certificate_Store;
+
+namespace OCSP {
+
+class BOTAN_PUBLIC_API(2,0) CertID final : public ASN1_Object
+ {
+ public:
+ CertID() = default;
+
+ CertID(const X509_Certificate& issuer,
+ const BigInt& subject_serial);
+
+ bool is_id_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const;
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+
+ const std::vector<uint8_t>& issuer_key_hash() const { return m_issuer_key_hash; }
+
+ private:
+ AlgorithmIdentifier m_hash_id;
+ std::vector<uint8_t> m_issuer_dn_hash;
+ std::vector<uint8_t> m_issuer_key_hash;
+ BigInt m_subject_serial;
+ };
+
+class BOTAN_PUBLIC_API(2,0) SingleResponse final : public ASN1_Object
+ {
+ public:
+ const CertID& certid() const { return m_certid; }
+
+ size_t cert_status() const { return m_cert_status; }
+
+ X509_Time this_update() const { return m_thisupdate; }
+
+ X509_Time next_update() const { return m_nextupdate; }
+
+ void encode_into(class DER_Encoder& to) const override;
+
+ void decode_from(class BER_Decoder& from) override;
+ private:
+ CertID m_certid;
+ size_t m_cert_status = 2; // unknown
+ X509_Time m_thisupdate;
+ X509_Time m_nextupdate;
+ };
+
+/**
+* An OCSP request.
+*/
+class BOTAN_PUBLIC_API(2,0) Request final
+ {
+ public:
+ /**
+ * Create an OCSP request.
+ * @param issuer_cert issuer certificate
+ * @param subject_cert subject certificate
+ */
+ Request(const X509_Certificate& issuer_cert,
+ const X509_Certificate& subject_cert);
+
+ Request(const X509_Certificate& issuer_cert,
+ const BigInt& subject_serial);
+
+ /**
+ * @return BER-encoded OCSP request
+ */
+ std::vector<uint8_t> BER_encode() const;
+
+ /**
+ * @return Base64-encoded OCSP request
+ */
+ std::string base64_encode() const;
+
+ /**
+ * @return issuer certificate
+ */
+ const X509_Certificate& issuer() const { return m_issuer; }
+
+ /**
+ * @return subject certificate
+ */
+ const X509_Certificate& subject() const { throw Not_Implemented("Method have been deprecated"); }
+
+ const std::vector<uint8_t>& issuer_key_hash() const
+ { return m_certid.issuer_key_hash(); }
+ private:
+ X509_Certificate m_issuer;
+ CertID m_certid;
+ };
+
+/**
+* OCSP response status.
+*
+* see https://tools.ietf.org/html/rfc6960#section-4.2.1
+*/
+enum class Response_Status_Code {
+ Successful = 0,
+ Malformed_Request = 1,
+ Internal_Error = 2,
+ Try_Later = 3,
+ Sig_Required = 5,
+ Unauthorized = 6
+};
+
+/**
+* OCSP response.
+*
+* Note this class is only usable as an OCSP client
+*/
+class BOTAN_PUBLIC_API(2,0) Response final
+ {
+ public:
+ /**
+ * Creates an empty OCSP response.
+ */
+ Response() = default;
+
+ /**
+ * Create a fake OCSP response from a given status code.
+ * @param status the status code the check functions will return
+ */
+ Response(Certificate_Status_Code status);
+
+ /**
+ * Parses an OCSP response.
+ * @param response_bits response bits received
+ */
+ Response(const std::vector<uint8_t>& response_bits) :
+ Response(response_bits.data(), response_bits.size())
+ {}
+
+ /**
+ * Parses an OCSP response.
+ * @param response_bits response bits received
+ * @param response_bits_len length of response in bytes
+ */
+ Response(const uint8_t response_bits[],
+ size_t response_bits_len);
+
+ /**
+ * Check signature and return status
+ * The optional cert_path is the (already validated!) certificate path of
+ * the end entity which is being inquired about
+ * @param trust_roots list of certstores containing trusted roots
+ * @param cert_path optionally, the (already verified!) certificate path for the certificate
+ * this is an OCSP response for. This is necessary to find the correct intermediate CA in
+ * some cases.
+ */
+ Certificate_Status_Code check_signature(const std::vector<Certificate_Store*>& trust_roots,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path = {}) const;
+
+ /**
+ * Verify that issuer's key signed this response
+ * @param issuer certificate of issuer
+ * @return if signature valid OCSP_SIGNATURE_OK else an error code
+ */
+ Certificate_Status_Code verify_signature(const X509_Certificate& issuer) const;
+
+ /**
+ * @return the status of the response
+ */
+ Response_Status_Code status() const { return m_status; }
+
+ /**
+ * @return the time this OCSP response was supposedly produced at
+ */
+ const X509_Time& produced_at() const { return m_produced_at; }
+
+ /**
+ * @return DN of signer, if provided in response (may be empty)
+ */
+ const X509_DN& signer_name() const { return m_signer_name; }
+
+ /**
+ * @return key hash, if provided in response (may be empty)
+ */
+ const std::vector<uint8_t>& signer_key_hash() const { return m_key_hash; }
+
+ const std::vector<uint8_t>& raw_bits() const { return m_response_bits; }
+
+ /**
+ * Searches the OCSP response for issuer and subject certificate.
+ * @param issuer issuer certificate
+ * @param subject subject certificate
+ * @param ref_time the reference time
+ * @param max_age the maximum age the response should be considered valid
+ * if next_update is not set
+ * @return OCSP status code, possible values:
+ * CERT_IS_REVOKED,
+ * OCSP_NOT_YET_VALID,
+ * OCSP_HAS_EXPIRED,
+ * OCSP_IS_TOO_OLD,
+ * OCSP_RESPONSE_GOOD,
+ * OCSP_BAD_STATUS,
+ * OCSP_CERT_NOT_LISTED
+ */
+ Certificate_Status_Code status_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ std::chrono::system_clock::time_point ref_time = std::chrono::system_clock::now(),
+ std::chrono::seconds max_age = std::chrono::seconds::zero()) const;
+
+ /**
+ * @return the certificate chain, if provided in response
+ */
+ const std::vector<X509_Certificate> &certificates() const { return m_certs; }
+
+ private:
+ Response_Status_Code m_status;
+ std::vector<uint8_t> m_response_bits;
+ X509_Time m_produced_at;
+ X509_DN m_signer_name;
+ std::vector<uint8_t> m_key_hash;
+ std::vector<uint8_t> m_tbs_bits;
+ AlgorithmIdentifier m_sig_algo;
+ std::vector<uint8_t> m_signature;
+ std::vector<X509_Certificate> m_certs;
+
+ std::vector<SingleResponse> m_responses;
+
+ Certificate_Status_Code m_dummy_response_status;
+ };
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject_serial the subject's serial number
+* @param ocsp_responder the OCSP responder to query
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,1)
+Response online_check(const X509_Certificate& issuer,
+ const BigInt& subject_serial,
+ const std::string& ocsp_responder,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+/**
+* Makes an online OCSP request via HTTP and returns the OCSP response.
+* @param issuer issuer certificate
+* @param subject subject certificate
+* @param trusted_roots trusted roots for the OCSP response
+* @param timeout a timeout on the HTTP request
+* @return OCSP response
+*/
+BOTAN_PUBLIC_API(2,0)
+Response online_check(const X509_Certificate& issuer,
+ const X509_Certificate& subject,
+ Certificate_Store* trusted_roots,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(3000));
+
+#endif
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/ocsp_types.cpp b/comm/third_party/botan/src/lib/x509/ocsp_types.cpp
new file mode 100644
index 0000000000..c4b78112eb
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp_types.cpp
@@ -0,0 +1,105 @@
+/*
+* OCSP subtypes
+* (C) 2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/ocsp.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/x509_ext.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace OCSP {
+
+CertID::CertID(const X509_Certificate& issuer,
+ const BigInt& subject_serial)
+ {
+ /*
+ In practice it seems some responders, including, notably,
+ ocsp.verisign.com, will reject anything but SHA-1 here
+ */
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw("SHA-160"));
+
+ m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM);
+ m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring()));
+ m_issuer_dn_hash = unlock(hash->process(issuer.raw_subject_dn()));
+ m_subject_serial = subject_serial;
+ }
+
+bool CertID::is_id_for(const X509_Certificate& issuer,
+ const X509_Certificate& subject) const
+ {
+ try
+ {
+ if(BigInt::decode(subject.serial_number()) != m_subject_serial)
+ return false;
+
+ const std::string hash_algo = m_hash_id.get_oid().to_formatted_string();
+ std::unique_ptr<HashFunction> hash = HashFunction::create_or_throw(hash_algo);
+
+ if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn())))
+ return false;
+
+ if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring())))
+ return false;
+ }
+ catch(...)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+void CertID::encode_into(class DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .encode(m_hash_id)
+ .encode(m_issuer_dn_hash, OCTET_STRING)
+ .encode(m_issuer_key_hash, OCTET_STRING)
+ .encode(m_subject_serial)
+ .end_cons();
+ }
+
+void CertID::decode_from(class BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .decode(m_hash_id)
+ .decode(m_issuer_dn_hash, OCTET_STRING)
+ .decode(m_issuer_key_hash, OCTET_STRING)
+ .decode(m_subject_serial)
+ .end_cons();
+
+ }
+
+void SingleResponse::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("SingleResponse::encode_into");
+ }
+
+void SingleResponse::decode_from(class BER_Decoder& from)
+ {
+ BER_Object cert_status;
+ Extensions extensions;
+
+ from.start_cons(SEQUENCE)
+ .decode(m_certid)
+ .get_next(cert_status)
+ .decode(m_thisupdate)
+ .decode_optional(m_nextupdate, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .decode_optional(extensions,
+ ASN1_Tag(1),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+ .end_cons();
+
+ m_cert_status = cert_status.type();
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/ocsp_types.h b/comm/third_party/botan/src/lib/x509/ocsp_types.h
new file mode 100644
index 0000000000..aa628d8d60
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/ocsp_types.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_OCSP_TYPES_H_
+#define BOTAN_OCSP_TYPES_H_
+
+#include <botan/ocsp.h>
+BOTAN_DEPRECATED_HEADER(ocsp_types.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkcs10.cpp b/comm/third_party/botan/src/lib/x509/pkcs10.cpp
new file mode 100644
index 0000000000..1da5ecca48
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkcs10.cpp
@@ -0,0 +1,304 @@
+/*
+* PKCS #10
+* (C) 1999-2007,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkcs10.h>
+#include <botan/x509_key.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+
+namespace Botan {
+
+struct PKCS10_Data
+ {
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_public_key_bits;
+ AlternativeName m_alt_name;
+ std::string m_challenge;
+ Extensions m_extensions;
+ };
+
+std::string PKCS10_Request::PEM_label() const
+ {
+ return "CERTIFICATE REQUEST";
+ }
+
+std::vector<std::string> PKCS10_Request::alternate_PEM_labels() const
+ {
+ return { "NEW CERTIFICATE REQUEST" };
+ }
+
+PKCS10_Request::PKCS10_Request(DataSource& src)
+ {
+ load_data(src);
+ }
+
+PKCS10_Request::PKCS10_Request(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+PKCS10_Request::PKCS10_Request(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+//static
+PKCS10_Request PKCS10_Request::create(const Private_Key& key,
+ const X509_DN& subject_dn,
+ const Extensions& extensions,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng,
+ const std::string& padding_scheme,
+ const std::string& challenge)
+ {
+ AlgorithmIdentifier sig_algo;
+ std::unique_ptr<PK_Signer> signer = choose_sig_format(sig_algo, key, rng, hash_fn, padding_scheme);
+
+ const size_t PKCS10_VERSION = 0;
+
+ DER_Encoder tbs_req;
+
+ tbs_req.start_cons(SEQUENCE)
+ .encode(PKCS10_VERSION)
+ .encode(subject_dn)
+ .raw_bytes(key.subject_public_key())
+ .start_explicit(0);
+
+ if(challenge.empty() == false)
+ {
+ std::vector<uint8_t> value;
+ DER_Encoder(value).encode(ASN1_String(challenge, DIRECTORY_STRING));
+ tbs_req.encode(Attribute("PKCS9.ChallengePassword", value));
+ }
+
+ std::vector<uint8_t> extension_req;
+ DER_Encoder(extension_req).start_cons(SEQUENCE).encode(extensions).end_cons();
+ tbs_req.encode(Attribute("PKCS9.ExtensionRequest", extension_req));
+
+ // end the start_explicit above
+ tbs_req.end_explicit().end_cons();
+
+ const std::vector<uint8_t> req =
+ X509_Object::make_signed(signer.get(), rng, sig_algo,
+ tbs_req.get_contents());
+
+ return PKCS10_Request(req);
+ }
+
+/*
+* Decode the CertificateRequestInfo
+*/
+namespace {
+
+std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
+ {
+ std::unique_ptr<PKCS10_Data> data(new PKCS10_Data);
+
+ BER_Decoder cert_req_info(body);
+
+ size_t version;
+ cert_req_info.decode(version);
+ if(version != 0)
+ throw Decoding_Error("Unknown version code in PKCS #10 request: " +
+ std::to_string(version));
+
+ cert_req_info.decode(data->m_subject_dn);
+
+ BER_Object public_key = cert_req_info.get_next_object();
+ if(public_key.is_a(SEQUENCE, CONSTRUCTED) == false)
+ throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for public key", public_key.tagging());
+
+ data->m_public_key_bits = ASN1::put_in_sequence(public_key.bits(), public_key.length());
+
+ BER_Object attr_bits = cert_req_info.get_next_object();
+
+ std::set<std::string> pkcs9_email;
+
+ if(attr_bits.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ BER_Decoder attributes(attr_bits);
+ while(attributes.more_items())
+ {
+ Attribute attr;
+ attributes.decode(attr);
+
+ const OID& oid = attr.get_oid();
+ BER_Decoder value(attr.get_parameters());
+
+ if(oid == OID::from_string("PKCS9.EmailAddress"))
+ {
+ ASN1_String email;
+ value.decode(email);
+ pkcs9_email.insert(email.value());
+ }
+ else if(oid == OID::from_string("PKCS9.ChallengePassword"))
+ {
+ ASN1_String challenge_password;
+ value.decode(challenge_password);
+ data->m_challenge = challenge_password.value();
+ }
+ else if(oid == OID::from_string("PKCS9.ExtensionRequest"))
+ {
+ value.decode(data->m_extensions).verify_end();
+ }
+ }
+ attributes.verify_end();
+ }
+ else if(attr_bits.is_set())
+ throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for attributes", attr_bits.tagging());
+
+ cert_req_info.verify_end();
+
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_alt_name = ext->get_alt_name();
+ }
+
+ for(std::string email : pkcs9_email)
+ {
+ data->m_alt_name.add_attribute("RFC882", email);
+ }
+
+ return data;
+ }
+
+}
+
+void PKCS10_Request::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<PKCS10_Data> data = decode_pkcs10(signed_body());
+
+ m_data.reset(data.release());
+
+ if(!this->check_signature(subject_public_key()))
+ throw Decoding_Error("PKCS #10 request: Bad signature detected");
+ }
+
+const PKCS10_Data& PKCS10_Request::data() const
+ {
+ if(m_data == nullptr)
+ throw Decoding_Error("PKCS10_Request decoding failed");
+ return *m_data.get();
+ }
+
+/*
+* Return the challenge password (if any)
+*/
+std::string PKCS10_Request::challenge_password() const
+ {
+ return data().m_challenge;
+ }
+
+/*
+* Return the name of the requestor
+*/
+const X509_DN& PKCS10_Request::subject_dn() const
+ {
+ return data().m_subject_dn;
+ }
+
+/*
+* Return the public key of the requestor
+*/
+const std::vector<uint8_t>& PKCS10_Request::raw_public_key() const
+ {
+ return data().m_public_key_bits;
+ }
+
+/*
+* Return the public key of the requestor
+*/
+Public_Key* PKCS10_Request::subject_public_key() const
+ {
+ DataSource_Memory source(raw_public_key());
+ return X509::load_key(source);
+ }
+
+/*
+* Return the alternative names of the requestor
+*/
+const AlternativeName& PKCS10_Request::subject_alt_name() const
+ {
+ return data().m_alt_name;
+ }
+
+/*
+* Return the X509v3 extensions
+*/
+const Extensions& PKCS10_Request::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+/*
+* Return the key constraints (if any)
+*/
+Key_Constraints PKCS10_Request::constraints() const
+ {
+ if(auto ext = extensions().get(OID::from_string("X509v3.KeyUsage")))
+ {
+ return dynamic_cast<Cert_Extension::Key_Usage&>(*ext).get_constraints();
+ }
+
+ return NO_CONSTRAINTS;
+ }
+
+/*
+* Return the extendend key constraints (if any)
+*/
+std::vector<OID> PKCS10_Request::ex_constraints() const
+ {
+ if(auto ext = extensions().get(OID::from_string("X509v3.ExtendedKeyUsage")))
+ {
+ return dynamic_cast<Cert_Extension::Extended_Key_Usage&>(*ext).get_oids();
+ }
+
+ return {};
+ }
+
+/*
+* Return is a CA certificate is requested
+*/
+bool PKCS10_Request::is_CA() const
+ {
+ if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints")))
+ {
+ return dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext).get_is_ca();
+ }
+
+ return false;
+ }
+
+/*
+* Return the desired path limit (if any)
+*/
+size_t PKCS10_Request::path_limit() const
+ {
+ if(auto ext = extensions().get(OID::from_string("X509v3.BasicConstraints")))
+ {
+ Cert_Extension::Basic_Constraints& basic_constraints = dynamic_cast<Cert_Extension::Basic_Constraints&>(*ext);
+ if(basic_constraints.get_is_ca())
+ {
+ return basic_constraints.get_path_limit();
+ }
+ }
+
+ return 0;
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/pkcs10.h b/comm/third_party/botan/src/lib/x509/pkcs10.h
new file mode 100644
index 0000000000..3f3f87357d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkcs10.h
@@ -0,0 +1,148 @@
+/*
+* PKCS #10
+* (C) 1999-2007 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKCS10_H_
+#define BOTAN_PKCS10_H_
+
+#include <botan/x509_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+struct PKCS10_Data;
+
+class Private_Key;
+class Extensions;
+class X509_DN;
+class AlternativeName;
+
+/**
+* PKCS #10 Certificate Request.
+*/
+class BOTAN_PUBLIC_API(2,0) PKCS10_Request final : public X509_Object
+ {
+ public:
+ /**
+ * Get the subject public key.
+ * @return subject public key
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Get the raw DER encoded public key.
+ * @return raw DER encoded public key
+ */
+ const std::vector<uint8_t>& raw_public_key() const;
+
+ /**
+ * Get the subject DN.
+ * @return subject DN
+ */
+ const X509_DN& subject_dn() const;
+
+ /**
+ * Get the subject alternative name.
+ * @return subject alternative name.
+ */
+ const AlternativeName& subject_alt_name() const;
+
+ /**
+ * Get the key constraints for the key associated with this
+ * PKCS#10 object.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the extendend key constraints (if any).
+ * @return extended key constraints
+ */
+ std::vector<OID> ex_constraints() const;
+
+ /**
+ * Find out whether this is a CA request.
+ * @result true if it is a CA request, false otherwise.
+ */
+ bool is_CA() const;
+
+ /**
+ * Return the constraint on the path length defined
+ * in the BasicConstraints extension.
+ * @return path limit
+ */
+ size_t path_limit() const;
+
+ /**
+ * Get the challenge password for this request
+ * @return challenge password for this request
+ */
+ std::string challenge_password() const;
+
+ /**
+ * Get the X509v3 extensions.
+ * @return X509v3 extensions
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Create a PKCS#10 Request from a data source.
+ * @param source the data source providing the DER encoded request
+ */
+ explicit PKCS10_Request(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a PKCS#10 Request from a file.
+ * @param filename the name of the file containing the DER or PEM
+ * encoded request file
+ */
+ explicit PKCS10_Request(const std::string& filename);
+#endif
+
+ /**
+ * Create a PKCS#10 Request from binary data.
+ * @param vec a std::vector containing the DER value
+ */
+ explicit PKCS10_Request(const std::vector<uint8_t>& vec);
+
+ /**
+ * Create a new PKCS10 certificate request
+ * @param key the key that will be included in the certificate request
+ * @param subject_dn the DN to be placed in the request
+ * @param extensions extensions to include in the request
+ * @param hash_fn the hash function to use to create the signature
+ * @param rng a random number generator
+ * @param padding_scheme if set specifies the padding scheme, otherwise an
+ * algorithm-specific default is used.
+ * @param challenge a challenge string to be included in the PKCS10 request,
+ * sometimes used for revocation purposes.
+ */
+ static PKCS10_Request create(const Private_Key& key,
+ const X509_DN& subject_dn,
+ const Extensions& extensions,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng,
+ const std::string& padding_scheme = "",
+ const std::string& challenge = "");
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const PKCS10_Data& data() const;
+
+ std::shared_ptr<PKCS10_Data> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkix_enums.h b/comm/third_party/botan/src/lib/x509/pkix_enums.h
new file mode 100644
index 0000000000..a1c85293b8
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkix_enums.h
@@ -0,0 +1,143 @@
+/*
+* (C) 2013 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_PKIX_ENUMS_H_
+#define BOTAN_X509_PKIX_ENUMS_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* Certificate validation status code
+*/
+enum class Certificate_Status_Code {
+ OK = 0,
+ VERIFIED = 0,
+
+ // Revocation status
+ OCSP_RESPONSE_GOOD = 1,
+ OCSP_SIGNATURE_OK = 2,
+ VALID_CRL_CHECKED = 3,
+ OCSP_NO_HTTP = 4,
+
+ // Warnings
+ FIRST_WARNING_STATUS = 500,
+ CERT_SERIAL_NEGATIVE = 500,
+ DN_TOO_LONG = 501,
+ OCSP_NO_REVOCATION_URL = 502,
+ OCSP_SERVER_NOT_AVAILABLE = 503,
+
+ // Typo versions of above - will be removed in future major release
+ OSCP_NO_REVOCATION_URL = 502,
+ OSCP_SERVER_NOT_AVAILABLE = 503,
+
+ // Errors
+ FIRST_ERROR_STATUS = 1000,
+
+ SIGNATURE_METHOD_TOO_WEAK = 1000,
+ UNTRUSTED_HASH = 1001,
+ NO_REVOCATION_DATA = 1002,
+ NO_MATCHING_CRLDP = 1003,
+
+ // Time problems
+ CERT_NOT_YET_VALID = 2000,
+ CERT_HAS_EXPIRED = 2001,
+ OCSP_NOT_YET_VALID = 2002,
+ OCSP_HAS_EXPIRED = 2003,
+ CRL_NOT_YET_VALID = 2004,
+ CRL_HAS_EXPIRED = 2005,
+ OCSP_IS_TOO_OLD = 2006,
+
+ // Chain generation problems
+ CERT_ISSUER_NOT_FOUND = 3000,
+ CANNOT_ESTABLISH_TRUST = 3001,
+ CERT_CHAIN_LOOP = 3002,
+ CHAIN_LACKS_TRUST_ROOT = 3003,
+ CHAIN_NAME_MISMATCH = 3004,
+
+ // Validation errors
+ POLICY_ERROR = 4000,
+ INVALID_USAGE = 4001,
+ CERT_CHAIN_TOO_LONG = 4002,
+ CA_CERT_NOT_FOR_CERT_ISSUER = 4003,
+ NAME_CONSTRAINT_ERROR = 4004,
+
+ // Revocation errors
+ CA_CERT_NOT_FOR_CRL_ISSUER = 4005,
+ OCSP_CERT_NOT_LISTED = 4006,
+ OCSP_BAD_STATUS = 4007,
+
+ // Other problems
+ CERT_NAME_NOMATCH = 4008,
+ UNKNOWN_CRITICAL_EXTENSION = 4009,
+ DUPLICATE_CERT_EXTENSION = 4010,
+ OCSP_SIGNATURE_ERROR = 4501,
+ OCSP_ISSUER_NOT_FOUND = 4502,
+ OCSP_RESPONSE_MISSING_KEYUSAGE = 4503,
+ OCSP_RESPONSE_INVALID = 4504,
+ EXT_IN_V1_V2_CERT = 4505,
+ DUPLICATE_CERT_POLICY = 4506,
+ V2_IDENTIFIERS_IN_V1_CERT = 4507,
+
+ // Hard failures
+ CERT_IS_REVOKED = 5000,
+ CRL_BAD_SIGNATURE = 5001,
+ SIGNATURE_ERROR = 5002,
+ CERT_PUBKEY_INVALID = 5003,
+ SIGNATURE_ALGO_UNKNOWN = 5004,
+ SIGNATURE_ALGO_BAD_PARAMS = 5005
+};
+
+/**
+* Convert a status code to a human readable diagnostic message
+* @param code the certifcate status
+* @return string literal constant, or nullptr if code unknown
+*/
+BOTAN_PUBLIC_API(2,0) const char* to_string(Certificate_Status_Code code);
+
+/**
+* X.509v3 Key Constraints.
+* If updating update copy in ffi.h
+*/
+enum Key_Constraints {
+ NO_CONSTRAINTS = 0,
+ DIGITAL_SIGNATURE = 1 << 15,
+ NON_REPUDIATION = 1 << 14,
+ KEY_ENCIPHERMENT = 1 << 13,
+ DATA_ENCIPHERMENT = 1 << 12,
+ KEY_AGREEMENT = 1 << 11,
+ KEY_CERT_SIGN = 1 << 10,
+ CRL_SIGN = 1 << 9,
+ ENCIPHER_ONLY = 1 << 8,
+ DECIPHER_ONLY = 1 << 7
+};
+
+/**
+* X.509v2 CRL Reason Code.
+* This will become an enum class in a future major release
+*/
+enum CRL_Code : uint32_t {
+ UNSPECIFIED = 0,
+ KEY_COMPROMISE = 1,
+ CA_COMPROMISE = 2,
+ AFFILIATION_CHANGED = 3,
+ SUPERSEDED = 4,
+ CESSATION_OF_OPERATION = 5,
+ CERTIFICATE_HOLD = 6,
+ REMOVE_FROM_CRL = 8,
+ PRIVLEDGE_WITHDRAWN = 9,
+ PRIVILEGE_WITHDRAWN = 9,
+ AA_COMPROMISE = 10,
+
+ DELETE_CRL_ENTRY = 0xFF00,
+ OCSP_GOOD = 0xFF01,
+ OCSP_UNKNOWN = 0xFF02
+};
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/pkix_types.h b/comm/third_party/botan/src/lib/x509/pkix_types.h
new file mode 100644
index 0000000000..983121955f
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/pkix_types.h
@@ -0,0 +1,613 @@
+/*
+* (C) 1999-2010,2012,2018,2020 Jack Lloyd
+* (C) 2007 Yves Jerschow
+* (C) 2015 Kai Michaelis
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_PKIX_TYPES_H_
+#define BOTAN_PKIX_TYPES_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+#include <string>
+#include <iosfwd>
+#include <map>
+#include <set>
+
+namespace Botan {
+
+class X509_Certificate;
+class Data_Store;
+class Public_Key;
+
+/**
+* Check that key constraints are permitted for a specific public key.
+* @param pub_key the public key on which the constraints shall be enforced on
+* @param constraints the constraints that shall be enforced on the key
+* @throw Invalid_Argument if the given constraints are not permitted for this key
+*/
+BOTAN_PUBLIC_API(2,0) void verify_cert_constraints_valid_for_key_type(const Public_Key& pub_key,
+ Key_Constraints constraints);
+
+std::string BOTAN_PUBLIC_API(2,0) key_constraints_to_string(Key_Constraints);
+
+/**
+* Distinguished Name
+*/
+class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object
+ {
+ public:
+ X509_DN() = default;
+
+ explicit X509_DN(const std::multimap<OID, std::string>& args)
+ {
+ for(auto i : args)
+ add_attribute(i.first, i.second);
+ }
+
+ explicit X509_DN(const std::multimap<std::string, std::string>& args)
+ {
+ for(auto i : args)
+ add_attribute(i.first, i.second);
+ }
+
+ void encode_into(DER_Encoder&) const override;
+ void decode_from(BER_Decoder&) override;
+
+ bool has_field(const OID& oid) const;
+ ASN1_String get_first_attribute(const OID& oid) const;
+
+ /*
+ * Return the BER encoded data, if any
+ */
+ const std::vector<uint8_t>& get_bits() const { return m_dn_bits; }
+
+ bool empty() const { return m_rdn.empty(); }
+
+ std::string to_string() const;
+
+ const std::vector<std::pair<OID,ASN1_String>>& dn_info() const { return m_rdn; }
+
+ std::multimap<OID, std::string> get_attributes() const;
+ std::multimap<std::string, std::string> contents() const;
+
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+ std::string get_first_attribute(const std::string& attr) const;
+
+ void add_attribute(const std::string& key, const std::string& val);
+
+ void add_attribute(const OID& oid, const std::string& val)
+ {
+ add_attribute(oid, ASN1_String(val));
+ }
+
+ void add_attribute(const OID& oid, const ASN1_String& val);
+
+ static std::string deref_info_field(const std::string& key);
+
+ /**
+ * Lookup upper bounds in characters for the length of distinguished name fields
+ * as given in RFC 5280, Appendix A.
+ *
+ * @param oid the oid of the DN to lookup
+ * @return the upper bound, or zero if no ub is known to Botan
+ */
+ static size_t lookup_ub(const OID& oid);
+
+ private:
+ std::vector<std::pair<OID,ASN1_String>> m_rdn;
+ std::vector<uint8_t> m_dn_bits;
+ };
+
+bool BOTAN_PUBLIC_API(2,0) operator==(const X509_DN& dn1, const X509_DN& dn2);
+bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_DN& dn1, const X509_DN& dn2);
+
+/*
+The ordering here is arbitrary and may change from release to release.
+It is intended for allowing DNs as keys in std::map and similiar containers
+*/
+bool BOTAN_PUBLIC_API(2,0) operator<(const X509_DN& dn1, const X509_DN& dn2);
+
+BOTAN_PUBLIC_API(2,0) std::ostream& operator<<(std::ostream& out, const X509_DN& dn);
+BOTAN_PUBLIC_API(2,0) std::istream& operator>>(std::istream& in, X509_DN& dn);
+
+/**
+* Alternative Name
+*/
+class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object
+ {
+ public:
+ void encode_into(DER_Encoder&) const override;
+ void decode_from(BER_Decoder&) override;
+
+ std::multimap<std::string, std::string> contents() const;
+
+ bool has_field(const std::string& attr) const;
+ std::vector<std::string> get_attribute(const std::string& attr) const;
+
+ std::string get_first_attribute(const std::string& attr) const;
+
+ void add_attribute(const std::string& type, const std::string& value);
+ void add_othername(const OID& oid, const std::string& value, ASN1_Tag type);
+
+ const std::multimap<std::string, std::string>& get_attributes() const
+ {
+ return m_alt_info;
+ }
+
+ const std::multimap<OID, ASN1_String>& get_othernames() const
+ {
+ return m_othernames;
+ }
+
+ X509_DN dn() const;
+
+ bool has_items() const;
+
+ AlternativeName(const std::string& email_addr = "",
+ const std::string& uri = "",
+ const std::string& dns = "",
+ const std::string& ip_address = "");
+ private:
+ std::multimap<std::string, std::string> m_alt_info;
+ std::multimap<OID, ASN1_String> m_othernames;
+ };
+
+/**
+* Attribute
+*/
+class BOTAN_PUBLIC_API(2,0) Attribute final : public ASN1_Object
+ {
+ public:
+ void encode_into(DER_Encoder& to) const override;
+ void decode_from(BER_Decoder& from) override;
+
+ Attribute() = default;
+ Attribute(const OID&, const std::vector<uint8_t>&);
+ Attribute(const std::string&, const std::vector<uint8_t>&);
+
+ const OID& get_oid() const { return oid; }
+
+ const std::vector<uint8_t>& get_parameters() const { return parameters; }
+
+ BOTAN_DEPRECATED_PUBLIC_MEMBER_VARIABLES:
+ /*
+ * These values are public for historical reasons, but in a future release
+ * they will be made private. Do not access them.
+ */
+ OID oid;
+ std::vector<uint8_t> parameters;
+ };
+
+/**
+* @brief X.509 GeneralName Type
+*
+* Handles parsing GeneralName types in their BER and canonical string
+* encoding. Allows matching GeneralNames against each other using
+* the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
+ {
+ public:
+ enum MatchResult : int
+ {
+ All,
+ Some,
+ None,
+ NotFound,
+ UnknownType,
+ };
+
+ /**
+ * Creates an empty GeneralName.
+ */
+ GeneralName() = default;
+
+ /**
+ * Creates a new GeneralName for its string format.
+ * @param str type and name, colon-separated, e.g., "DNS:google.com"
+ */
+ GeneralName(const std::string& str);
+
+ void encode_into(DER_Encoder&) const override;
+
+ void decode_from(BER_Decoder&) override;
+
+ /**
+ * @return Type of the name. Can be DN, DNS, IP, RFC822 or URI.
+ */
+ const std::string& type() const { return m_type; }
+
+ /**
+ * @return The name as string. Format depends on type.
+ */
+ const std::string& name() const { return m_name; }
+
+ /**
+ * Checks whether a given certificate (partially) matches this name.
+ * @param cert certificate to be matched
+ * @return the match result
+ */
+ MatchResult matches(const X509_Certificate& cert) const;
+
+ private:
+ std::string m_type;
+ std::string m_name;
+
+ bool matches_dns(const std::string&) const;
+ bool matches_dn(const std::string&) const;
+ bool matches_ip(const std::string&) const;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+
+/**
+* @brief A single Name Constraint
+*
+* The Name Constraint extension adds a minimum and maximum path
+* length to a GeneralName to form a constraint. The length limits
+* are currently unused.
+*/
+class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
+ {
+ public:
+ /**
+ * Creates an empty name constraint.
+ */
+ GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+ {}
+
+ /***
+ * Creates a new name constraint.
+ * @param base name
+ * @param min minimum path length
+ * @param max maximum path length
+ */
+ GeneralSubtree(const GeneralName& base, size_t min, size_t max)
+ : m_base(base), m_minimum(min), m_maximum(max)
+ {}
+
+ /**
+ * Creates a new name constraint for its string format.
+ * @param str name constraint
+ */
+ GeneralSubtree(const std::string& str);
+
+ void encode_into(DER_Encoder&) const override;
+
+ void decode_from(BER_Decoder&) override;
+
+ /**
+ * @return name
+ */
+ const GeneralName& base() const { return m_base; }
+
+ /**
+ * @return minimum path length
+ */
+ size_t minimum() const { return m_minimum; }
+
+ /**
+ * @return maximum path length
+ */
+ size_t maximum() const { return m_maximum; }
+
+ private:
+ GeneralName m_base;
+ size_t m_minimum;
+ size_t m_maximum;
+ };
+
+std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs);
+
+/**
+* @brief Name Constraints
+*
+* Wraps the Name Constraints associated with a certificate.
+*/
+class BOTAN_PUBLIC_API(2,0) NameConstraints final
+ {
+ public:
+ /**
+ * Creates an empty name NameConstraints.
+ */
+ NameConstraints() : m_permitted_subtrees(), m_excluded_subtrees() {}
+
+ /**
+ * Creates NameConstraints from a list of permitted and excluded subtrees.
+ * @param permitted_subtrees names for which the certificate is permitted
+ * @param excluded_subtrees names for which the certificate is not permitted
+ */
+ NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+ std::vector<GeneralSubtree>&& excluded_subtrees)
+ : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+ {}
+
+ /**
+ * @return permitted names
+ */
+ const std::vector<GeneralSubtree>& permitted() const { return m_permitted_subtrees; }
+
+ /**
+ * @return excluded names
+ */
+ const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+
+ private:
+ std::vector<GeneralSubtree> m_permitted_subtrees;
+ std::vector<GeneralSubtree> m_excluded_subtrees;
+ };
+
+/**
+* X.509 Certificate Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Extension
+ {
+ public:
+ /**
+ * @return OID representing this extension
+ */
+ virtual OID oid_of() const = 0;
+
+ /*
+ * @return specific OID name
+ * If possible OIDS table should match oid_name to OIDS, ie
+ * OID::from_string(ext->oid_name()) == ext->oid_of()
+ * Should return empty string if OID is not known
+ */
+ virtual std::string oid_name() const = 0;
+
+ /**
+ * Make a copy of this extension
+ * @return copy of this
+ */
+ virtual Certificate_Extension* copy() const = 0;
+
+ /*
+ * Add the contents of this extension into the information
+ * for the subject and/or issuer, as necessary.
+ * @param subject the subject info
+ * @param issuer the issuer info
+ */
+ virtual void contents_to(Data_Store& subject,
+ Data_Store& issuer) const = 0;
+
+ /*
+ * Callback visited during path validation.
+ *
+ * An extension can implement this callback to inspect
+ * the path during path validation.
+ *
+ * If an error occurs during validation of this extension,
+ * an appropriate status code shall be added to cert_status.
+ *
+ * @param subject Subject certificate that contains this extension
+ * @param issuer Issuer certificate
+ * @param status Certificate validation status codes for subject certificate
+ * @param cert_path Certificate path which is currently validated
+ * @param pos Position of subject certificate in cert_path
+ */
+ virtual void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos);
+
+ virtual ~Certificate_Extension() = default;
+ protected:
+ friend class Extensions;
+ virtual bool should_encode() const { return true; }
+ virtual std::vector<uint8_t> encode_inner() const = 0;
+ virtual void decode_inner(const std::vector<uint8_t>&) = 0;
+ };
+
+/**
+* X.509 Certificate Extension List
+*/
+class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object
+ {
+ public:
+ /**
+ * Look up an object in the extensions, based on OID Returns
+ * nullptr if not set, if the extension was either absent or not
+ * handled. The pointer returned is owned by the Extensions
+ * object.
+ * This would be better with an optional<T> return value
+ */
+ const Certificate_Extension* get_extension_object(const OID& oid) const;
+
+ template<typename T>
+ const T* get_extension_object_as(const OID& oid = T::static_oid()) const
+ {
+ if(const Certificate_Extension* extn = get_extension_object(oid))
+ {
+ // Unknown_Extension oid_name is empty
+ if(extn->oid_name().empty())
+ {
+ return nullptr;
+ }
+ else if(const T* extn_as_T = dynamic_cast<const T*>(extn))
+ {
+ return extn_as_T;
+ }
+ else
+ {
+ throw Decoding_Error("Exception::get_extension_object_as dynamic_cast failed");
+ }
+ }
+
+ return nullptr;
+ }
+
+ /**
+ * Return the set of extensions in the order they appeared in the certificate
+ * (or as they were added, if constructed)
+ */
+ const std::vector<OID>& get_extension_oids() const
+ {
+ return m_extension_oids;
+ }
+
+ /**
+ * Return true if an extension was set
+ */
+ bool extension_set(const OID& oid) const;
+
+ /**
+ * Return true if an extesion was set and marked critical
+ */
+ bool critical_extension_set(const OID& oid) const;
+
+ /**
+ * Return the raw bytes of the extension
+ * Will throw if OID was not set as an extension.
+ */
+ std::vector<uint8_t> get_extension_bits(const OID& oid) const;
+
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+ void contents_to(Data_Store&, Data_Store&) const;
+
+ /**
+ * Adds a new extension to the list.
+ * @param extn pointer to the certificate extension (Extensions takes ownership)
+ * @param critical whether this extension should be marked as critical
+ * @throw Invalid_Argument if the extension is already present in the list
+ */
+ void add(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Adds a new extension to the list unless it already exists. If the extension
+ * already exists within the Extensions object, the extn pointer will be deleted.
+ *
+ * @param extn pointer to the certificate extension (Extensions takes ownership)
+ * @param critical whether this extension should be marked as critical
+ * @return true if the object was added false if the extension was already used
+ */
+ bool add_new(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Adds an extension to the list or replaces it.
+ * @param extn the certificate extension
+ * @param critical whether this extension should be marked as critical
+ */
+ void replace(Certificate_Extension* extn, bool critical = false);
+
+ /**
+ * Remove an extension from the list. Returns true if the
+ * extension had been set, false otherwise.
+ */
+ bool remove(const OID& oid);
+
+ /**
+ * Searches for an extension by OID and returns the result.
+ * Only the known extensions types declared in this header
+ * are searched for by this function.
+ * @return Copy of extension with oid, nullptr if not found.
+ * Can avoid creating a copy by using get_extension_object function
+ */
+ std::unique_ptr<Certificate_Extension> get(const OID& oid) const;
+
+ /**
+ * Searches for an extension by OID and returns the result decoding
+ * it to some arbitrary extension type chosen by the application.
+ *
+ * Only the unknown extensions, that is, extensions types that
+ * are not declared in this header, are searched for by this
+ * function.
+ *
+ * @return Pointer to new extension with oid, nullptr if not found.
+ */
+ template<typename T>
+ std::unique_ptr<T> get_raw(const OID& oid) const
+ {
+ auto extn_info = m_extension_info.find(oid);
+
+ if(extn_info != m_extension_info.end())
+ {
+ // Unknown_Extension oid_name is empty
+ if(extn_info->second.obj().oid_name() == "")
+ {
+ std::unique_ptr<T> ext(new T);
+ ext->decode_inner(extn_info->second.bits());
+ return ext;
+ }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Returns a copy of the list of extensions together with the corresponding
+ * criticality flag. All extensions are encoded as some object, falling back
+ * to Unknown_Extension class which simply allows reading the bytes as well
+ * as the criticality flag.
+ */
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> extensions() const;
+
+ /**
+ * Returns the list of extensions as raw, encoded bytes
+ * together with the corresponding criticality flag.
+ * Contains all extensions, including any extensions encoded as Unknown_Extension
+ */
+ std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const;
+
+ Extensions() {}
+
+ Extensions(const Extensions&) = default;
+ Extensions& operator=(const Extensions&) = default;
+
+ Extensions(Extensions&&) = default;
+ Extensions& operator=(Extensions&&) = default;
+
+ private:
+ static std::unique_ptr<Certificate_Extension>
+ create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body);
+
+ class Extensions_Info
+ {
+ public:
+ Extensions_Info(bool critical,
+ Certificate_Extension* ext) :
+ m_obj(ext),
+ m_bits(m_obj->encode_inner()),
+ m_critical(critical)
+ {
+ }
+
+ Extensions_Info(bool critical,
+ const std::vector<uint8_t>& encoding,
+ Certificate_Extension* ext) :
+ m_obj(ext),
+ m_bits(encoding),
+ m_critical(critical)
+ {
+ }
+
+ bool is_critical() const { return m_critical; }
+ const std::vector<uint8_t>& bits() const { return m_bits; }
+ const Certificate_Extension& obj() const
+ {
+ BOTAN_ASSERT_NONNULL(m_obj.get());
+ return *m_obj.get();
+ }
+
+ private:
+ std::shared_ptr<Certificate_Extension> m_obj;
+ std::vector<uint8_t> m_bits;
+ bool m_critical = false;
+ };
+
+ std::vector<OID> m_extension_oids;
+ std::map<OID, Extensions_Info> m_extension_info;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_attribute.cpp b/comm/third_party/botan/src/lib/x509/x509_attribute.cpp
new file mode 100644
index 0000000000..035f254c88
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_attribute.cpp
@@ -0,0 +1,58 @@
+/*
+* Attribute
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const OID& attr_oid, const std::vector<uint8_t>& attr_value) :
+ oid(attr_oid),
+ parameters(attr_value)
+ {}
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const std::string& attr_oid,
+ const std::vector<uint8_t>& attr_value) :
+ oid(OID::from_string(attr_oid)),
+ parameters(attr_value)
+ {}
+
+/*
+* DER encode a Attribute
+*/
+void Attribute::encode_into(DER_Encoder& codec) const
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(oid)
+ .start_cons(SET)
+ .raw_bytes(parameters)
+ .end_cons()
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded Attribute
+*/
+void Attribute::decode_from(BER_Decoder& codec)
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(oid)
+ .start_cons(SET)
+ .raw_bytes(parameters)
+ .end_cons()
+ .end_cons();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ca.cpp b/comm/third_party/botan/src/lib/x509/x509_ca.cpp
new file mode 100644
index 0000000000..542a3a9191
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ca.cpp
@@ -0,0 +1,338 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ca.h>
+#include <botan/x509_key.h>
+#include <botan/x509self.h>
+#include <botan/x509_ext.h>
+#include <botan/pkix_types.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/bigint.h>
+#include <botan/parsing.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/emsa.h>
+#include <botan/scan_name.h>
+#include <algorithm>
+#include <iterator>
+
+namespace Botan {
+
+/*
+* Load the certificate and private key
+*/
+X509_CA::X509_CA(const X509_Certificate& c,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) :
+ m_ca_cert(c),
+ m_hash_fn(hash_fn)
+ {
+ if(!m_ca_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ std::map<std::string,std::string> opts;
+ // constructor without additional options: use the padding used in the CA certificate
+ // sig_oid_str = <sig_alg>/<padding>, so padding with all its options will look
+ // like a cipher mode to the scanner
+ std::string sig_oid_str = OIDS::oid2str_or_throw(c.signature_algorithm().get_oid());
+ SCAN_Name scanner(sig_oid_str);
+ std::string pad = scanner.cipher_mode();
+ if(!pad.empty())
+ opts.insert({"padding",pad});
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+ }
+
+/*
+* Load the certificate and private key, and additional options
+*/
+X509_CA::X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng) : m_ca_cert(ca_certificate), m_hash_fn(hash_fn)
+ {
+ if(!m_ca_cert.is_CA_cert())
+ throw Invalid_Argument("X509_CA: This certificate is not for a CA");
+
+ m_signer.reset(choose_sig_format(key, opts, rng, hash_fn, m_ca_sig_algo));
+ }
+
+/*
+* X509_CA Destructor
+*/
+X509_CA::~X509_CA()
+ {
+ /* for unique_ptr */
+ }
+
+namespace {
+
+Extensions choose_extensions(const PKCS10_Request& req,
+ const X509_Certificate& ca_cert,
+ const std::string& hash_fn)
+ {
+ Key_Constraints constraints;
+ if(req.is_CA())
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ std::unique_ptr<Public_Key> key(req.subject_public_key());
+ verify_cert_constraints_valid_for_key_type(*key, req.constraints());
+ constraints = req.constraints();
+ }
+
+ Extensions extensions = req.extensions();
+
+ extensions.replace(
+ new Cert_Extension::Basic_Constraints(req.is_CA(), req.path_limit()),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.replace(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ extensions.replace(new Cert_Extension::Authority_Key_ID(ca_cert.subject_key_id()));
+ extensions.replace(new Cert_Extension::Subject_Key_ID(req.raw_public_key(), hash_fn));
+
+ extensions.replace(
+ new Cert_Extension::Subject_Alternative_Name(req.subject_alt_name()));
+
+ extensions.replace(
+ new Cert_Extension::Extended_Key_Usage(req.ex_constraints()));
+
+ return extensions;
+ }
+
+}
+
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const
+ {
+ auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+ return make_cert(m_signer.get(), rng, serial_number,
+ m_ca_sig_algo, req.raw_public_key(),
+ not_before, not_after,
+ m_ca_cert.subject_dn(), req.subject_dn(),
+ extensions);
+ }
+
+/*
+* Sign a PKCS #10 certificate request
+*/
+X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const
+ {
+ auto extensions = choose_extensions(req, m_ca_cert, m_hash_fn);
+
+ return make_cert(m_signer.get(), rng, m_ca_sig_algo,
+ req.raw_public_key(),
+ not_before, not_after,
+ m_ca_cert.subject_dn(), req.subject_dn(),
+ extensions);
+ }
+
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions)
+ {
+ const size_t SERIAL_BITS = 128;
+ BigInt serial_no(rng, SERIAL_BITS);
+
+ return make_cert(signer, rng, serial_no, sig_algo, pub_key,
+ not_before, not_after, issuer_dn, subject_dn, extensions);
+ }
+
+/*
+* Create a new certificate
+*/
+X509_Certificate X509_CA::make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_no,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions)
+ {
+ const size_t X509_CERT_VERSION = 3;
+
+ // clang-format off
+ return X509_Certificate(X509_Object::make_signed(
+ signer, rng, sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .start_explicit(0)
+ .encode(X509_CERT_VERSION-1)
+ .end_explicit()
+
+ .encode(serial_no)
+
+ .encode(sig_algo)
+ .encode(issuer_dn)
+
+ .start_cons(SEQUENCE)
+ .encode(not_before)
+ .encode(not_after)
+ .end_cons()
+
+ .encode(subject_dn)
+ .raw_bytes(pub_key)
+
+ .start_explicit(3)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents()
+ ));
+ // clang-format on
+ }
+
+/*
+* Create a new, empty CRL
+*/
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+ uint32_t next_update) const
+ {
+ return new_crl(rng,
+ std::chrono::system_clock::now(),
+ std::chrono::seconds(next_update));
+ }
+
+/*
+* Update a CRL with new entries
+*/
+X509_CRL X509_CA::update_crl(const X509_CRL& crl,
+ const std::vector<CRL_Entry>& new_revoked,
+ RandomNumberGenerator& rng,
+ uint32_t next_update) const
+ {
+ return update_crl(crl, new_revoked, rng,
+ std::chrono::system_clock::now(),
+ std::chrono::seconds(next_update));
+ }
+
+
+X509_CRL X509_CA::new_crl(RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ std::vector<CRL_Entry> empty;
+ return make_crl(empty, 1, rng, issue_time, next_update);
+ }
+
+X509_CRL X509_CA::update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_revoked,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ std::vector<CRL_Entry> revoked = last_crl.get_revoked();
+
+ std::copy(new_revoked.begin(), new_revoked.end(),
+ std::back_inserter(revoked));
+
+ return make_crl(revoked, last_crl.crl_number() + 1, rng, issue_time, next_update);
+ }
+
+/*
+* Create a CRL
+*/
+X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
+ uint32_t crl_number,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const
+ {
+ const size_t X509_CRL_VERSION = 2;
+
+ auto expire_time = issue_time + next_update;
+
+ Extensions extensions;
+ extensions.add(new Cert_Extension::Authority_Key_ID(m_ca_cert.subject_key_id()));
+ extensions.add(new Cert_Extension::CRL_Number(crl_number));
+
+ // clang-format off
+ const std::vector<uint8_t> crl = X509_Object::make_signed(
+ m_signer.get(), rng, m_ca_sig_algo,
+ DER_Encoder().start_cons(SEQUENCE)
+ .encode(X509_CRL_VERSION-1)
+ .encode(m_ca_sig_algo)
+ .encode(m_ca_cert.subject_dn())
+ .encode(X509_Time(issue_time))
+ .encode(X509_Time(expire_time))
+ .encode_if(revoked.size() > 0,
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode_list(revoked)
+ .end_cons()
+ )
+ .start_explicit(0)
+ .start_cons(SEQUENCE)
+ .encode(extensions)
+ .end_cons()
+ .end_explicit()
+ .end_cons()
+ .get_contents());
+ // clang-format on
+
+ return X509_CRL(crl);
+ }
+
+/*
+* Return the CA's certificate
+*/
+X509_Certificate X509_CA::ca_certificate() const
+ {
+ return m_ca_cert;
+ }
+
+/*
+* Choose a signing format for the key
+*/
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, "").release();
+ }
+
+PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& sig_algo)
+ {
+ std::string padding;
+ if(opts.count("padding"))
+ padding = opts.at("padding");
+ return X509_Object::choose_sig_format(sig_algo, key, rng, hash_fn, padding).release();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ca.h b/comm/third_party/botan/src/lib/x509/x509_ca.h
new file mode 100644
index 0000000000..20e9b1bccf
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ca.h
@@ -0,0 +1,261 @@
+/*
+* X.509 Certificate Authority
+* (C) 1999-2008 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CA_H_
+#define BOTAN_X509_CA_H_
+
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <chrono>
+#include <map>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+class BigInt;
+class Private_Key;
+class PKCS10_Request;
+class PK_Signer;
+
+/**
+* This class represents X.509 Certificate Authorities (CAs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CA final
+ {
+ public:
+ /**
+ * Sign a PKCS#10 Request.
+ * @param req the request to sign
+ * @param rng the rng to use
+ * @param not_before the starting time for the certificate
+ * @param not_after the expiration time for the certificate
+ * @return resulting certificate
+ */
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const;
+
+ /**
+ * Sign a PKCS#10 Request.
+ * @param req the request to sign
+ * @param rng the rng to use
+ * @param serial_number the serial number the cert will be assigned.
+ * @param not_before the starting time for the certificate
+ * @param not_after the expiration time for the certificate
+ * @return resulting certificate
+ */
+ X509_Certificate sign_request(const PKCS10_Request& req,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const X509_Time& not_before,
+ const X509_Time& not_after) const;
+
+ /**
+ * Get the certificate of this CA.
+ * @return CA certificate
+ */
+ X509_Certificate ca_certificate() const;
+
+ /**
+ * Create a new and empty CRL for this CA.
+ * @param rng the random number generator to use
+ * @param issue_time the issue time (typically system_clock::now)
+ * @param next_update the time interval after issue_data within which
+ * a new CRL will be produced.
+ * @return new CRL
+ */
+ X509_CRL new_crl(RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ /**
+ * Create a new CRL by with additional entries.
+ * @param last_crl the last CRL of this CA to add the new entries to
+ * @param new_entries contains the new CRL entries to be added to the CRL
+ * @param rng the random number generator to use
+ * @param issue_time the issue time (typically system_clock::now)
+ * @param next_update the time interval after issue_data within which
+ * a new CRL will be produced.
+ */
+ X509_CRL update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_entries,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ /**
+ * Create a new and empty CRL for this CA.
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ * @return new CRL
+ */
+ X509_CRL new_crl(RandomNumberGenerator& rng,
+ uint32_t next_update = 604800) const;
+
+ /**
+ * Create a new CRL by with additional entries.
+ * @param last_crl the last CRL of this CA to add the new entries to
+ * @param new_entries contains the new CRL entries to be added to the CRL
+ * @param rng the random number generator to use
+ * @param next_update the time to set in next update in seconds
+ * as the offset from the current time
+ */
+ X509_CRL update_crl(const X509_CRL& last_crl,
+ const std::vector<CRL_Entry>& new_entries,
+ RandomNumberGenerator& rng,
+ uint32_t next_update = 604800) const;
+
+ /**
+ * Interface for creating new certificates
+ * @param signer a signing object
+ * @param rng a random number generator
+ * @param sig_algo the signature algorithm identifier
+ * @param pub_key the serialized public key
+ * @param not_before the start time of the certificate
+ * @param not_after the end time of the certificate
+ * @param issuer_dn the DN of the issuer
+ * @param subject_dn the DN of the subject
+ * @param extensions an optional list of certificate extensions
+ * @returns newly minted certificate
+ */
+ static X509_Certificate make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions);
+
+ /**
+ * Interface for creating new certificates
+ * @param signer a signing object
+ * @param rng a random number generator
+ * @param serial_number the serial number the cert will be assigned
+ * @param sig_algo the signature algorithm identifier
+ * @param pub_key the serialized public key
+ * @param not_before the start time of the certificate
+ * @param not_after the end time of the certificate
+ * @param issuer_dn the DN of the issuer
+ * @param subject_dn the DN of the subject
+ * @param extensions an optional list of certificate extensions
+ * @returns newly minted certificate
+ */
+ static X509_Certificate make_cert(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const BigInt& serial_number,
+ const AlgorithmIdentifier& sig_algo,
+ const std::vector<uint8_t>& pub_key,
+ const X509_Time& not_before,
+ const X509_Time& not_after,
+ const X509_DN& issuer_dn,
+ const X509_DN& subject_dn,
+ const Extensions& extensions);
+
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Create a new CA object.
+ * @param ca_certificate the certificate of the CA
+ * @param key the private key of the CA
+ * @param opts additional options, e.g. padding, as key value pairs
+ * @param hash_fn name of a hash function to use for signing
+ * @param rng the random generator to use
+ */
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ BOTAN_DEPRECATED("Use version taking RNG object")
+ X509_CA(const X509_Certificate& ca_certificate,
+ const Private_Key& key,
+ const std::string& hash_fn) :
+ X509_CA(ca_certificate, key, hash_fn, system_rng())
+ {}
+#endif
+
+ X509_CA(const X509_CA&) = delete;
+ X509_CA& operator=(const X509_CA&) = delete;
+
+ X509_CA(X509_CA&&) = default;
+ X509_CA& operator=(X509_CA&&) = default;
+
+ ~X509_CA();
+
+ private:
+ X509_CRL make_crl(const std::vector<CRL_Entry>& entries,
+ uint32_t crl_number,
+ RandomNumberGenerator& rng,
+ std::chrono::system_clock::time_point issue_time,
+ std::chrono::seconds next_update) const;
+
+ AlgorithmIdentifier m_ca_sig_algo;
+ X509_Certificate m_ca_cert;
+ std::string m_hash_fn;
+ std::unique_ptr<PK_Signer> m_signer;
+ };
+
+/**
+* Choose the default signature format for a certain public key signature
+* scheme.
+* @param key will be the key to choose a padding scheme for
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+BOTAN_PUBLIC_API(2,0) PK_Signer* choose_sig_format(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
+/**
+* @verbatim
+* Choose the default signature format for a certain public key signature
+* scheme.
+*
+* The only option recognized by opts at this moment is "padding"
+* Find an entry from src/build-data/oids.txt under [signature] of the form
+* <sig_algo>/<padding>[(<hash_algo>)] and add {"padding",<padding>}
+* to opts.
+* @endverbatim
+*
+* @param key will be the key to choose a padding scheme for
+* @param opts contains additional options for building the certificate
+* @param rng the random generator to use
+* @param hash_fn is the desired hash function
+* @param alg_id will be set to the chosen scheme
+* @return A PK_Signer object for generating signatures
+*/
+PK_Signer* choose_sig_format(const Private_Key& key,
+ const std::map<std::string,std::string>& opts,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ AlgorithmIdentifier& alg_id);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_crl.cpp b/comm/third_party/botan/src/lib/x509/x509_crl.cpp
new file mode 100644
index 0000000000..1b5df1a083
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_crl.cpp
@@ -0,0 +1,268 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_crl.h>
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/ber_dec.h>
+
+#include <sstream>
+
+namespace Botan {
+
+struct CRL_Data
+ {
+ X509_DN m_issuer;
+ X509_Time m_this_update;
+ X509_Time m_next_update;
+ std::vector<CRL_Entry> m_entries;
+ Extensions m_extensions;
+
+ // cached values from extensions
+ size_t m_crl_number = 0;
+ std::vector<uint8_t> m_auth_key_id;
+ std::string m_issuing_distribution_point;
+ };
+
+std::string X509_CRL::PEM_label() const
+ {
+ return "X509 CRL";
+ }
+
+std::vector<std::string> X509_CRL::alternate_PEM_labels() const
+ {
+ return { "CRL" };
+ }
+
+X509_CRL::X509_CRL(DataSource& src)
+ {
+ load_data(src);
+ }
+
+X509_CRL::X509_CRL(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_CRL::X509_CRL(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+X509_CRL::X509_CRL(const X509_DN& issuer,
+ const X509_Time& this_update,
+ const X509_Time& next_update,
+ const std::vector<CRL_Entry>& revoked) :
+ X509_Object()
+ {
+ m_data.reset(new CRL_Data);
+ m_data->m_issuer = issuer;
+ m_data->m_this_update = this_update;
+ m_data->m_next_update = next_update;
+ m_data->m_entries = revoked;
+ }
+
+/**
+* Check if this particular certificate is listed in the CRL
+*/
+bool X509_CRL::is_revoked(const X509_Certificate& cert) const
+ {
+ /*
+ If the cert wasn't issued by the CRL issuer, it's possible the cert
+ is revoked, but not by this CRL. Maybe throw an exception instead?
+ */
+ if(cert.issuer_dn() != issuer_dn())
+ return false;
+
+ std::vector<uint8_t> crl_akid = authority_key_id();
+ std::vector<uint8_t> cert_akid = cert.authority_key_id();
+
+ if(!crl_akid.empty() && !cert_akid.empty())
+ {
+ if(crl_akid != cert_akid)
+ return false;
+ }
+
+ std::vector<uint8_t> cert_serial = cert.serial_number();
+
+ bool is_revoked = false;
+
+ // FIXME would be nice to avoid a linear scan here - maybe sort the entries?
+ for(const CRL_Entry& entry : get_revoked())
+ {
+ if(cert_serial == entry.serial_number())
+ {
+ if(entry.reason_code() == REMOVE_FROM_CRL)
+ is_revoked = false;
+ else
+ is_revoked = true;
+ }
+ }
+
+ return is_revoked;
+ }
+
+/*
+* Decode the TBSCertList data
+*/
+namespace {
+
+std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
+ const AlgorithmIdentifier& sig_algo)
+ {
+ std::unique_ptr<CRL_Data> data(new CRL_Data);
+
+ BER_Decoder tbs_crl(body);
+
+ size_t version;
+ tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+
+ if(version != 0 && version != 1)
+ throw X509_CRL::X509_CRL_Error("Unknown X.509 CRL version " +
+ std::to_string(version+1));
+
+ AlgorithmIdentifier sig_algo_inner;
+ tbs_crl.decode(sig_algo_inner);
+
+ if(sig_algo != sig_algo_inner)
+ throw X509_CRL::X509_CRL_Error("Algorithm identifier mismatch");
+
+ tbs_crl.decode(data->m_issuer)
+ .decode(data->m_this_update)
+ .decode(data->m_next_update);
+
+ BER_Object next = tbs_crl.get_next_object();
+
+ if(next.is_a(SEQUENCE, CONSTRUCTED))
+ {
+ BER_Decoder cert_list(std::move(next));
+
+ while(cert_list.more_items())
+ {
+ CRL_Entry entry;
+ cert_list.decode(entry);
+ data->m_entries.push_back(entry);
+ }
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ BER_Decoder crl_options(std::move(next));
+ crl_options.decode(data->m_extensions).verify_end();
+ next = tbs_crl.get_next_object();
+ }
+
+ if(next.is_set())
+ throw X509_CRL::X509_CRL_Error("Unknown tag in CRL");
+
+ tbs_crl.verify_end();
+
+ // Now cache some fields from the extensions
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Number>())
+ {
+ data->m_crl_number = ext->get_crl_number();
+ }
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+ {
+ data->m_auth_key_id = ext->get_key_id();
+ }
+ if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Issuing_Distribution_Point>())
+ {
+ std::stringstream ss;
+
+ for(const auto& pair : ext->get_point().contents())
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+ data->m_issuing_distribution_point = ss.str();
+ }
+
+ return data;
+ }
+
+}
+
+void X509_CRL::force_decode()
+ {
+ m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
+ }
+
+const CRL_Data& X509_CRL::data() const
+ {
+ if(!m_data)
+ {
+ throw Invalid_State("X509_CRL uninitialized");
+ }
+ return *m_data.get();
+ }
+
+const Extensions& X509_CRL::extensions() const
+ {
+ return data().m_extensions;
+ }
+
+/*
+* Return the list of revoked certificates
+*/
+const std::vector<CRL_Entry>& X509_CRL::get_revoked() const
+ {
+ return data().m_entries;
+ }
+
+/*
+* Return the distinguished name of the issuer
+*/
+const X509_DN& X509_CRL::issuer_dn() const
+ {
+ return data().m_issuer;
+ }
+
+/*
+* Return the key identifier of the issuer
+*/
+const std::vector<uint8_t>& X509_CRL::authority_key_id() const
+ {
+ return data().m_auth_key_id;
+ }
+
+/*
+* Return the CRL number of this CRL
+*/
+uint32_t X509_CRL::crl_number() const
+ {
+ return static_cast<uint32_t>(data().m_crl_number);
+ }
+
+/*
+* Return the issue data of the CRL
+*/
+const X509_Time& X509_CRL::this_update() const
+ {
+ return data().m_this_update;
+ }
+
+/*
+* Return the date when a new CRL will be issued
+*/
+const X509_Time& X509_CRL::next_update() const
+ {
+ return data().m_next_update;
+ }
+
+/*
+* Return the CRL's distribution point
+*/
+std::string X509_CRL::crl_issuing_distribution_point() const
+ {
+ return data().m_issuing_distribution_point;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_crl.h b/comm/third_party/botan/src/lib/x509/x509_crl.h
new file mode 100644
index 0000000000..6d4a301abd
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_crl.h
@@ -0,0 +1,209 @@
+/*
+* X.509 CRL
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CRL_H_
+#define BOTAN_X509_CRL_H_
+
+#include <botan/x509_obj.h>
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+class Extensions;
+class X509_Certificate;
+class X509_DN;
+
+struct CRL_Entry_Data;
+struct CRL_Data;
+
+/**
+* This class represents CRL entries
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Entry final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ /**
+ * Get the serial number of the certificate associated with this entry.
+ * @return certificate's serial number
+ */
+ const std::vector<uint8_t>& serial_number() const;
+
+ /**
+ * Get the revocation date of the certificate associated with this entry
+ * @return certificate's revocation date
+ */
+ const X509_Time& expire_time() const;
+
+ /**
+ * Get the entries reason code
+ * @return reason code
+ */
+ CRL_Code reason_code() const;
+
+ /**
+ * Get the extensions on this CRL entry
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Create uninitialized CRL_Entry object
+ */
+ CRL_Entry() = default;
+
+ /**
+ * Construct an CRL entry.
+ * @param cert the certificate to revoke
+ * @param reason the reason code to set in the entry
+ */
+ CRL_Entry(const X509_Certificate& cert,
+ CRL_Code reason = UNSPECIFIED);
+
+ private:
+ friend class X509_CRL;
+
+ const CRL_Entry_Data& data() const;
+
+ std::shared_ptr<CRL_Entry_Data> m_data;
+ };
+
+/**
+* Test two CRL entries for equality in all fields.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator==(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* Test two CRL entries for inequality in at least one field.
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const CRL_Entry&, const CRL_Entry&);
+
+/**
+* This class represents X.509 Certificate Revocation Lists (CRLs).
+*/
+class BOTAN_PUBLIC_API(2,0) X509_CRL final : public X509_Object
+ {
+ public:
+ /**
+ * This class represents CRL related errors.
+ *
+ * In a future major release this exception type will be removed and
+ * replaced with Decoding_Error
+ */
+ class BOTAN_PUBLIC_API(2,0) X509_CRL_Error final : public Decoding_Error
+ {
+ public:
+ explicit X509_CRL_Error(const std::string& error) :
+ Decoding_Error("X509_CRL: " + error) {}
+ };
+
+ /**
+ * Check if this particular certificate is listed in the CRL
+ */
+ bool is_revoked(const X509_Certificate& cert) const;
+
+ /**
+ * Get the entries of this CRL in the form of a vector.
+ * @return vector containing the entries of this CRL.
+ */
+ const std::vector<CRL_Entry>& get_revoked() const;
+
+ /**
+ * Get the issuer DN of this CRL.
+ * @return CRLs issuer DN
+ */
+ const X509_DN& issuer_dn() const;
+
+ /**
+ * @return extension data for this CRL
+ */
+ const Extensions& extensions() const;
+
+ /**
+ * Get the AuthorityKeyIdentifier of this CRL.
+ * @return this CRLs AuthorityKeyIdentifier
+ */
+ const std::vector<uint8_t>& authority_key_id() const;
+
+ /**
+ * Get the serial number of this CRL.
+ * @return CRLs serial number
+ */
+ uint32_t crl_number() const;
+
+ /**
+ * Get the CRL's thisUpdate value.
+ * @return CRLs thisUpdate
+ */
+ const X509_Time& this_update() const;
+
+ /**
+ * Get the CRL's nextUpdate value.
+ * @return CRLs nextdUpdate
+ */
+ const X509_Time& next_update() const;
+
+ /**
+ * Get the CRL's distribution point
+ * @return CRL.IssuingDistributionPoint from the CRL's Data_Store
+ */
+ std::string crl_issuing_distribution_point() const;
+
+ /**
+ * Create an uninitialized CRL object. Any attempts to access
+ * this object will throw an exception.
+ */
+ X509_CRL() = default;
+
+ /**
+ * Construct a CRL from a data source.
+ * @param source the data source providing the DER or PEM encoded CRL.
+ */
+ X509_CRL(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Construct a CRL from a file containing the DER or PEM encoded CRL.
+ * @param filename the name of the CRL file
+ */
+ X509_CRL(const std::string& filename);
+#endif
+
+ /**
+ * Construct a CRL from a binary vector
+ * @param vec the binary (DER) representation of the CRL
+ */
+ X509_CRL(const std::vector<uint8_t>& vec);
+
+ /**
+ * Construct a CRL
+ * @param issuer issuer of this CRL
+ * @param thisUpdate valid from
+ * @param nextUpdate valid until
+ * @param revoked entries to be included in the CRL
+ */
+ X509_CRL(const X509_DN& issuer, const X509_Time& thisUpdate,
+ const X509_Time& nextUpdate, const std::vector<CRL_Entry>& revoked);
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const CRL_Data& data() const;
+
+ std::shared_ptr<CRL_Data> m_data;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn.cpp b/comm/third_party/botan/src/lib/x509/x509_dn.cpp
new file mode 100644
index 0000000000..99aad475a9
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn.cpp
@@ -0,0 +1,428 @@
+/*
+* X509_DN
+* (C) 1999-2007,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/internal/stl_util.h>
+#include <botan/oids.h>
+#include <ostream>
+#include <sstream>
+#include <cctype>
+
+namespace Botan {
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const std::string& type,
+ const std::string& str)
+ {
+ add_attribute(OID::from_string(type), str);
+ }
+
+/*
+* Add an attribute to a X509_DN
+*/
+void X509_DN::add_attribute(const OID& oid, const ASN1_String& str)
+ {
+ if(str.empty())
+ return;
+
+ m_rdn.push_back(std::make_pair(oid, str));
+ m_dn_bits.clear();
+ }
+
+/*
+* Get the attributes of this X509_DN
+*/
+std::multimap<OID, std::string> X509_DN::get_attributes() const
+ {
+ std::multimap<OID, std::string> retval;
+
+ for(auto& i : m_rdn)
+ multimap_insert(retval, i.first, i.second.value());
+ return retval;
+ }
+
+/*
+* Get the contents of this X.500 Name
+*/
+std::multimap<std::string, std::string> X509_DN::contents() const
+ {
+ std::multimap<std::string, std::string> retval;
+
+ for(auto& i : m_rdn)
+ {
+ multimap_insert(retval, i.first.to_formatted_string(), i.second.value());
+ }
+ return retval;
+ }
+
+bool X509_DN::has_field(const std::string& attr) const
+ {
+ const OID o = OIDS::str2oid_or_empty(deref_info_field(attr));
+ if(o.has_value())
+ return has_field(o);
+ else
+ return false;
+ }
+
+bool X509_DN::has_field(const OID& oid) const
+ {
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ return true;
+ }
+
+ return false;
+ }
+
+std::string X509_DN::get_first_attribute(const std::string& attr) const
+ {
+ const OID oid = OID::from_string(deref_info_field(attr));
+ return get_first_attribute(oid).value();
+ }
+
+ASN1_String X509_DN::get_first_attribute(const OID& oid) const
+ {
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ {
+ return i.second;
+ }
+ }
+
+ return ASN1_String();
+ }
+
+/*
+* Get a single attribute type
+*/
+std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
+ {
+ const OID oid = OID::from_string(deref_info_field(attr));
+
+ std::vector<std::string> values;
+
+ for(auto& i : m_rdn)
+ {
+ if(i.first == oid)
+ {
+ values.push_back(i.second.value());
+ }
+ }
+
+ return values;
+ }
+
+/*
+* Deref aliases in a subject/issuer info request
+*/
+std::string X509_DN::deref_info_field(const std::string& info)
+ {
+ if(info == "Name" || info == "CommonName" || info == "CN") return "X520.CommonName";
+ if(info == "SerialNumber" || info == "SN") return "X520.SerialNumber";
+ if(info == "Country" || info == "C") return "X520.Country";
+ if(info == "Organization" || info == "O") return "X520.Organization";
+ if(info == "Organizational Unit" || info == "OrgUnit" || info == "OU")
+ return "X520.OrganizationalUnit";
+ if(info == "Locality" || info == "L") return "X520.Locality";
+ if(info == "State" || info == "Province" || info == "ST") return "X520.State";
+ if(info == "Email") return "RFC822";
+ return info;
+ }
+
+/*
+* Compare two X509_DNs for equality
+*/
+bool operator==(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ auto attr1 = dn1.get_attributes();
+ auto attr2 = dn2.get_attributes();
+
+ if(attr1.size() != attr2.size()) return false;
+
+ auto p1 = attr1.begin();
+ auto p2 = attr2.begin();
+
+ while(true)
+ {
+ if(p1 == attr1.end() && p2 == attr2.end())
+ break;
+ if(p1 == attr1.end()) return false;
+ if(p2 == attr2.end()) return false;
+ if(p1->first != p2->first) return false;
+ if(!x500_name_cmp(p1->second, p2->second))
+ return false;
+ ++p1;
+ ++p2;
+ }
+ return true;
+ }
+
+/*
+* Compare two X509_DNs for inequality
+*/
+bool operator!=(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ return !(dn1 == dn2);
+ }
+
+/*
+* Induce an arbitrary ordering on DNs
+*/
+bool operator<(const X509_DN& dn1, const X509_DN& dn2)
+ {
+ auto attr1 = dn1.get_attributes();
+ auto attr2 = dn2.get_attributes();
+
+ // If they are not the same size, choose the smaller as the "lessor"
+ if(attr1.size() < attr2.size())
+ return true;
+ if(attr1.size() > attr2.size())
+ return false;
+
+ // We know they are the same # of elements, now compare the OIDs:
+ auto p1 = attr1.begin();
+ auto p2 = attr2.begin();
+
+ while(p1 != attr1.end() && p2 != attr2.end())
+ {
+ if(p1->first != p2->first)
+ {
+ return (p1->first < p2->first);
+ }
+
+ ++p1;
+ ++p2;
+ }
+
+ // We know this is true because maps have the same size
+ BOTAN_ASSERT_NOMSG(p1 == attr1.end());
+ BOTAN_ASSERT_NOMSG(p2 == attr2.end());
+
+ // Now we know all elements have the same OIDs, compare
+ // their string values:
+
+ p1 = attr1.begin();
+ p2 = attr2.begin();
+ while(p1 != attr1.end() && p2 != attr2.end())
+ {
+ BOTAN_DEBUG_ASSERT(p1->first == p2->first);
+
+ // They may be binary different but same by X.500 rules, check this
+ if(!x500_name_cmp(p1->second, p2->second))
+ {
+ // If they are not (by X.500) the same string, pick the
+ // lexicographic first as the lessor
+ return (p1->second < p2->second);
+ }
+
+ ++p1;
+ ++p2;
+ }
+
+ // if we reach here, then the DNs should be identical
+ BOTAN_DEBUG_ASSERT(dn1 == dn2);
+ return false;
+ }
+
+/*
+* DER encode a DistinguishedName
+*/
+void X509_DN::encode_into(DER_Encoder& der) const
+ {
+ der.start_cons(SEQUENCE);
+
+ if(!m_dn_bits.empty())
+ {
+ /*
+ If we decoded this from somewhere, encode it back exactly as
+ we received it
+ */
+ der.raw_bytes(m_dn_bits);
+ }
+ else
+ {
+ for(const auto& dn : m_rdn)
+ {
+ der.start_cons(SET)
+ .start_cons(SEQUENCE)
+ .encode(dn.first)
+ .encode(dn.second)
+ .end_cons()
+ .end_cons();
+ }
+ }
+
+ der.end_cons();
+ }
+
+/*
+* Decode a BER encoded DistinguishedName
+*/
+void X509_DN::decode_from(BER_Decoder& source)
+ {
+ std::vector<uint8_t> bits;
+
+ source.start_cons(SEQUENCE)
+ .raw_bytes(bits)
+ .end_cons();
+
+ BER_Decoder sequence(bits);
+
+ while(sequence.more_items())
+ {
+ BER_Decoder rdn = sequence.start_cons(SET);
+
+ while(rdn.more_items())
+ {
+ OID oid;
+ ASN1_String str;
+
+ rdn.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode(str) // TODO support Any
+ .end_cons();
+
+ add_attribute(oid, str);
+ }
+ }
+
+ m_dn_bits = bits;
+ }
+
+namespace {
+
+std::string to_short_form(const OID& oid)
+ {
+ const std::string long_id = oid.to_formatted_string();
+
+ if(long_id == "X520.CommonName")
+ return "CN";
+
+ if(long_id == "X520.Country")
+ return "C";
+
+ if(long_id == "X520.Organization")
+ return "O";
+
+ if(long_id == "X520.OrganizationalUnit")
+ return "OU";
+
+ return long_id;
+ }
+
+}
+
+std::string X509_DN::to_string() const
+ {
+ std::ostringstream out;
+ out << *this;
+ return out.str();
+ }
+
+std::ostream& operator<<(std::ostream& out, const X509_DN& dn)
+ {
+ auto info = dn.dn_info();
+
+ for(size_t i = 0; i != info.size(); ++i)
+ {
+ out << to_short_form(info[i].first) << "=\"";
+ for(char c : info[i].second.value())
+ {
+ if(c == '\\' || c == '\"')
+ {
+ out << "\\";
+ }
+ out << c;
+ }
+ out << "\"";
+
+ if(i + 1 < info.size())
+ {
+ out << ",";
+ }
+ }
+ return out;
+ }
+
+std::istream& operator>>(std::istream& in, X509_DN& dn)
+ {
+ in >> std::noskipws;
+ do
+ {
+ std::string key;
+ std::string val;
+ char c;
+
+ while(in.good())
+ {
+ in >> c;
+
+ if(std::isspace(c) && key.empty())
+ continue;
+ else if(!std::isspace(c))
+ {
+ key.push_back(c);
+ break;
+ }
+ else
+ break;
+ }
+
+ while(in.good())
+ {
+ in >> c;
+
+ if(!std::isspace(c) && c != '=')
+ key.push_back(c);
+ else if(c == '=')
+ break;
+ else
+ throw Invalid_Argument("Ill-formed X.509 DN");
+ }
+
+ bool in_quotes = false;
+ while(in.good())
+ {
+ in >> c;
+
+ if(std::isspace(c))
+ {
+ if(!in_quotes && !val.empty())
+ break;
+ else if(in_quotes)
+ val.push_back(' ');
+ }
+ else if(c == '"')
+ in_quotes = !in_quotes;
+ else if(c == '\\')
+ {
+ if(in.good())
+ in >> c;
+ val.push_back(c);
+ }
+ else if(c == ',' && !in_quotes)
+ break;
+ else
+ val.push_back(c);
+ }
+
+ if(!key.empty() && !val.empty())
+ dn.add_attribute(X509_DN::deref_info_field(key),val);
+ else
+ break;
+ }
+ while(in.good());
+ return in;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn.h b/comm/third_party/botan/src/lib/x509/x509_dn.h
new file mode 100644
index 0000000000..cd7beebe9c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn.h
@@ -0,0 +1,11 @@
+/*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_DN_H_
+#define BOTAN_X509_DN_H_
+
+#include <botan/pkix_types.h>
+BOTAN_DEPRECATED_HEADER(x509_dn.h)
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp b/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp
new file mode 100644
index 0000000000..b5c7645a13
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_dn_ub.cpp
@@ -0,0 +1,60 @@
+/*
+* DN_UB maps: Upper bounds on the length of DN strings
+*
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
+*
+* All manual edits to this file will be lost. Edit the script
+* then regenerate this source file.
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/pkix_types.h>
+#include <botan/asn1_obj.h>
+#include <map>
+
+namespace {
+
+/**
+ * Upper bounds for the length of distinguished name fields as given in RFC 5280, Appendix A.
+ * Only OIDS recognized by botan are considered, so far.
+ * Maps OID string representations instead of human readable strings in order
+ * to avoid an additional lookup.
+ */
+static const std::map<Botan::OID, size_t> DN_UB =
+ {
+ { Botan::OID({2,5,4,10}), 64 }, // X520.Organization
+ { Botan::OID({2,5,4,11}), 64 }, // X520.OrganizationalUnit
+ { Botan::OID({2,5,4,12}), 64 }, // X520.Title
+ { Botan::OID({2,5,4,3}), 64 }, // X520.CommonName
+ { Botan::OID({2,5,4,4}), 40 }, // X520.Surname
+ { Botan::OID({2,5,4,42}), 32768 }, // X520.GivenName
+ { Botan::OID({2,5,4,43}), 32768 }, // X520.Initials
+ { Botan::OID({2,5,4,44}), 32768 }, // X520.GenerationalQualifier
+ { Botan::OID({2,5,4,46}), 64 }, // X520.DNQualifier
+ { Botan::OID({2,5,4,5}), 64 }, // X520.SerialNumber
+ { Botan::OID({2,5,4,6}), 3 }, // X520.Country
+ { Botan::OID({2,5,4,65}), 128 }, // X520.Pseudonym
+ { Botan::OID({2,5,4,7}), 128 }, // X520.Locality
+ { Botan::OID({2,5,4,8}), 128 }, // X520.State
+ { Botan::OID({2,5,4,9}), 128 } // X520.StreetAddress
+ };
+}
+
+namespace Botan {
+
+//static
+size_t X509_DN::lookup_ub(const OID& oid)
+ {
+ auto ub_entry = DN_UB.find(oid);
+ if(ub_entry != DN_UB.end())
+ {
+ return ub_entry->second;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
diff --git a/comm/third_party/botan/src/lib/x509/x509_ext.cpp b/comm/third_party/botan/src/lib/x509/x509_ext.cpp
new file mode 100644
index 0000000000..e81e15c187
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ext.cpp
@@ -0,0 +1,1023 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2010,2012 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_ext.h>
+#include <botan/x509cert.h>
+#include <botan/datastor.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/hash.h>
+#include <botan/loadstor.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+#include <set>
+#include <sstream>
+
+namespace Botan {
+
+/*
+* Create a Certificate_Extension object of some kind to handle
+*/
+std::unique_ptr<Certificate_Extension>
+Extensions::create_extn_obj(const OID& oid,
+ bool critical,
+ const std::vector<uint8_t>& body)
+ {
+ const std::string oid_str = oid.to_string();
+
+ std::unique_ptr<Certificate_Extension> extn;
+
+ if(oid == Cert_Extension::Subject_Key_ID::static_oid())
+ {
+ extn.reset(new Cert_Extension::Subject_Key_ID);
+ }
+ else if(oid == Cert_Extension::Key_Usage::static_oid())
+ {
+ extn.reset(new Cert_Extension::Key_Usage);
+ }
+ else if(oid == Cert_Extension::Subject_Alternative_Name::static_oid())
+ {
+ extn.reset(new Cert_Extension::Subject_Alternative_Name);
+ }
+ else if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid())
+ {
+ extn.reset(new Cert_Extension::Issuer_Alternative_Name);
+ }
+ else if(oid == Cert_Extension::Basic_Constraints::static_oid())
+ {
+ extn.reset(new Cert_Extension::Basic_Constraints);
+ }
+ else if(oid == Cert_Extension::CRL_Number::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Number);
+ }
+ else if(oid == Cert_Extension::CRL_ReasonCode::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_ReasonCode);
+ }
+ else if(oid == Cert_Extension::Authority_Key_ID::static_oid())
+ {
+ extn.reset(new Cert_Extension::Authority_Key_ID);
+ }
+ else if(oid == Cert_Extension::Name_Constraints::static_oid())
+ {
+ extn.reset(new Cert_Extension::Name_Constraints);
+ }
+ else if(oid == Cert_Extension::CRL_Distribution_Points::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Distribution_Points);
+ }
+ else if(oid == Cert_Extension::CRL_Issuing_Distribution_Point::static_oid())
+ {
+ extn.reset(new Cert_Extension::CRL_Issuing_Distribution_Point);
+ }
+ else if(oid == Cert_Extension::Certificate_Policies::static_oid())
+ {
+ extn.reset(new Cert_Extension::Certificate_Policies);
+ }
+ else if(oid == Cert_Extension::Extended_Key_Usage::static_oid())
+ {
+ extn.reset(new Cert_Extension::Extended_Key_Usage);
+ }
+ else if(oid == Cert_Extension::Authority_Information_Access::static_oid())
+ {
+ extn.reset(new Cert_Extension::Authority_Information_Access);
+ }
+ else
+ {
+ // some other unknown extension type
+ extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+ }
+
+ try
+ {
+ extn->decode_inner(body);
+ }
+ catch(Decoding_Error&)
+ {
+ extn.reset(new Cert_Extension::Unknown_Extension(oid, critical));
+ extn->decode_inner(body);
+ }
+ return extn;
+ }
+
+/*
+* Validate the extension (the default implementation is a NOP)
+*/
+void Certificate_Extension::validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
+ std::vector<std::set<Certificate_Status_Code>>&,
+ size_t)
+ {
+ }
+
+/*
+* Add a new cert
+*/
+void Extensions::add(Certificate_Extension* extn, bool critical)
+ {
+ // sanity check: we don't want to have the same extension more than once
+ if(m_extension_info.count(extn->oid_of()) > 0)
+ {
+ const std::string name = extn->oid_name();
+ delete extn;
+ throw Invalid_Argument("Extension " + name + " already present in Extensions::add");
+ }
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+
+bool Extensions::add_new(Certificate_Extension* extn, bool critical)
+ {
+ if(m_extension_info.count(extn->oid_of()) > 0)
+ {
+ delete extn;
+ return false; // already exists
+ }
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ return true;
+ }
+
+bool Extensions::remove(const OID& oid)
+ {
+ const bool erased = m_extension_info.erase(oid) > 0;
+
+ if(erased)
+ {
+ m_extension_oids.erase(std::find(m_extension_oids.begin(), m_extension_oids.end(), oid));
+ }
+
+ return erased;
+ }
+
+void Extensions::replace(Certificate_Extension* extn, bool critical)
+ {
+ // Remove it if it existed
+ remove(extn->oid_of());
+
+ const OID oid = extn->oid_of();
+ Extensions_Info info(critical, extn);
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+
+bool Extensions::extension_set(const OID& oid) const
+ {
+ return (m_extension_info.find(oid) != m_extension_info.end());
+ }
+
+bool Extensions::critical_extension_set(const OID& oid) const
+ {
+ auto i = m_extension_info.find(oid);
+ if(i != m_extension_info.end())
+ return i->second.is_critical();
+ return false;
+ }
+
+std::vector<uint8_t> Extensions::get_extension_bits(const OID& oid) const
+ {
+ auto i = m_extension_info.find(oid);
+ if(i == m_extension_info.end())
+ throw Invalid_Argument("Extensions::get_extension_bits no such extension set");
+
+ return i->second.bits();
+ }
+
+const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const
+ {
+ auto extn = m_extension_info.find(oid);
+ if(extn == m_extension_info.end())
+ return nullptr;
+
+ return &extn->second.obj();
+ }
+
+std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const
+ {
+ if(const Certificate_Extension* ext = this->get_extension_object(oid))
+ {
+ return std::unique_ptr<Certificate_Extension>(ext->copy());
+ }
+ return nullptr;
+ }
+
+std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const
+ {
+ std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts;
+ for(auto&& ext : m_extension_info)
+ {
+ exts.push_back(
+ std::make_pair(
+ std::unique_ptr<Certificate_Extension>(ext.second.obj().copy()),
+ ext.second.is_critical())
+ );
+ }
+ return exts;
+ }
+
+std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw() const
+ {
+ std::map<OID, std::pair<std::vector<uint8_t>, bool>> out;
+ for(auto&& ext : m_extension_info)
+ {
+ out.emplace(ext.first,
+ std::make_pair(ext.second.bits(),
+ ext.second.is_critical()));
+ }
+ return out;
+ }
+
+/*
+* Encode an Extensions list
+*/
+void Extensions::encode_into(DER_Encoder& to_object) const
+ {
+ for(auto ext_info : m_extension_info)
+ {
+ const OID& oid = ext_info.first;
+ const bool should_encode = ext_info.second.obj().should_encode();
+
+ if(should_encode)
+ {
+ const bool is_critical = ext_info.second.is_critical();
+ const std::vector<uint8_t>& ext_value = ext_info.second.bits();
+
+ to_object.start_cons(SEQUENCE)
+ .encode(oid)
+ .encode_optional(is_critical, false)
+ .encode(ext_value, OCTET_STRING)
+ .end_cons();
+ }
+ }
+ }
+
+/*
+* Decode a list of Extensions
+*/
+void Extensions::decode_from(BER_Decoder& from_source)
+ {
+ m_extension_oids.clear();
+ m_extension_info.clear();
+
+ BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+
+ while(sequence.more_items())
+ {
+ OID oid;
+ bool critical;
+ std::vector<uint8_t> bits;
+
+ sequence.start_cons(SEQUENCE)
+ .decode(oid)
+ .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
+ .decode(bits, OCTET_STRING)
+ .end_cons();
+
+ std::unique_ptr<Certificate_Extension> obj = create_extn_obj(oid, critical, bits);
+ Extensions_Info info(critical, bits, obj.release());
+
+ m_extension_oids.push_back(oid);
+ m_extension_info.emplace(oid, info);
+ }
+ sequence.verify_end();
+ }
+
+/*
+* Write the extensions to an info store
+*/
+void Extensions::contents_to(Data_Store& subject_info,
+ Data_Store& issuer_info) const
+ {
+ for(auto&& m_extn_info : m_extension_info)
+ {
+ m_extn_info.second.obj().contents_to(subject_info, issuer_info);
+ subject_info.add(m_extn_info.second.obj().oid_name() + ".is_critical",
+ m_extn_info.second.is_critical());
+ }
+ }
+
+namespace Cert_Extension {
+
+/*
+* Checked accessor for the path_limit member
+*/
+size_t Basic_Constraints::get_path_limit() const
+ {
+ if(!m_is_ca)
+ throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA");
+ return m_path_limit;
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Basic_Constraints::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_if(m_is_ca,
+ DER_Encoder()
+ .encode(m_is_ca)
+ .encode_optional(m_path_limit, NO_CERT_PATH_LIMIT)
+ )
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false)
+ .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+ .end_cons();
+
+ if(m_is_ca == false)
+ m_path_limit = 0;
+ }
+
+/*
+* Return a textual representation
+*/
+void Basic_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.BasicConstraints.is_ca", (m_is_ca ? 1 : 0));
+ subject.add("X509v3.BasicConstraints.path_constraint", static_cast<uint32_t>(m_path_limit));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Key_Usage::encode_inner() const
+ {
+ if(m_constraints == NO_CONSTRAINTS)
+ throw Encoding_Error("Cannot encode zero usage constraints");
+
+ const size_t unused_bits = ctz(static_cast<uint32_t>(m_constraints));
+
+ std::vector<uint8_t> der;
+ der.push_back(BIT_STRING);
+ der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
+ der.push_back(unused_bits % 8);
+ der.push_back((m_constraints >> 8) & 0xFF);
+ if(m_constraints & 0xFF)
+ der.push_back(m_constraints & 0xFF);
+
+ return der;
+ }
+
+/*
+* Decode the extension
+*/
+void Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder ber(in);
+
+ BER_Object obj = ber.get_next_object();
+
+ obj.assert_is_a(BIT_STRING, UNIVERSAL, "usage constraint");
+
+ if(obj.length() != 2 && obj.length() != 3)
+ throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint");
+
+ uint16_t usage = 0;
+
+ const uint8_t* bits = obj.bits();
+
+ if(bits[0] >= 8)
+ throw BER_Decoding_Error("Invalid unused bits in usage constraint");
+
+ const uint8_t mask = static_cast<uint8_t>(0xFF << bits[0]);
+
+ if(obj.length() == 2)
+ {
+ usage = make_uint16(bits[1] & mask, 0);
+ }
+ else if(obj.length() == 3)
+ {
+ usage = make_uint16(bits[1], bits[2] & mask);
+ }
+
+ m_constraints = Key_Constraints(usage);
+ }
+
+/*
+* Return a textual representation
+*/
+void Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.KeyUsage", m_constraints);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Key_ID::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_key_id, OCTET_STRING);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Subject_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_key_id, OCTET_STRING).verify_end();
+ }
+
+/*
+* Return a textual representation
+*/
+void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ subject.add("X509v3.SubjectKeyIdentifier", m_key_id);
+ }
+
+/*
+* Subject_Key_ID Constructor
+*/
+Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key, const std::string& hash_name)
+ {
+ std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_name));
+
+ m_key_id.resize(hash->output_length());
+
+ hash->update(pub_key);
+ hash->final(m_key_id.data());
+
+ // Truncate longer hashes, 192 bits here seems plenty
+ const size_t max_skid_len = (192 / 8);
+ if(m_key_id.size() > max_skid_len)
+ m_key_id.resize(max_skid_len);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Authority_Key_ID::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode(m_key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Authority_Key_ID::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in)
+ .start_cons(SEQUENCE)
+ .decode_optional_string(m_key_id, OCTET_STRING, 0);
+ }
+
+/*
+* Return a textual representation
+*/
+void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const
+ {
+ if(m_key_id.size())
+ issuer.add("X509v3.AuthorityKeyIdentifier", m_key_id);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_alt_name);
+ return output;
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_alt_name);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
+
+/*
+* Decode the extension
+*/
+void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_alt_name);
+ }
+
+/*
+* Return a textual representation
+*/
+void Subject_Alternative_Name::contents_to(Data_Store& subject_info,
+ Data_Store&) const
+ {
+ subject_info.add(get_alt_name().contents());
+ }
+
+/*
+* Return a textual representation
+*/
+void Issuer_Alternative_Name::contents_to(Data_Store&, Data_Store& issuer_info) const
+ {
+ issuer_info.add(get_alt_name().contents());
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Extended_Key_Usage::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_list(m_oids)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Extended_Key_Usage::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode_list(m_oids);
+ }
+
+/*
+* Return a textual representation
+*/
+void Extended_Key_Usage::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ subject.add("X509v3.ExtendedKeyUsage", m_oids[i].to_string());
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Name_Constraints::encode_inner() const
+ {
+ throw Not_Implemented("Name_Constraints encoding");
+ }
+
+
+/*
+* Decode the extension
+*/
+void Name_Constraints::decode_inner(const std::vector<uint8_t>& in)
+ {
+ std::vector<GeneralSubtree> permit, exclude;
+ BER_Decoder ber(in);
+ BER_Decoder ext = ber.start_cons(SEQUENCE);
+ BER_Object per = ext.get_next_object();
+
+ ext.push_back(per);
+ if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(permit.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ BER_Object exc = ext.get_next_object();
+ ext.push_back(exc);
+ if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+ if(exclude.empty())
+ throw Encoding_Error("Empty Name Contraint list");
+ }
+
+ ext.end_cons();
+
+ if(permit.empty() && exclude.empty())
+ throw Encoding_Error("Empty Name Contraint extension");
+
+ m_name_constraints = NameConstraints(std::move(permit),std::move(exclude));
+ }
+
+/*
+* Return a textual representation
+*/
+void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ std::stringstream ss;
+
+ for(const GeneralSubtree& gs: m_name_constraints.permitted())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.permitted", ss.str());
+ ss.str(std::string());
+ }
+ for(const GeneralSubtree& gs: m_name_constraints.excluded())
+ {
+ ss << gs;
+ subject.add("X509v3.NameConstraints.excluded", ss.str());
+ ss.str(std::string());
+ }
+ }
+
+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+ {
+ if(!subject.is_CA_cert())
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+
+ const bool issuer_name_constraint_critical =
+ issuer.is_critical("X509v3.NameConstraints");
+
+ // Check that all subordinate certs pass the name constraint
+ for(size_t j = 0; j < pos; ++j)
+ {
+ bool permitted = m_name_constraints.permitted().empty();
+ bool failed = false;
+
+ for(auto c: m_name_constraints.permitted())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::NotFound:
+ case GeneralName::MatchResult::All:
+ permitted = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer_name_constraint_critical;
+ permitted = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for(auto c: m_name_constraints.excluded())
+ {
+ switch(c.base().matches(*cert_path.at(j)))
+ {
+ case GeneralName::MatchResult::All:
+ case GeneralName::MatchResult::Some:
+ failed = true;
+ break;
+ case GeneralName::MatchResult::UnknownType:
+ failed = issuer_name_constraint_critical;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(failed || !permitted)
+ {
+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+ }
+ }
+ }
+ }
+
+namespace {
+
+/*
+* A policy specifier
+*/
+class Policy_Information final : public ASN1_Object
+ {
+ public:
+ Policy_Information() = default;
+ explicit Policy_Information(const OID& oid) : m_oid(oid) {}
+
+ const OID& oid() const { return m_oid; }
+
+ void encode_into(DER_Encoder& codec) const override
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(m_oid)
+ .end_cons();
+ }
+
+ void decode_from(BER_Decoder& codec) override
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(m_oid)
+ .discard_remaining()
+ .end_cons();
+ }
+
+ private:
+ OID m_oid;
+ };
+
+}
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> Certificate_Policies::encode_inner() const
+ {
+ std::vector<Policy_Information> policies;
+
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ policies.push_back(Policy_Information(m_oids[i]));
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .encode_list(policies)
+ .end_cons();
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void Certificate_Policies::decode_inner(const std::vector<uint8_t>& in)
+ {
+ std::vector<Policy_Information> policies;
+
+ BER_Decoder(in).decode_list(policies);
+ m_oids.clear();
+ for(size_t i = 0; i != policies.size(); ++i)
+ m_oids.push_back(policies[i].oid());
+ }
+
+/*
+* Return a textual representation
+*/
+void Certificate_Policies::contents_to(Data_Store& info, Data_Store&) const
+ {
+ for(size_t i = 0; i != m_oids.size(); ++i)
+ info.add("X509v3.CertificatePolicies", m_oids[i].to_string());
+ }
+
+void Certificate_Policies::validate(
+ const X509_Certificate& /*subject*/,
+ const X509_Certificate& /*issuer*/,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& /*cert_path*/,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos)
+ {
+ std::set<OID> oid_set(m_oids.begin(), m_oids.end());
+ if(oid_set.size() != m_oids.size())
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::DUPLICATE_CERT_POLICY);
+ }
+ }
+
+std::vector<uint8_t> Authority_Information_Access::encode_inner() const
+ {
+ ASN1_String url(m_ocsp_responder, IA5_STRING);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .encode(OID::from_string("PKIX.OCSP"))
+ .add_object(ASN1_Tag(6), CONTEXT_SPECIFIC, url.value())
+ .end_cons()
+ .end_cons();
+ return output;
+ }
+
+void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder ber = BER_Decoder(in).start_cons(SEQUENCE);
+
+ while(ber.more_items())
+ {
+ OID oid;
+
+ BER_Decoder info = ber.start_cons(SEQUENCE);
+
+ info.decode(oid);
+
+ if(oid == OID::from_string("PKIX.OCSP"))
+ {
+ BER_Object name = info.get_next_object();
+
+ if(name.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_ocsp_responder = ASN1::to_string(name);
+ }
+
+ }
+ if(oid == OID::from_string("PKIX.CertificateAuthorityIssuers"))
+ {
+ BER_Object name = info.get_next_object();
+
+ if(name.is_a(6, CONTEXT_SPECIFIC))
+ {
+ m_ca_issuers.push_back(ASN1::to_string(name));
+ }
+ }
+ }
+ }
+
+void Authority_Information_Access::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ if(!m_ocsp_responder.empty())
+ subject.add("OCSP.responder", m_ocsp_responder);
+ for(const std::string& ca_issuer : m_ca_issuers)
+ subject.add("PKIX.CertificateAuthorityIssuers", ca_issuer);
+ }
+
+/*
+* Checked accessor for the crl_number member
+*/
+size_t CRL_Number::get_crl_number() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::get_crl_number: Not set");
+ return m_crl_number;
+ }
+
+/*
+* Copy a CRL_Number extension
+*/
+CRL_Number* CRL_Number::copy() const
+ {
+ if(!m_has_value)
+ throw Invalid_State("CRL_Number::copy: Not set");
+ return new CRL_Number(m_crl_number);
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_Number::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(m_crl_number);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_Number::decode_inner(const std::vector<uint8_t>& in)
+ {
+ BER_Decoder(in).decode(m_crl_number);
+ m_has_value = true;
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_Number::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLNumber", static_cast<uint32_t>(m_crl_number));
+ }
+
+/*
+* Encode the extension
+*/
+std::vector<uint8_t> CRL_ReasonCode::encode_inner() const
+ {
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(static_cast<size_t>(m_reason), ENUMERATED, UNIVERSAL);
+ return output;
+ }
+
+/*
+* Decode the extension
+*/
+void CRL_ReasonCode::decode_inner(const std::vector<uint8_t>& in)
+ {
+ size_t reason_code = 0;
+ BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+ m_reason = static_cast<CRL_Code>(reason_code);
+ }
+
+/*
+* Return a textual representation
+*/
+void CRL_ReasonCode::contents_to(Data_Store& info, Data_Store&) const
+ {
+ info.add("X509v3.CRLReasonCode", m_reason);
+ }
+
+std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf)
+ {
+ BER_Decoder(buf)
+ .decode_list(m_distribution_points)
+ .verify_end();
+
+ std::stringstream ss;
+
+ for(size_t i = 0; i != m_distribution_points.size(); ++i)
+ {
+ auto contents = m_distribution_points[i].point().contents();
+
+ for(const auto& pair : contents)
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+ }
+
+ m_crl_distribution_urls.push_back(ss.str());
+ }
+
+void CRL_Distribution_Points::contents_to(Data_Store& subject, Data_Store&) const
+ {
+ for(const std::string& crl_url : m_crl_distribution_urls)
+ subject.add("CRL.DistributionPoint", crl_url);
+ }
+
+void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&) const
+ {
+ throw Not_Implemented("CRL_Distribution_Points encoding");
+ }
+
+void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber)
+ {
+ ber.start_cons(SEQUENCE)
+ .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC)
+ .decode_optional_implicit(m_point, ASN1_Tag(0),
+ ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED),
+ SEQUENCE, CONSTRUCTED)
+ .end_cons().end_cons();
+ }
+
+std::vector<uint8_t> CRL_Issuing_Distribution_Point::encode_inner() const
+ {
+ throw Not_Implemented("CRL_Issuing_Distribution_Point encoding");
+ }
+
+void CRL_Issuing_Distribution_Point::decode_inner(const std::vector<uint8_t>& buf)
+ {
+ BER_Decoder(buf).decode(m_distribution_point).verify_end();
+ }
+
+void CRL_Issuing_Distribution_Point::contents_to(Data_Store& info, Data_Store&) const
+ {
+ auto contents = m_distribution_point.point().contents();
+ std::stringstream ss;
+
+ for(const auto& pair : contents)
+ {
+ ss << pair.first << ": " << pair.second << " ";
+ }
+
+ info.add("X509v3.CRLIssuingDistributionPoint", ss.str());
+ }
+
+std::vector<uint8_t> Unknown_Extension::encode_inner() const
+ {
+ return m_bytes;
+ }
+
+void Unknown_Extension::decode_inner(const std::vector<uint8_t>& bytes)
+ {
+ // Just treat as an opaque blob at this level
+ m_bytes = bytes;
+ }
+
+void Unknown_Extension::contents_to(Data_Store&, Data_Store&) const
+ {
+ // No information store
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_ext.h b/comm/third_party/botan/src/lib/x509/x509_ext.h
new file mode 100644
index 0000000000..cb6e064c9d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_ext.h
@@ -0,0 +1,529 @@
+/*
+* X.509 Certificate Extensions
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_EXTENSIONS_H_
+#define BOTAN_X509_EXTENSIONS_H_
+
+#include <botan/pkix_types.h>
+#include <set>
+
+namespace Botan {
+
+class Data_Store;
+class X509_Certificate;
+
+namespace Cert_Extension {
+
+static const size_t NO_CERT_PATH_LIMIT = 0xFFFFFFF0;
+
+/**
+* Basic Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Basic_Constraints final : public Certificate_Extension
+ {
+ public:
+ Basic_Constraints* copy() const override
+ { return new Basic_Constraints(m_is_ca, m_path_limit); }
+
+ Basic_Constraints(bool ca = false, size_t limit = 0) :
+ m_is_ca(ca), m_path_limit(limit) {}
+
+ bool get_is_ca() const { return m_is_ca; }
+ size_t get_path_limit() const;
+
+ static OID static_oid() { return OID("2.5.29.19"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.BasicConstraints"; }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_is_ca;
+ size_t m_path_limit;
+ };
+
+/**
+* Key Usage Constraints Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Key_Usage* copy() const override { return new Key_Usage(m_constraints); }
+
+ explicit Key_Usage(Key_Constraints c = NO_CONSTRAINTS) : m_constraints(c) {}
+
+ Key_Constraints get_constraints() const { return m_constraints; }
+
+ static OID static_oid() { return OID("2.5.29.15"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.KeyUsage"; }
+
+ bool should_encode() const override
+ { return (m_constraints != NO_CONSTRAINTS); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ Key_Constraints m_constraints;
+ };
+
+/**
+* Subject Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Subject_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Subject_Key_ID() = default;
+
+ explicit Subject_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+ Subject_Key_ID(const std::vector<uint8_t>& public_key,
+ const std::string& hash_fn);
+
+ Subject_Key_ID* copy() const override
+ { return new Subject_Key_ID(m_key_id); }
+
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.14"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+
+ std::string oid_name() const override
+ { return "X509v3.SubjectKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<uint8_t> m_key_id;
+ };
+
+/**
+* Authority Key Identifier Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extension
+ {
+ public:
+ Authority_Key_ID* copy() const override
+ { return new Authority_Key_ID(m_key_id); }
+
+ Authority_Key_ID() = default;
+ explicit Authority_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {}
+
+ const std::vector<uint8_t>& get_key_id() const { return m_key_id; }
+
+ static OID static_oid() { return OID("2.5.29.35"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.AuthorityKeyIdentifier"; }
+
+ bool should_encode() const override { return (m_key_id.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<uint8_t> m_key_id;
+ };
+
+/**
+* Subject Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,4) Subject_Alternative_Name final : public Certificate_Extension
+ {
+ public:
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+ static OID static_oid() { return OID("2.5.29.17"); }
+ OID oid_of() const override { return static_oid(); }
+
+ Subject_Alternative_Name* copy() const override
+ { return new Subject_Alternative_Name(get_alt_name()); }
+
+ explicit Subject_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
+
+ private:
+ std::string oid_name() const override { return "X509v3.SubjectAlternativeName"; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ AlternativeName m_alt_name;
+ };
+
+/**
+* Issuer Alternative Name Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Certificate_Extension
+ {
+ public:
+ const AlternativeName& get_alt_name() const { return m_alt_name; }
+
+ static OID static_oid() { return OID("2.5.29.18"); }
+ OID oid_of() const override { return static_oid(); }
+
+ Issuer_Alternative_Name* copy() const override
+ { return new Issuer_Alternative_Name(get_alt_name()); }
+
+ explicit Issuer_Alternative_Name(const AlternativeName& name = AlternativeName()) :
+ m_alt_name(name) {}
+
+ private:
+ std::string oid_name() const override { return "X509v3.IssuerAlternativeName"; }
+
+ bool should_encode() const override { return m_alt_name.has_items(); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ AlternativeName m_alt_name;
+ };
+
+/**
+* Extended Key Usage Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extension
+ {
+ public:
+ Extended_Key_Usage* copy() const override
+ { return new Extended_Key_Usage(m_oids); }
+
+ Extended_Key_Usage() = default;
+ explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {}
+
+ const std::vector<OID>& get_oids() const { return m_oids; }
+
+ static OID static_oid() { return OID("2.5.29.37"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.ExtendedKeyUsage"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+/**
+* Name Constraints
+*/
+class BOTAN_PUBLIC_API(2,0) Name_Constraints final : public Certificate_Extension
+ {
+ public:
+ Name_Constraints* copy() const override
+ { return new Name_Constraints(m_name_constraints); }
+
+ Name_Constraints() = default;
+ Name_Constraints(const NameConstraints &nc) : m_name_constraints(nc) {}
+
+ void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override;
+
+ const NameConstraints& get_name_constraints() const { return m_name_constraints; }
+
+ static OID static_oid() { return OID("2.5.29.30"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.NameConstraints"; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ NameConstraints m_name_constraints;
+ };
+
+/**
+* Certificate Policies Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Certificate_Policies final : public Certificate_Extension
+ {
+ public:
+ Certificate_Policies* copy() const override
+ { return new Certificate_Policies(m_oids); }
+
+ Certificate_Policies() = default;
+ explicit Certificate_Policies(const std::vector<OID>& o) : m_oids(o) {}
+
+ BOTAN_DEPRECATED("Use get_policy_oids")
+ std::vector<OID> get_oids() const { return m_oids; }
+
+ const std::vector<OID>& get_policy_oids() const { return m_oids; }
+
+ static OID static_oid() { return OID("2.5.29.32"); }
+ OID oid_of() const override { return static_oid(); }
+
+ void validate(const X509_Certificate& subject, const X509_Certificate& issuer,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override;
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CertificatePolicies"; }
+
+ bool should_encode() const override { return (m_oids.size() > 0); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<OID> m_oids;
+ };
+
+/**
+* Authority Information Access Extension
+*/
+class BOTAN_PUBLIC_API(2,0) Authority_Information_Access final : public Certificate_Extension
+ {
+ public:
+ Authority_Information_Access* copy() const override
+ { return new Authority_Information_Access(m_ocsp_responder, m_ca_issuers); }
+
+ Authority_Information_Access() = default;
+
+ explicit Authority_Information_Access(const std::string& ocsp, const std::vector<std::string>& ca_issuers = std::vector<std::string>()) :
+ m_ocsp_responder(ocsp), m_ca_issuers(ca_issuers) {}
+
+ std::string ocsp_responder() const { return m_ocsp_responder; }
+
+ static OID static_oid() { return OID("1.3.6.1.5.5.7.1.1"); }
+ OID oid_of() const override { return static_oid(); }
+ const std::vector<std::string> ca_issuers() const { return m_ca_issuers; }
+
+ private:
+ std::string oid_name() const override
+ { return "PKIX.AuthorityInformationAccess"; }
+
+ bool should_encode() const override { return (!m_ocsp_responder.empty()); }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::string m_ocsp_responder;
+ std::vector<std::string> m_ca_issuers;
+ };
+
+/**
+* CRL Number Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Number final : public Certificate_Extension
+ {
+ public:
+ CRL_Number* copy() const override;
+
+ CRL_Number() : m_has_value(false), m_crl_number(0) {}
+ CRL_Number(size_t n) : m_has_value(true), m_crl_number(n) {}
+
+ size_t get_crl_number() const;
+
+ static OID static_oid() { return OID("2.5.29.20"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.CRLNumber"; }
+
+ bool should_encode() const override { return m_has_value; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ bool m_has_value;
+ size_t m_crl_number;
+ };
+
+/**
+* CRL Entry Reason Code Extension
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_ReasonCode final : public Certificate_Extension
+ {
+ public:
+ CRL_ReasonCode* copy() const override
+ { return new CRL_ReasonCode(m_reason); }
+
+ explicit CRL_ReasonCode(CRL_Code r = UNSPECIFIED) : m_reason(r) {}
+
+ CRL_Code get_reason() const { return m_reason; }
+
+ static OID static_oid() { return OID("2.5.29.21"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override { return "X509v3.ReasonCode"; }
+
+ bool should_encode() const override { return (m_reason != UNSPECIFIED); }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ CRL_Code m_reason;
+ };
+
+/**
+* CRL Distribution Points Extension
+* todo enforce restrictions from RFC 5280 4.2.1.13
+*/
+class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_Extension
+ {
+ public:
+ class BOTAN_PUBLIC_API(2,0) Distribution_Point final : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const override;
+ void decode_from(class BER_Decoder&) override;
+
+ const AlternativeName& point() const { return m_point; }
+ private:
+ AlternativeName m_point;
+ };
+
+ CRL_Distribution_Points* copy() const override
+ { return new CRL_Distribution_Points(m_distribution_points); }
+
+ CRL_Distribution_Points() = default;
+
+ explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) :
+ m_distribution_points(points) {}
+
+ const std::vector<Distribution_Point>& distribution_points() const
+ { return m_distribution_points; }
+
+ const std::vector<std::string>& crl_distribution_urls() const
+ { return m_crl_distribution_urls; }
+
+ static OID static_oid() { return OID("2.5.29.31"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CRLDistributionPoints"; }
+
+ bool should_encode() const override
+ { return !m_distribution_points.empty(); }
+
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ std::vector<Distribution_Point> m_distribution_points;
+ std::vector<std::string> m_crl_distribution_urls;
+ };
+
+/**
+* CRL Issuing Distribution Point Extension
+* todo enforce restrictions from RFC 5280 5.2.5
+*/
+class CRL_Issuing_Distribution_Point final : public Certificate_Extension
+ {
+ public:
+ CRL_Issuing_Distribution_Point() = default;
+
+ explicit CRL_Issuing_Distribution_Point(const CRL_Distribution_Points::Distribution_Point& distribution_point) :
+ m_distribution_point(distribution_point) {}
+
+ CRL_Issuing_Distribution_Point* copy() const override
+ { return new CRL_Issuing_Distribution_Point(m_distribution_point); }
+
+ const AlternativeName& get_point() const
+ { return m_distribution_point.point(); }
+
+ static OID static_oid() { return OID("2.5.29.28"); }
+ OID oid_of() const override { return static_oid(); }
+
+ private:
+ std::string oid_name() const override
+ { return "X509v3.CRLIssuingDistributionPoint"; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ CRL_Distribution_Points::Distribution_Point m_distribution_point;
+ };
+
+/**
+* An unknown X.509 extension
+* Will add a failure to the path validation result, if critical
+*/
+class BOTAN_PUBLIC_API(2,4) Unknown_Extension final : public Certificate_Extension
+ {
+ public:
+ Unknown_Extension(const OID& oid, bool critical) :
+ m_oid(oid), m_critical(critical) {}
+
+ Unknown_Extension* copy() const override
+ { return new Unknown_Extension(m_oid, m_critical); }
+
+ /**
+ * Return the OID of this unknown extension
+ */
+ OID oid_of() const override
+ { return m_oid; }
+
+ //static_oid not defined for Unknown_Extension
+
+ /**
+ * Return the extension contents
+ */
+ const std::vector<uint8_t>& extension_contents() const { return m_bytes; }
+
+ /**
+ * Return if this extension was marked critical
+ */
+ bool is_critical_extension() const { return m_critical; }
+
+ void validate(const X509_Certificate&, const X509_Certificate&,
+ const std::vector<std::shared_ptr<const X509_Certificate>>&,
+ std::vector<std::set<Certificate_Status_Code>>& cert_status,
+ size_t pos) override
+ {
+ if(m_critical)
+ {
+ cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION);
+ }
+ }
+
+ private:
+ std::string oid_name() const override { return ""; }
+
+ bool should_encode() const override { return true; }
+ std::vector<uint8_t> encode_inner() const override;
+ void decode_inner(const std::vector<uint8_t>&) override;
+ void contents_to(Data_Store&, Data_Store&) const override;
+
+ OID m_oid;
+ bool m_critical;
+ std::vector<uint8_t> m_bytes;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509_obj.cpp b/comm/third_party/botan/src/lib/x509/x509_obj.cpp
new file mode 100644
index 0000000000..2bb67629a6
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_obj.cpp
@@ -0,0 +1,424 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007,2020 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509_obj.h>
+#include <botan/pubkey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/pem.h>
+#include <botan/emsa.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+struct Pss_params
+ {
+ AlgorithmIdentifier hash_algo;
+ AlgorithmIdentifier mask_gen_algo;
+ AlgorithmIdentifier mask_gen_hash; // redundant: decoded mask_gen_algo.parameters
+ size_t salt_len;
+ size_t trailer_field;
+ };
+
+Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params)
+ {
+ const AlgorithmIdentifier default_hash("SHA-160", AlgorithmIdentifier::USE_NULL_PARAM);
+ const AlgorithmIdentifier default_mgf("MGF1", default_hash.BER_encode());
+
+ Pss_params pss_parameter;
+ BER_Decoder(encoded_pss_params)
+ .start_cons(SEQUENCE)
+ .decode_optional(pss_parameter.hash_algo, ASN1_Tag(0), PRIVATE, default_hash)
+ .decode_optional(pss_parameter.mask_gen_algo, ASN1_Tag(1), PRIVATE, default_mgf)
+ .decode_optional(pss_parameter.salt_len, ASN1_Tag(2), PRIVATE, size_t(20))
+ .decode_optional(pss_parameter.trailer_field, ASN1_Tag(3), PRIVATE, size_t(1))
+ .end_cons();
+
+ BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash);
+
+ return pss_parameter;
+ }
+}
+
+/*
+* Read a PEM or BER X.509 object
+*/
+void X509_Object::load_data(DataSource& in)
+ {
+ try {
+ if(ASN1::maybe_BER(in) && !PEM_Code::matches(in))
+ {
+ BER_Decoder dec(in);
+ decode_from(dec);
+ }
+ else
+ {
+ std::string got_label;
+ DataSource_Memory ber(PEM_Code::decode(in, got_label));
+
+ if(got_label != PEM_label())
+ {
+ bool is_alternate = false;
+ for(std::string alt_label : alternate_PEM_labels())
+ {
+ if(got_label == alt_label)
+ {
+ is_alternate = true;
+ break;
+ }
+ }
+
+ if(!is_alternate)
+ throw Decoding_Error("Unexpected PEM label for " + PEM_label() + " of " + got_label);
+ }
+
+ BER_Decoder dec(ber);
+ decode_from(dec);
+ }
+ }
+ catch(Decoding_Error& e)
+ {
+ throw Decoding_Error(PEM_label() + " decoding", e);
+ }
+ }
+
+
+void X509_Object::encode_into(DER_Encoder& to) const
+ {
+ to.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(signed_body())
+ .end_cons()
+ .encode(signature_algorithm())
+ .encode(signature(), BIT_STRING)
+ .end_cons();
+ }
+
+/*
+* Read a BER encoded X.509 object
+*/
+void X509_Object::decode_from(BER_Decoder& from)
+ {
+ from.start_cons(SEQUENCE)
+ .start_cons(SEQUENCE)
+ .raw_bytes(m_tbs_bits)
+ .end_cons()
+ .decode(m_sig_algo)
+ .decode(m_sig, BIT_STRING)
+ .end_cons();
+
+ force_decode();
+ }
+
+/*
+* Return a PEM encoded X.509 object
+*/
+std::string X509_Object::PEM_encode() const
+ {
+ return PEM_Code::encode(BER_encode(), PEM_label());
+ }
+
+/*
+* Return the TBS data
+*/
+std::vector<uint8_t> X509_Object::tbs_data() const
+ {
+ return ASN1::put_in_sequence(m_tbs_bits);
+ }
+
+/*
+* Return the hash used in generating the signature
+*/
+std::string X509_Object::hash_used_for_signature() const
+ {
+ const OID& oid = m_sig_algo.get_oid();
+ const std::vector<std::string> sig_info = split_on(oid.to_formatted_string(), '/');
+
+ if(sig_info.size() == 1 && sig_info[0] == "Ed25519")
+ return "SHA-512";
+ else if(sig_info.size() != 2)
+ throw Internal_Error("Invalid name format found for " + oid.to_string());
+
+ if(sig_info[1] == "EMSA4")
+ {
+ const OID hash_oid = decode_pss_params(signature_algorithm().get_parameters()).hash_algo.get_oid();
+ return hash_oid.to_formatted_string();
+ }
+ else
+ {
+ const std::vector<std::string> pad_and_hash =
+ parse_algorithm_name(sig_info[1]);
+
+ if(pad_and_hash.size() != 2)
+ {
+ throw Internal_Error("Invalid name format " + sig_info[1]);
+ }
+
+ return pad_and_hash[1];
+ }
+ }
+
+/*
+* Check the signature on an object
+*/
+bool X509_Object::check_signature(const Public_Key* pub_key) const
+ {
+ if(!pub_key)
+ throw Invalid_Argument("No key provided for " + PEM_label() + " signature check");
+ std::unique_ptr<const Public_Key> key(pub_key);
+ return check_signature(*key);
+ }
+
+bool X509_Object::check_signature(const Public_Key& pub_key) const
+ {
+ const Certificate_Status_Code code = verify_signature(pub_key);
+ return (code == Certificate_Status_Code::VERIFIED);
+ }
+
+Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key) const
+ {
+ const std::vector<std::string> sig_info =
+ split_on(m_sig_algo.get_oid().to_formatted_string(), '/');
+
+ if(sig_info.size() < 1 || sig_info.size() > 2 || sig_info[0] != pub_key.algo_name())
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+ const std::string pub_key_algo = sig_info[0];
+ std::string padding;
+ if(sig_info.size() == 2)
+ padding = sig_info[1];
+ else if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+ padding = "Pure";
+ else
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+
+ const Signature_Format format = pub_key.default_x509_signature_format();
+
+ if(padding == "EMSA4")
+ {
+ // "MUST contain RSASSA-PSS-params"
+ if(signature_algorithm().get_parameters().empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ Pss_params pss_parameter = decode_pss_params(signature_algorithm().get_parameters());
+
+ // hash_algo must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+ const std::string hash_algo = pss_parameter.hash_algo.get_oid().to_formatted_string();
+ if(hash_algo != "SHA-160" &&
+ hash_algo != "SHA-224" &&
+ hash_algo != "SHA-256" &&
+ hash_algo != "SHA-384" &&
+ hash_algo != "SHA-512")
+ {
+ return Certificate_Status_Code::UNTRUSTED_HASH;
+ }
+
+ const std::string mgf_algo = pss_parameter.mask_gen_algo.get_oid().to_formatted_string();
+ if(mgf_algo != "MGF1")
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ // For MGF1, it is strongly RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm
+ // Must be SHA1, SHA2-224, SHA2-256, SHA2-384 or SHA2-512
+ if(pss_parameter.mask_gen_hash.get_oid() != pss_parameter.hash_algo.get_oid())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ if(pss_parameter.trailer_field != 1)
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+
+ padding += "(" + hash_algo + "," + mgf_algo + "," + std::to_string(pss_parameter.salt_len) + ")";
+ }
+ else
+ {
+ /*
+ * For all other signature types the signature parameters should
+ * be either NULL or empty. In theory there is some distinction between
+ * these but in practice they seem to be used somewhat interchangeably.
+ *
+ * The various RFCs all have prescriptions of what is allowed:
+ * RSA - NULL (RFC 3279)
+ * DSA - empty (RFC 3279)
+ * ECDSA - empty (RFC 3279)
+ * GOST - empty (RFC 4491)
+ * Ed25519 - empty (RFC 8410)
+ * XMSS - empty (draft-vangeest-x509-hash-sigs)
+ *
+ * But in practice we find RSA with empty and ECDSA will NULL all
+ * over the place so it's not really possible to enforce. For Ed25519
+ * and XMSS because they are new we attempt to enforce.
+ */
+ if(pub_key_algo == "Ed25519" || pub_key_algo == "XMSS")
+ {
+ if(!signature_algorithm().parameters_are_empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+ }
+ else
+ {
+ if(!signature_algorithm().parameters_are_null_or_empty())
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
+ }
+ }
+ }
+
+ try
+ {
+ PK_Verifier verifier(pub_key, padding, format);
+ const bool valid = verifier.verify_message(tbs_data(), signature());
+
+ if(valid)
+ return Certificate_Status_Code::VERIFIED;
+ else
+ return Certificate_Status_Code::SIGNATURE_ERROR;
+ }
+ catch(Algorithm_Not_Found&)
+ {
+ return Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN;
+ }
+ catch(...)
+ {
+ // This shouldn't happen, fallback to generic signature error
+ return Certificate_Status_Code::SIGNATURE_ERROR;
+ }
+ }
+
+/*
+* Apply the X.509 SIGNED macro
+*/
+std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& algo,
+ const secure_vector<uint8_t>& tbs_bits)
+ {
+ const std::vector<uint8_t> signature = signer->sign_message(tbs_bits, rng);
+
+ std::vector<uint8_t> output;
+ DER_Encoder(output)
+ .start_cons(SEQUENCE)
+ .raw_bytes(tbs_bits)
+ .encode(algo)
+ .encode(signature, BIT_STRING)
+ .end_cons();
+
+ return output;
+ }
+
+namespace {
+
+std::string choose_sig_algo(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ const std::string& user_specified)
+ {
+ const std::string algo_name = key.algo_name();
+ std::string padding;
+
+ // check algo_name and set default
+ if(algo_name == "RSA")
+ {
+ // set to EMSA3 for compatibility reasons, originally it was the only option
+ padding = "EMSA3(" + hash_fn + ")";
+ }
+ else if(algo_name == "DSA" ||
+ algo_name == "ECDSA" ||
+ algo_name == "ECGDSA" ||
+ algo_name == "ECKCDSA" ||
+ algo_name == "GOST-34.10" ||
+ algo_name == "GOST-34.10-2012-256" ||
+ algo_name == "GOST-34.10-2012-512")
+ {
+ padding = "EMSA1(" + hash_fn + ")";
+ }
+ else if(algo_name == "Ed25519")
+ {
+ padding = "Pure";
+ }
+ else if(algo_name == "XMSS")
+ {
+ if(user_specified.empty() == true)
+ {
+ throw Invalid_Argument("XMSS requires padding scheme");
+ }
+ padding = user_specified;
+ sig_algo = AlgorithmIdentifier(OID::from_string("XMSS"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ return padding;
+ }
+ else
+ {
+ throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
+ }
+
+ if(user_specified.empty() == false)
+ {
+ padding = user_specified;
+ }
+
+ if(padding != "Pure")
+ {
+ // try to construct an EMSA object from the padding options or default
+ std::unique_ptr<EMSA> emsa;
+ try
+ {
+ emsa.reset(get_emsa(padding));
+ }
+ /*
+ * get_emsa will throw if opts contains {"padding",<valid_padding>} but
+ * <valid_padding> does not specify a hash function.
+ * Omitting it is valid since it needs to be identical to hash_fn.
+ * If it still throws, something happened that we cannot repair here,
+ * e.g. the algorithm/padding combination is not supported.
+ */
+ catch(...)
+ {
+ emsa.reset(get_emsa(padding + "(" + hash_fn + ")"));
+ }
+
+ if(!emsa)
+ {
+ throw Invalid_Argument("Could not parse padding scheme " + padding);
+ }
+
+ sig_algo = emsa->config_for_x509(key, hash_fn);
+ return emsa->name();
+ }
+ else
+ {
+ sig_algo = AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ return "Pure";
+ }
+ }
+
+}
+
+/*
+* Choose a signing format for the key
+*/
+std::unique_ptr<PK_Signer> X509_Object::choose_sig_format(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ const std::string& padding_algo)
+ {
+ const Signature_Format format = key.default_x509_signature_format();
+
+ const std::string emsa = choose_sig_algo(sig_algo, key, hash_fn, padding_algo);
+
+ return std::unique_ptr<PK_Signer>(new PK_Signer(key, rng, emsa, format));
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509_obj.h b/comm/third_party/botan/src/lib/x509/x509_obj.h
new file mode 100644
index 0000000000..fd972aed9d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509_obj.h
@@ -0,0 +1,144 @@
+/*
+* X.509 SIGNED Object
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_OBJECT_H_
+#define BOTAN_X509_OBJECT_H_
+
+#include <botan/asn1_obj.h>
+#include <botan/pkix_enums.h>
+#include <vector>
+
+namespace Botan {
+
+class Public_Key;
+class Private_Key;
+class RandomNumberGenerator;
+
+/**
+* This class represents abstract X.509 signed objects as in the X.500
+* SIGNED macro
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Object : public ASN1_Object
+ {
+ public:
+ /**
+ * The underlying data that is to be or was signed
+ * @return data that is or was signed
+ */
+ std::vector<uint8_t> tbs_data() const;
+
+ /**
+ * @return signature on tbs_data()
+ */
+ const std::vector<uint8_t>& signature() const { return m_sig; }
+
+ /**
+ * @return signed body
+ */
+ const std::vector<uint8_t>& signed_body() const { return m_tbs_bits; }
+
+ /**
+ * @return signature algorithm that was used to generate signature
+ */
+ const AlgorithmIdentifier& signature_algorithm() const { return m_sig_algo; }
+
+ /**
+ * @return hash algorithm that was used to generate signature
+ */
+ std::string hash_used_for_signature() const;
+
+ /**
+ * Create a signed X509 object.
+ * @param signer the signer used to sign the object
+ * @param rng the random number generator to use
+ * @param alg_id the algorithm identifier of the signature scheme
+ * @param tbs the tbs bits to be signed
+ * @return signed X509 object
+ */
+ static std::vector<uint8_t> make_signed(class PK_Signer* signer,
+ RandomNumberGenerator& rng,
+ const AlgorithmIdentifier& alg_id,
+ const secure_vector<uint8_t>& tbs);
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * @return status of the signature - OK if verified or otherwise an indicator of
+ * the problem preventing verification.
+ */
+ Certificate_Status_Code verify_signature(const Public_Key& key) const;
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key& key) const;
+
+ /**
+ * Check the signature on this data
+ * @param key the public key purportedly used to sign this data
+ * the object will be deleted after use (this should have
+ * been a std::unique_ptr<Public_Key>)
+ * @return true if the signature is valid, otherwise false
+ */
+ bool check_signature(const Public_Key* key) const;
+
+ /**
+ * DER encode an X509_Object
+ * See @ref ASN1_Object::encode_into()
+ */
+ void encode_into(class DER_Encoder& to) const override;
+
+ /**
+ * Decode a BER encoded X509_Object
+ * See @ref ASN1_Object::decode_from()
+ */
+ void decode_from(class BER_Decoder& from) override;
+
+ /**
+ * @return PEM encoding of this
+ */
+ std::string PEM_encode() const;
+
+ X509_Object(const X509_Object&) = default;
+ X509_Object& operator=(const X509_Object&) = default;
+
+ virtual std::string PEM_label() const = 0;
+
+ virtual std::vector<std::string> alternate_PEM_labels() const
+ { return std::vector<std::string>(); }
+
+ virtual ~X509_Object() = default;
+
+ static std::unique_ptr<PK_Signer>
+ choose_sig_format(AlgorithmIdentifier& sig_algo,
+ const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& hash_fn,
+ const std::string& padding_algo);
+
+ protected:
+
+ X509_Object() = default;
+
+ /**
+ * Decodes from src as either DER or PEM data, then calls force_decode()
+ */
+ void load_data(DataSource& src);
+
+ private:
+ virtual void force_decode() = 0;
+
+ AlgorithmIdentifier m_sig_algo;
+ std::vector<uint8_t> m_tbs_bits;
+ std::vector<uint8_t> m_sig;
+ };
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509cert.cpp b/comm/third_party/botan/src/lib/x509/x509cert.cpp
new file mode 100644
index 0000000000..55f279c58c
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509cert.cpp
@@ -0,0 +1,956 @@
+/*
+* X.509 Certificates
+* (C) 1999-2010,2015,2017 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509cert.h>
+#include <botan/x509_key.h>
+#include <botan/datastor.h>
+#include <botan/pk_keys.h>
+#include <botan/x509_ext.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/bigint.h>
+#include <botan/oids.h>
+#include <botan/hash.h>
+#include <botan/hex.h>
+#include <algorithm>
+#include <sstream>
+
+namespace Botan {
+
+struct X509_Certificate_Data
+ {
+ std::vector<uint8_t> m_serial;
+ AlgorithmIdentifier m_sig_algo_inner;
+ X509_DN m_issuer_dn;
+ X509_DN m_subject_dn;
+ std::vector<uint8_t> m_issuer_dn_bits;
+ std::vector<uint8_t> m_subject_dn_bits;
+ X509_Time m_not_before;
+ X509_Time m_not_after;
+ std::vector<uint8_t> m_subject_public_key_bits;
+ std::vector<uint8_t> m_subject_public_key_bits_seq;
+ std::vector<uint8_t> m_subject_public_key_bitstring;
+ std::vector<uint8_t> m_subject_public_key_bitstring_sha1;
+ AlgorithmIdentifier m_subject_public_key_algid;
+
+ std::vector<uint8_t> m_v2_issuer_key_id;
+ std::vector<uint8_t> m_v2_subject_key_id;
+ Extensions m_v3_extensions;
+
+ std::vector<OID> m_extended_key_usage;
+ std::vector<uint8_t> m_authority_key_id;
+ std::vector<uint8_t> m_subject_key_id;
+ std::vector<OID> m_cert_policies;
+
+ std::vector<std::string> m_crl_distribution_points;
+ std::string m_ocsp_responder;
+ std::vector<std::string> m_ca_issuers;
+
+ std::vector<uint8_t> m_issuer_dn_bits_sha256;
+ std::vector<uint8_t> m_subject_dn_bits_sha256;
+
+ std::string m_fingerprint_sha1;
+ std::string m_fingerprint_sha256;
+
+ AlternativeName m_subject_alt_name;
+ AlternativeName m_issuer_alt_name;
+ NameConstraints m_name_constraints;
+
+ Data_Store m_subject_ds;
+ Data_Store m_issuer_ds;
+
+ size_t m_version = 0;
+ size_t m_path_len_constraint = 0;
+ Key_Constraints m_key_constraints = NO_CONSTRAINTS;
+ bool m_self_signed = false;
+ bool m_is_ca_certificate = false;
+ bool m_serial_negative = false;
+ };
+
+std::string X509_Certificate::PEM_label() const
+ {
+ return "CERTIFICATE";
+ }
+
+std::vector<std::string> X509_Certificate::alternate_PEM_labels() const
+ {
+ return { "X509 CERTIFICATE" };
+ }
+
+X509_Certificate::X509_Certificate(DataSource& src)
+ {
+ load_data(src);
+ }
+
+X509_Certificate::X509_Certificate(const std::vector<uint8_t>& vec)
+ {
+ DataSource_Memory src(vec.data(), vec.size());
+ load_data(src);
+ }
+
+X509_Certificate::X509_Certificate(const uint8_t data[], size_t len)
+ {
+ DataSource_Memory src(data, len);
+ load_data(src);
+ }
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+X509_Certificate::X509_Certificate(const std::string& fsname)
+ {
+ DataSource_Stream src(fsname, true);
+ load_data(src);
+ }
+#endif
+
+namespace {
+
+std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& obj)
+ {
+ std::unique_ptr<X509_Certificate_Data> data(new X509_Certificate_Data);
+
+ BigInt serial_bn;
+ BER_Object public_key;
+ BER_Object v3_exts_data;
+
+ BER_Decoder(obj.signed_body())
+ .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+ .decode(serial_bn)
+ .decode(data->m_sig_algo_inner)
+ .decode(data->m_issuer_dn)
+ .start_cons(SEQUENCE)
+ .decode(data->m_not_before)
+ .decode(data->m_not_after)
+ .end_cons()
+ .decode(data->m_subject_dn)
+ .get_next(public_key)
+ .decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1)
+ .decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2)
+ .get_next(v3_exts_data)
+ .verify_end("TBSCertificate has extra data after extensions block");
+
+ if(data->m_version > 2)
+ throw Decoding_Error("Unknown X.509 cert version " + std::to_string(data->m_version));
+ if(obj.signature_algorithm() != data->m_sig_algo_inner)
+ throw Decoding_Error("X.509 Certificate had differing algorithm identifers in inner and outer ID fields");
+
+ public_key.assert_is_a(SEQUENCE, CONSTRUCTED, "X.509 certificate public key");
+
+ // crude method to save the serial's sign; will get lost during decoding, otherwise
+ data->m_serial_negative = serial_bn.is_negative();
+
+ // for general sanity convert wire version (0 based) to standards version (v1 .. v3)
+ data->m_version += 1;
+
+ data->m_serial = BigInt::encode(serial_bn);
+ data->m_subject_dn_bits = ASN1::put_in_sequence(data->m_subject_dn.get_bits());
+ data->m_issuer_dn_bits = ASN1::put_in_sequence(data->m_issuer_dn.get_bits());
+
+ // validate_public_key_params(public_key.value);
+ AlgorithmIdentifier public_key_alg_id;
+ BER_Decoder(public_key).decode(public_key_alg_id).discard_remaining();
+
+ const std::vector<std::string> public_key_info =
+ split_on(OIDS::oid2str_or_empty(public_key_alg_id.get_oid()), '/');
+
+ if(!public_key_info.empty() && public_key_info[0] == "RSA")
+ {
+ // RFC4055: If PublicKeyAlgo = PSS or OAEP: limit the use of the public key exclusively to either RSASSA - PSS or RSAES - OAEP
+ if(public_key_info.size() >= 2)
+ {
+ if(public_key_info[1] == "EMSA4")
+ {
+ /*
+ When the RSA private key owner wishes to limit the use of the public
+ key exclusively to RSASSA-PSS, then the id-RSASSA-PSS object
+ identifier MUST be used in the algorithm field within the subject
+ public key information, and, if present, the parameters field MUST
+ contain RSASSA-PSS-params.
+
+ All parameters in the signature structure algorithm identifier MUST
+ match the parameters in the key structure algorithm identifier
+ except the saltLength field. The saltLength field in the signature parameters
+ MUST be greater or equal to that in the key parameters field.
+
+ ToDo: Allow salt length to be greater
+ */
+ if(public_key_alg_id != obj.signature_algorithm())
+ {
+ throw Decoding_Error("Algorithm identifier mismatch");
+ }
+ }
+ }
+ else
+ {
+ // oid = rsaEncryption -> parameters field MUST contain NULL
+ if(public_key_alg_id != AlgorithmIdentifier(public_key_alg_id.get_oid(), AlgorithmIdentifier::USE_NULL_PARAM))
+ {
+ throw Decoding_Error("RSA algorithm parameters field MUST contain NULL");
+ }
+ }
+ }
+
+ data->m_subject_public_key_bits.assign(public_key.bits(), public_key.bits() + public_key.length());
+
+ data->m_subject_public_key_bits_seq = ASN1::put_in_sequence(data->m_subject_public_key_bits);
+
+ BER_Decoder(data->m_subject_public_key_bits)
+ .decode(data->m_subject_public_key_algid)
+ .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+
+ if(v3_exts_data.is_a(3, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+ {
+ // Path validation will reject a v1/v2 cert with v3 extensions
+ BER_Decoder(v3_exts_data).decode(data->m_v3_extensions).verify_end();
+ }
+ else if(v3_exts_data.is_set())
+ {
+ throw BER_Bad_Tag("Unknown tag in X.509 cert", v3_exts_data.tagging());
+ }
+
+ // Now cache some fields from the extensions
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Key_Usage>())
+ {
+ data->m_key_constraints = ext->get_constraints();
+ /*
+ RFC 5280: When the keyUsage extension appears in a certificate,
+ at least one of the bits MUST be set to 1.
+ */
+ if(data->m_key_constraints == NO_CONSTRAINTS)
+ {
+ throw Decoding_Error("Certificate has invalid encoding for KeyUsage");
+ }
+ }
+ else
+ {
+ data->m_key_constraints = NO_CONSTRAINTS;
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Key_ID>())
+ {
+ data->m_subject_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>())
+ {
+ data->m_authority_key_id = ext->get_key_id();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Name_Constraints>())
+ {
+ data->m_name_constraints = ext->get_name_constraints();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Basic_Constraints>())
+ {
+ if(ext->get_is_ca() == true)
+ {
+ /*
+ * RFC 5280 section 4.2.1.3 requires that CAs include KeyUsage in all
+ * intermediate CA certificates they issue. Currently we accept it being
+ * missing, as do most other implementations. But it may be worth
+ * removing this entirely, or alternately adding a warning level
+ * validation failure for it.
+ */
+ if(data->m_key_constraints == NO_CONSTRAINTS ||
+ (data->m_key_constraints & KEY_CERT_SIGN))
+ {
+ data->m_is_ca_certificate = true;
+ data->m_path_len_constraint = ext->get_path_limit();
+ }
+ }
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Issuer_Alternative_Name>())
+ {
+ data->m_issuer_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Subject_Alternative_Name>())
+ {
+ data->m_subject_alt_name = ext->get_alt_name();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Extended_Key_Usage>())
+ {
+ data->m_extended_key_usage = ext->get_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Certificate_Policies>())
+ {
+ data->m_cert_policies = ext->get_policy_oids();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::Authority_Information_Access>())
+ {
+ data->m_ocsp_responder = ext->ocsp_responder();
+ data->m_ca_issuers = ext->ca_issuers();
+ }
+
+ if(auto ext = data->m_v3_extensions.get_extension_object_as<Cert_Extension::CRL_Distribution_Points>())
+ {
+ data->m_crl_distribution_points = ext->crl_distribution_urls();
+ }
+
+ // Check for self-signed vs self-issued certificates
+ if(data->m_subject_dn == data->m_issuer_dn)
+ {
+ if(data->m_subject_key_id.empty() == false && data->m_authority_key_id.empty() == false)
+ {
+ data->m_self_signed = (data->m_subject_key_id == data->m_authority_key_id);
+ }
+ else
+ {
+ /*
+ If a parse error or unknown algorithm is encountered, default
+ to assuming it is self signed. We have no way of being certain but
+ that is usually the default case (self-issued is rare in practice).
+ */
+ data->m_self_signed = true;
+
+ try
+ {
+ std::unique_ptr<Public_Key> pub_key(X509::load_key(data->m_subject_public_key_bits_seq));
+
+ Certificate_Status_Code sig_status = obj.verify_signature(*pub_key);
+
+ if(sig_status == Certificate_Status_Code::OK ||
+ sig_status == Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN)
+ {
+ data->m_self_signed = true;
+ }
+ else
+ {
+ data->m_self_signed = false;
+ }
+ }
+ catch(...)
+ {
+ // ignore errors here to allow parsing to continue
+ }
+ }
+ }
+
+ const std::vector<uint8_t> full_encoding = obj.BER_encode();
+
+ std::unique_ptr<HashFunction> sha1(HashFunction::create("SHA-1"));
+ if(sha1)
+ {
+ sha1->update(data->m_subject_public_key_bitstring);
+ data->m_subject_public_key_bitstring_sha1 = sha1->final_stdvec();
+ // otherwise left as empty, and we will throw if subject_public_key_bitstring_sha1 is called
+
+ data->m_fingerprint_sha1 = create_hex_fingerprint(full_encoding, "SHA-1");
+ }
+
+ std::unique_ptr<HashFunction> sha256(HashFunction::create("SHA-256"));
+ if(sha256)
+ {
+ sha256->update(data->m_issuer_dn_bits);
+ data->m_issuer_dn_bits_sha256 = sha256->final_stdvec();
+
+ sha256->update(data->m_subject_dn_bits);
+ data->m_subject_dn_bits_sha256 = sha256->final_stdvec();
+
+ data->m_fingerprint_sha256 = create_hex_fingerprint(full_encoding, "SHA-256");
+ }
+
+ data->m_subject_ds.add(data->m_subject_dn.contents());
+ data->m_issuer_ds.add(data->m_issuer_dn.contents());
+ data->m_v3_extensions.contents_to(data->m_subject_ds, data->m_issuer_ds);
+
+ return data;
+ }
+
+}
+
+/*
+* Decode the TBSCertificate data
+*/
+void X509_Certificate::force_decode()
+ {
+ m_data.reset();
+
+ std::unique_ptr<X509_Certificate_Data> data = parse_x509_cert_body(*this);
+
+ m_data.reset(data.release());
+ }
+
+const X509_Certificate_Data& X509_Certificate::data() const
+ {
+ if(m_data == nullptr)
+ {
+ throw Invalid_State("X509_Certificate uninitialized");
+ }
+ return *m_data.get();
+ }
+
+uint32_t X509_Certificate::x509_version() const
+ {
+ return static_cast<uint32_t>(data().m_version);
+ }
+
+bool X509_Certificate::is_self_signed() const
+ {
+ return data().m_self_signed;
+ }
+
+const X509_Time& X509_Certificate::not_before() const
+ {
+ return data().m_not_before;
+ }
+
+const X509_Time& X509_Certificate::not_after() const
+ {
+ return data().m_not_after;
+ }
+
+const AlgorithmIdentifier& X509_Certificate::subject_public_key_algo() const
+ {
+ return data().m_subject_public_key_algid;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_issuer_key_id() const
+ {
+ return data().m_v2_issuer_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::v2_subject_key_id() const
+ {
+ return data().m_v2_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bits() const
+ {
+ return data().m_subject_public_key_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_info() const
+ {
+ return data().m_subject_public_key_bits_seq;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring() const
+ {
+ return data().m_subject_public_key_bitstring;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_public_key_bitstring_sha1() const
+ {
+ if(data().m_subject_public_key_bitstring_sha1.empty())
+ throw Encoding_Error("X509_Certificate::subject_public_key_bitstring_sha1 called but SHA-1 disabled in build");
+
+ return data().m_subject_public_key_bitstring_sha1;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::authority_key_id() const
+ {
+ return data().m_authority_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::subject_key_id() const
+ {
+ return data().m_subject_key_id;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::serial_number() const
+ {
+ return data().m_serial;
+ }
+
+bool X509_Certificate::is_serial_negative() const
+ {
+ return data().m_serial_negative;
+ }
+
+
+const X509_DN& X509_Certificate::issuer_dn() const
+ {
+ return data().m_issuer_dn;
+ }
+
+const X509_DN& X509_Certificate::subject_dn() const
+ {
+ return data().m_subject_dn;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_issuer_dn() const
+ {
+ return data().m_issuer_dn_bits;
+ }
+
+const std::vector<uint8_t>& X509_Certificate::raw_subject_dn() const
+ {
+ return data().m_subject_dn_bits;
+ }
+
+bool X509_Certificate::is_CA_cert() const
+ {
+ if(data().m_version < 3 && data().m_self_signed)
+ return true;
+
+ return data().m_is_ca_certificate;
+ }
+
+uint32_t X509_Certificate::path_limit() const
+ {
+ if(data().m_version < 3 && data().m_self_signed)
+ return 32; // in theory infinite, but this is more than enough
+
+ return static_cast<uint32_t>(data().m_path_len_constraint);
+ }
+
+Key_Constraints X509_Certificate::constraints() const
+ {
+ return data().m_key_constraints;
+ }
+
+const std::vector<OID>& X509_Certificate::extended_key_usage() const
+ {
+ return data().m_extended_key_usage;
+ }
+
+const std::vector<OID>& X509_Certificate::certificate_policy_oids() const
+ {
+ return data().m_cert_policies;
+ }
+
+const NameConstraints& X509_Certificate::name_constraints() const
+ {
+ return data().m_name_constraints;
+ }
+
+const Extensions& X509_Certificate::v3_extensions() const
+ {
+ return data().m_v3_extensions;
+ }
+
+bool X509_Certificate::allowed_usage(Key_Constraints usage) const
+ {
+ if(constraints() == NO_CONSTRAINTS)
+ return true;
+ return ((constraints() & usage) == usage);
+ }
+
+bool X509_Certificate::allowed_extended_usage(const std::string& usage) const
+ {
+ return allowed_extended_usage(OID::from_string(usage));
+ }
+
+bool X509_Certificate::allowed_extended_usage(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ if(ex.empty())
+ return true;
+
+ if(std::find(ex.begin(), ex.end(), usage) != ex.end())
+ return true;
+
+ return false;
+ }
+
+bool X509_Certificate::allowed_usage(Usage_Type usage) const
+ {
+ // These follow suggestions in RFC 5280 4.2.1.12
+
+ switch(usage)
+ {
+ case Usage_Type::UNSPECIFIED:
+ return true;
+
+ case Usage_Type::TLS_SERVER_AUTH:
+ return (allowed_usage(KEY_AGREEMENT) || allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DIGITAL_SIGNATURE)) && allowed_extended_usage("PKIX.ServerAuth");
+
+ case Usage_Type::TLS_CLIENT_AUTH:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(KEY_AGREEMENT)) && allowed_extended_usage("PKIX.ClientAuth");
+
+ case Usage_Type::OCSP_RESPONDER:
+ return (allowed_usage(DIGITAL_SIGNATURE) || allowed_usage(NON_REPUDIATION)) && allowed_extended_usage("PKIX.OCSPSigning");
+
+ case Usage_Type::CERTIFICATE_AUTHORITY:
+ return is_CA_cert();
+
+ case Usage_Type::ENCRYPTION:
+ return (allowed_usage(KEY_ENCIPHERMENT) || allowed_usage(DATA_ENCIPHERMENT));
+ }
+
+ return false;
+ }
+
+bool X509_Certificate::has_constraints(Key_Constraints constraints) const
+ {
+ if(this->constraints() == NO_CONSTRAINTS)
+ {
+ return false;
+ }
+
+ return ((this->constraints() & constraints) != 0);
+ }
+
+bool X509_Certificate::has_ex_constraint(const std::string& ex_constraint) const
+ {
+ return has_ex_constraint(OID::from_string(ex_constraint));
+ }
+
+bool X509_Certificate::has_ex_constraint(const OID& usage) const
+ {
+ const std::vector<OID>& ex = extended_key_usage();
+ return (std::find(ex.begin(), ex.end(), usage) != ex.end());
+ }
+
+/*
+* Return if a certificate extension is marked critical
+*/
+bool X509_Certificate::is_critical(const std::string& ex_name) const
+ {
+ return v3_extensions().critical_extension_set(OID::from_string(ex_name));
+ }
+
+std::string X509_Certificate::ocsp_responder() const
+ {
+ return data().m_ocsp_responder;
+ }
+
+std::vector<std::string> X509_Certificate::ca_issuers() const
+ {
+ return data().m_ca_issuers;
+ }
+
+std::string X509_Certificate::crl_distribution_point() const
+ {
+ // just returns the first (arbitrarily)
+ if(data().m_crl_distribution_points.size() > 0)
+ return data().m_crl_distribution_points[0];
+ return "";
+ }
+
+const AlternativeName& X509_Certificate::subject_alt_name() const
+ {
+ return data().m_subject_alt_name;
+ }
+
+const AlternativeName& X509_Certificate::issuer_alt_name() const
+ {
+ return data().m_issuer_alt_name;
+ }
+
+/*
+* Return information about the subject
+*/
+std::vector<std::string>
+X509_Certificate::subject_info(const std::string& req) const
+ {
+ if(req == "Email")
+ return this->subject_info("RFC822");
+
+ if(subject_dn().has_field(req))
+ return subject_dn().get_attribute(req);
+
+ if(subject_alt_name().has_field(req))
+ return subject_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_subject_key_id())};
+ if(req == "X509v3.SubjectKeyIdentifier")
+ return {hex_encode(this->subject_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_subject_dn())};
+ if(req == "X509.Certificate.start")
+ return {not_before().to_string()};
+ if(req == "X509.Certificate.end")
+ return {not_after().to_string()};
+
+ if(req == "X509.Certificate.version")
+ return {std::to_string(x509_version())};
+ if(req == "X509.Certificate.serial")
+ return {hex_encode(serial_number())};
+
+ return data().m_subject_ds.get(req);
+ }
+
+/*
+* Return information about the issuer
+*/
+std::vector<std::string>
+X509_Certificate::issuer_info(const std::string& req) const
+ {
+ if(issuer_dn().has_field(req))
+ return issuer_dn().get_attribute(req);
+
+ if(issuer_alt_name().has_field(req))
+ return issuer_alt_name().get_attribute(req);
+
+ // These will be removed later:
+ if(req == "X509.Certificate.v2.key_id")
+ return {hex_encode(this->v2_issuer_key_id())};
+ if(req == "X509v3.AuthorityKeyIdentifier")
+ return {hex_encode(this->authority_key_id())};
+ if(req == "X509.Certificate.dn_bits")
+ return {hex_encode(this->raw_issuer_dn())};
+
+ return data().m_issuer_ds.get(req);
+ }
+
+/*
+* Return the public key in this certificate
+*/
+std::unique_ptr<Public_Key> X509_Certificate::load_subject_public_key() const
+ {
+ try
+ {
+ return std::unique_ptr<Public_Key>(X509::load_key(subject_public_key_info()));
+ }
+ catch(std::exception& e)
+ {
+ throw Decoding_Error("X509_Certificate::load_subject_public_key", e);
+ }
+ }
+
+Public_Key* X509_Certificate::subject_public_key() const
+ {
+ return load_subject_public_key().release();
+ }
+
+std::vector<uint8_t> X509_Certificate::raw_issuer_dn_sha256() const
+ {
+ if(data().m_issuer_dn_bits_sha256.empty())
+ throw Encoding_Error("X509_Certificate::raw_issuer_dn_sha256 called but SHA-256 disabled in build");
+ return data().m_issuer_dn_bits_sha256;
+ }
+
+std::vector<uint8_t> X509_Certificate::raw_subject_dn_sha256() const
+ {
+ if(data().m_subject_dn_bits_sha256.empty())
+ throw Encoding_Error("X509_Certificate::raw_subject_dn_sha256 called but SHA-256 disabled in build");
+ return data().m_subject_dn_bits_sha256;
+ }
+
+namespace {
+
+/*
+* Lookup each OID in the vector
+*/
+std::vector<std::string> lookup_oids(const std::vector<OID>& oids)
+ {
+ std::vector<std::string> out;
+
+ for(const OID& oid : oids)
+ {
+ out.push_back(oid.to_formatted_string());
+ }
+ return out;
+ }
+
+}
+
+/*
+* Return the list of extended key usage OIDs
+*/
+std::vector<std::string> X509_Certificate::ex_constraints() const
+ {
+ return lookup_oids(extended_key_usage());
+ }
+
+/*
+* Return the list of certificate policies
+*/
+std::vector<std::string> X509_Certificate::policies() const
+ {
+ return lookup_oids(certificate_policy_oids());
+ }
+
+std::string X509_Certificate::fingerprint(const std::string& hash_name) const
+ {
+ /*
+ * The SHA-1 and SHA-256 fingerprints are precomputed since these
+ * are the most commonly used. Especially, SHA-256 fingerprints are
+ * used for cycle detection during path construction.
+ *
+ * If SHA-1 or SHA-256 was missing at parsing time the vectors are
+ * left empty in which case we fall back to create_hex_fingerprint
+ * which will throw if the hash is unavailable.
+ */
+ if(hash_name == "SHA-256" && data().m_fingerprint_sha256.size() > 0)
+ return data().m_fingerprint_sha256;
+ else if(hash_name == "SHA-1" && data().m_fingerprint_sha1.size() > 0)
+ return data().m_fingerprint_sha1;
+ else
+ return create_hex_fingerprint(this->BER_encode(), hash_name);
+ }
+
+bool X509_Certificate::matches_dns_name(const std::string& name) const
+ {
+ if(name.empty())
+ return false;
+
+ std::vector<std::string> issued_names = subject_info("DNS");
+
+ // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+ if(issued_names.empty())
+ issued_names = subject_info("Name");
+
+ for(size_t i = 0; i != issued_names.size(); ++i)
+ {
+ if(host_wildcard_match(issued_names[i], name))
+ return true;
+ }
+
+ return false;
+ }
+
+/*
+* Compare two certificates for equality
+*/
+bool X509_Certificate::operator==(const X509_Certificate& other) const
+ {
+ return (this->signature() == other.signature() &&
+ this->signature_algorithm() == other.signature_algorithm() &&
+ this->signed_body() == other.signed_body());
+ }
+
+bool X509_Certificate::operator<(const X509_Certificate& other) const
+ {
+ /* If signature values are not equal, sort by lexicographic ordering of that */
+ if(this->signature() != other.signature())
+ {
+ return (this->signature() < other.signature());
+ }
+
+ // Then compare the signed contents
+ return this->signed_body() < other.signed_body();
+ }
+
+/*
+* X.509 Certificate Comparison
+*/
+bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2)
+ {
+ return !(cert1 == cert2);
+ }
+
+std::string X509_Certificate::to_string() const
+ {
+ std::ostringstream out;
+
+ out << "Version: " << this->x509_version() << "\n";
+ out << "Subject: " << subject_dn() << "\n";
+ out << "Issuer: " << issuer_dn() << "\n";
+ out << "Issued: " << this->not_before().readable_string() << "\n";
+ out << "Expires: " << this->not_after().readable_string() << "\n";
+
+ out << "Constraints:\n";
+ Key_Constraints constraints = this->constraints();
+ if(constraints == NO_CONSTRAINTS)
+ out << " None\n";
+ else
+ {
+ if(constraints & DIGITAL_SIGNATURE)
+ out << " Digital Signature\n";
+ if(constraints & NON_REPUDIATION)
+ out << " Non-Repudiation\n";
+ if(constraints & KEY_ENCIPHERMENT)
+ out << " Key Encipherment\n";
+ if(constraints & DATA_ENCIPHERMENT)
+ out << " Data Encipherment\n";
+ if(constraints & KEY_AGREEMENT)
+ out << " Key Agreement\n";
+ if(constraints & KEY_CERT_SIGN)
+ out << " Cert Sign\n";
+ if(constraints & CRL_SIGN)
+ out << " CRL Sign\n";
+ if(constraints & ENCIPHER_ONLY)
+ out << " Encipher Only\n";
+ if(constraints & DECIPHER_ONLY)
+ out << " Decipher Only\n";
+ }
+
+ const std::vector<OID>& policies = this->certificate_policy_oids();
+ if(!policies.empty())
+ {
+ out << "Policies: " << "\n";
+ for(auto oid : policies)
+ out << " " << oid.to_string() << "\n";
+ }
+
+ const std::vector<OID>& ex_constraints = this->extended_key_usage();
+ if(!ex_constraints.empty())
+ {
+ out << "Extended Constraints:\n";
+ for(auto&& oid : ex_constraints)
+ {
+ out << " " << oid.to_formatted_string() << "\n";
+ }
+ }
+
+ const NameConstraints& name_constraints = this->name_constraints();
+
+ if(!name_constraints.permitted().empty() || !name_constraints.excluded().empty())
+ {
+ out << "Name Constraints:\n";
+
+ if(!name_constraints.permitted().empty())
+ {
+ out << " Permit";
+ for(auto st: name_constraints.permitted())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+
+ if(!name_constraints.excluded().empty())
+ {
+ out << " Exclude";
+ for(auto st: name_constraints.excluded())
+ {
+ out << " " << st.base();
+ }
+ out << "\n";
+ }
+ }
+
+ if(!ocsp_responder().empty())
+ out << "OCSP responder " << ocsp_responder() << "\n";
+
+ const std::vector<std::string> ca_issuers = this->ca_issuers();
+ if(!ca_issuers.empty())
+ {
+ out << "CA Issuers:\n";
+ for(size_t i = 0; i != ca_issuers.size(); i++)
+ out << " URI: " << ca_issuers[i] << "\n";
+ }
+
+ if(!crl_distribution_point().empty())
+ out << "CRL " << crl_distribution_point() << "\n";
+
+ out << "Signature algorithm: " << this->signature_algorithm().get_oid().to_formatted_string() << "\n";
+
+ out << "Serial number: " << hex_encode(this->serial_number()) << "\n";
+
+ if(this->authority_key_id().size())
+ out << "Authority keyid: " << hex_encode(this->authority_key_id()) << "\n";
+
+ if(this->subject_key_id().size())
+ out << "Subject keyid: " << hex_encode(this->subject_key_id()) << "\n";
+
+ try
+ {
+ std::unique_ptr<Public_Key> pubkey(this->subject_public_key());
+ out << "Public Key [" << pubkey->algo_name() << "-" << pubkey->key_length() << "]\n\n";
+ out << X509::PEM_encode(*pubkey);
+ }
+ catch(Decoding_Error&)
+ {
+ const AlgorithmIdentifier& alg_id = this->subject_public_key_algo();
+ out << "Failed to decode key with oid " << alg_id.get_oid().to_string() << "\n";
+ }
+
+ return out.str();
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509cert.h b/comm/third_party/botan/src/lib/x509/x509cert.h
new file mode 100644
index 0000000000..0355bbb58e
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509cert.h
@@ -0,0 +1,461 @@
+/*
+* X.509 Certificates
+* (C) 1999-2007,2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERTS_H_
+#define BOTAN_X509_CERTS_H_
+
+#include <botan/x509_obj.h>
+#include <memory>
+
+namespace Botan {
+
+class Public_Key;
+class X509_DN;
+class Extensions;
+class AlternativeName;
+class NameConstraints;
+
+enum class Usage_Type
+ {
+ UNSPECIFIED, // no restrictions
+ TLS_SERVER_AUTH,
+ TLS_CLIENT_AUTH,
+ CERTIFICATE_AUTHORITY,
+ OCSP_RESPONDER,
+ ENCRYPTION
+ };
+
+struct X509_Certificate_Data;
+
+/**
+* This class represents an X.509 Certificate
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Certificate : public X509_Object
+ {
+ public:
+ /**
+ * Return a newly allocated copy of the public key associated
+ * with the subject of this certificate. This object is owned
+ * by the caller.
+ *
+ * Prefer load_subject_public_key in new code
+ *
+ * @return public key
+ */
+ Public_Key* subject_public_key() const;
+
+ /**
+ * Create a public key object associated with the public key bits in this
+ * certificate. If the public key bits was valid for X.509 encoding
+ * purposes but invalid algorithmically (for example, RSA with an even
+ * modulus) that will be detected at this point, and an exception will be
+ * thrown.
+ *
+ * @return subject public key of this certificate
+ */
+ std::unique_ptr<Public_Key> load_subject_public_key() const;
+
+ /**
+ * Get the public key associated with this certificate. This includes the
+ * outer AlgorithmIdentifier
+ * @return subject public key of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_bits() const;
+
+ /**
+ * Get the SubjectPublicKeyInfo associated with this certificate.
+ * @return subject public key info of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_info() const;
+
+ /**
+ * Return the algorithm identifier of the public key
+ */
+ const AlgorithmIdentifier& subject_public_key_algo() const;
+
+ /**
+ * Get the bit string of the public key associated with this certificate
+ * @return public key bits
+ */
+ const std::vector<uint8_t>& subject_public_key_bitstring() const;
+
+ /**
+ * Get the SHA-1 bit string of the public key associated with this certificate.
+ * This is used for OCSP among other protocols.
+ * This function will throw if SHA-1 is not available.
+ * @return hash of subject public key of this certificate
+ */
+ const std::vector<uint8_t>& subject_public_key_bitstring_sha1() const;
+
+ /**
+ * Get the certificate's issuer distinguished name (DN).
+ * @return issuer DN of this certificate
+ */
+ const X509_DN& issuer_dn() const;
+
+ /**
+ * Get the certificate's subject distinguished name (DN).
+ * @return subject DN of this certificate
+ */
+ const X509_DN& subject_dn() const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names include
+ * "X509.Certificate.version", "X509.Certificate.serial",
+ * "X509.Certificate.start", "X509.Certificate.end",
+ * "X509.Certificate.v2.key_id", "X509.Certificate.public_key",
+ * "X509v3.BasicConstraints.path_constraint",
+ * "X509v3.BasicConstraints.is_ca", "X509v3.NameConstraints",
+ * "X509v3.ExtendedKeyUsage", "X509v3.CertificatePolicies",
+ * "X509v3.SubjectKeyIdentifier", "X509.Certificate.serial",
+ * "X520.CommonName", "X520.Organization", "X520.Country",
+ * "RFC822" (Email in SAN) or "PKCS9.EmailAddress" (Email in DN).
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> subject_info(const std::string& name) const;
+
+ /**
+ * Get a value for a specific subject_info parameter name.
+ * @param name the name of the parameter to look up. Possible names are
+ * "X509.Certificate.v2.key_id" or "X509v3.AuthorityKeyIdentifier".
+ * @return value(s) of the specified parameter
+ */
+ std::vector<std::string> issuer_info(const std::string& name) const;
+
+ /**
+ * Raw issuer DN bits
+ */
+ const std::vector<uint8_t>& raw_issuer_dn() const;
+
+ /**
+ * SHA-256 of Raw issuer DN
+ */
+ std::vector<uint8_t> raw_issuer_dn_sha256() const;
+
+ /**
+ * Raw subject DN
+ */
+ const std::vector<uint8_t>& raw_subject_dn() const;
+
+ /**
+ * SHA-256 of Raw subject DN
+ */
+ std::vector<uint8_t> raw_subject_dn_sha256() const;
+
+ /**
+ * Get the notBefore of the certificate as a string
+ * @return notBefore of the certificate
+ */
+ std::string BOTAN_DEPRECATED("Use not_before().to_string()") start_time() const
+ {
+ return not_before().to_string();
+ }
+
+ /**
+ * Get the notAfter of the certificate as a string
+ * @return notAfter of the certificate
+ */
+ std::string BOTAN_DEPRECATED("Use not_after().to_string()") end_time() const
+ {
+ return not_after().to_string();
+ }
+
+ /**
+ * Get the notBefore of the certificate as X509_Time
+ * @return notBefore of the certificate
+ */
+ const X509_Time& not_before() const;
+
+ /**
+ * Get the notAfter of the certificate as X509_Time
+ * @return notAfter of the certificate
+ */
+ const X509_Time& not_after() const;
+
+ /**
+ * Get the X509 version of this certificate object.
+ * @return X509 version
+ */
+ uint32_t x509_version() const;
+
+ /**
+ * Get the serial number of this certificate.
+ * @return certificates serial number
+ */
+ const std::vector<uint8_t>& serial_number() const;
+
+ /**
+ * Get the serial number's sign
+ * @return 1 iff the serial is negative.
+ */
+ bool is_serial_negative() const;
+
+ /**
+ * Get the DER encoded AuthorityKeyIdentifier of this certificate.
+ * @return DER encoded AuthorityKeyIdentifier
+ */
+ const std::vector<uint8_t>& authority_key_id() const;
+
+ /**
+ * Get the DER encoded SubjectKeyIdentifier of this certificate.
+ * @return DER encoded SubjectKeyIdentifier
+ */
+ const std::vector<uint8_t>& subject_key_id() const;
+
+ /**
+ * Check whether this certificate is self signed.
+ * If the DN issuer and subject agree,
+ * @return true if this certificate is self signed
+ */
+ bool is_self_signed() const;
+
+ /**
+ * Check whether this certificate is a CA certificate.
+ * @return true if this certificate is a CA certificate
+ */
+ bool is_CA_cert() const;
+
+ /**
+ * Returns true if the specified @param usage is set in the key usage extension
+ * or if no key usage constraints are set at all.
+ * To check if a certain key constraint is set in the certificate
+ * use @see X509_Certificate#has_constraints.
+ */
+ bool allowed_usage(Key_Constraints usage) const;
+
+ /**
+ * Returns true if the specified @param usage is set in the extended key usage extension
+ * or if no extended key usage constraints are set at all.
+ * To check if a certain extended key constraint is set in the certificate
+ * use @see X509_Certificate#has_ex_constraint.
+ */
+ bool allowed_extended_usage(const std::string& usage) const;
+
+ /**
+ * Returns true if the specified usage is set in the extended key usage extension,
+ * or if no extended key usage constraints are set at all.
+ * To check if a certain extended key constraint is set in the certificate
+ * use @see X509_Certificate#has_ex_constraint.
+ */
+ bool allowed_extended_usage(const OID& usage) const;
+
+ /**
+ * Returns true if the required key and extended key constraints are set in the certificate
+ * for the specified @param usage or if no key constraints are set in both the key usage
+ * and extended key usage extension.
+ */
+ bool allowed_usage(Usage_Type usage) const;
+
+ /**
+ * Returns true if the specified @param constraints are included in the key
+ * usage extension.
+ */
+ bool has_constraints(Key_Constraints constraints) const;
+
+ /**
+ * Returns true if and only if @param ex_constraint (referring to an
+ * extended key constraint, eg "PKIX.ServerAuth") is included in the
+ * extended key extension.
+ */
+ bool BOTAN_DEPRECATED("Use version taking an OID")
+ has_ex_constraint(const std::string& ex_constraint) const;
+
+ /**
+ * Returns true if and only if OID @param ex_constraint is
+ * included in the extended key extension.
+ */
+ bool has_ex_constraint(const OID& ex_constraint) const;
+
+ /**
+ * Get the path limit as defined in the BasicConstraints extension of
+ * this certificate.
+ * @return path limit
+ */
+ uint32_t path_limit() const;
+
+ /**
+ * Check whenever a given X509 Extension is marked critical in this
+ * certificate.
+ */
+ bool is_critical(const std::string& ex_name) const;
+
+ /**
+ * Get the key constraints as defined in the KeyUsage extension of this
+ * certificate.
+ * @return key constraints
+ */
+ Key_Constraints constraints() const;
+
+ /**
+ * Get the key constraints as defined in the ExtendedKeyUsage
+ * extension of this certificate.
+ * @return key constraints
+ */
+ std::vector<std::string>
+ BOTAN_DEPRECATED("Use extended_key_usage") ex_constraints() const;
+
+ /**
+ * Get the key usage as defined in the ExtendedKeyUsage extension
+ * of this certificate, or else an empty vector.
+ * @return key usage
+ */
+ const std::vector<OID>& extended_key_usage() const;
+
+ /**
+ * Get the name constraints as defined in the NameConstraints
+ * extension of this certificate.
+ * @return name constraints
+ */
+ const NameConstraints& name_constraints() const;
+
+ /**
+ * Get the policies as defined in the CertificatePolicies extension
+ * of this certificate.
+ * @return certificate policies
+ */
+ std::vector<std::string> BOTAN_DEPRECATED("Use certificate_policy_oids") policies() const;
+
+ const std::vector<OID>& certificate_policy_oids() const;
+
+ /**
+ * Get all extensions of this certificate.
+ * @return certificate extensions
+ */
+ const Extensions& v3_extensions() const;
+
+ /**
+ * Return the v2 issuer key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_issuer_key_id() const;
+
+ /**
+ * Return the v2 subject key ID. v2 key IDs are almost never used,
+ * instead see v3_subject_key_id.
+ */
+ const std::vector<uint8_t>& v2_subject_key_id() const;
+
+ /**
+ * Return the subject alternative names (DNS, IP, ...)
+ */
+ const AlternativeName& subject_alt_name() const;
+
+ /**
+ * Return the issuer alternative names (DNS, IP, ...)
+ */
+ const AlternativeName& issuer_alt_name() const;
+
+ /**
+ * Return the listed address of an OCSP responder, or empty if not set
+ */
+ std::string ocsp_responder() const;
+
+ /**
+ * Return the listed addresses of ca issuers, or empty if not set
+ */
+ std::vector<std::string> ca_issuers() const;
+
+ /**
+ * Return the CRL distribution point, or empty if not set
+ */
+ std::string crl_distribution_point() const;
+
+ /**
+ * @return a free-form string describing the certificate
+ */
+ std::string to_string() const;
+
+ /**
+ * @return a fingerprint of the certificate
+ * @param hash_name hash function used to calculate the fingerprint
+ */
+ std::string fingerprint(const std::string& hash_name = "SHA-1") const;
+
+ /**
+ * Check if a certain DNS name matches up with the information in
+ * the cert
+ * @param name DNS name to match
+ */
+ bool matches_dns_name(const std::string& name) const;
+
+ /**
+ * Check to certificates for equality.
+ * @return true both certificates are (binary) equal
+ */
+ bool operator==(const X509_Certificate& other) const;
+
+ /**
+ * Impose an arbitrary (but consistent) ordering, eg to allow sorting
+ * a container of certificate objects.
+ * @return true if this is less than other by some unspecified criteria
+ */
+ bool operator<(const X509_Certificate& other) const;
+
+ /**
+ * Create a certificate from a data source providing the DER or
+ * PEM encoded certificate.
+ * @param source the data source
+ */
+ explicit X509_Certificate(DataSource& source);
+
+#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+ /**
+ * Create a certificate from a file containing the DER or PEM
+ * encoded certificate.
+ * @param filename the name of the certificate file
+ */
+ explicit X509_Certificate(const std::string& filename);
+#endif
+
+ /**
+ * Create a certificate from a buffer
+ * @param in the buffer containing the DER-encoded certificate
+ */
+ explicit X509_Certificate(const std::vector<uint8_t>& in);
+
+ /**
+ * Create a certificate from a buffer
+ * @param data the buffer containing the DER-encoded certificate
+ * @param length length of data in bytes
+ */
+ X509_Certificate(const uint8_t data[], size_t length);
+
+ /**
+ * Create an uninitialized certificate object. Any attempts to
+ * access this object will throw an exception.
+ */
+ X509_Certificate() = default;
+
+ X509_Certificate(const X509_Certificate& other) = default;
+
+ X509_Certificate& operator=(const X509_Certificate& other) = default;
+
+ private:
+ std::string PEM_label() const override;
+
+ std::vector<std::string> alternate_PEM_labels() const override;
+
+ void force_decode() override;
+
+ const X509_Certificate_Data& data() const;
+
+ std::shared_ptr<X509_Certificate_Data> m_data;
+ };
+
+/**
+* Check two certificates for inequality
+* @param cert1 The first certificate
+* @param cert2 The second certificate
+* @return true if the arguments represent different certificates,
+* false if they are binary identical
+*/
+BOTAN_PUBLIC_API(2,0) bool operator!=(const X509_Certificate& cert1, const X509_Certificate& cert2);
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509opt.cpp b/comm/third_party/botan/src/lib/x509/x509opt.cpp
new file mode 100644
index 0000000000..f762acd7b7
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509opt.cpp
@@ -0,0 +1,100 @@
+/*
+* X.509 Certificate Options
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/parsing.h>
+#include <chrono>
+
+namespace Botan {
+
+/*
+* Set when the certificate should become valid
+*/
+void X509_Cert_Options::not_before(const std::string& time_string)
+ {
+ start = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set when the certificate should expire
+*/
+void X509_Cert_Options::not_after(const std::string& time_string)
+ {
+ end = X509_Time(time_string, ASN1_Tag::UTC_OR_GENERALIZED_TIME);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_constraints(Key_Constraints usage)
+ {
+ constraints = usage;
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const OID& oid)
+ {
+ ex_constraints.push_back(oid);
+ }
+
+/*
+* Set key constraint information
+*/
+void X509_Cert_Options::add_ex_constraint(const std::string& oid_str)
+ {
+ ex_constraints.push_back(OID::from_string(oid_str));
+ }
+
+/*
+* Mark this certificate for CA usage
+*/
+void X509_Cert_Options::CA_key(size_t limit)
+ {
+ is_CA = true;
+ path_limit = limit;
+ }
+
+void X509_Cert_Options::set_padding_scheme(const std::string& scheme)
+ {
+ padding_scheme = scheme;
+ }
+
+/*
+* Initialize the certificate options
+*/
+X509_Cert_Options::X509_Cert_Options(const std::string& initial_opts,
+ uint32_t expiration_time)
+ {
+ is_CA = false;
+ path_limit = 0;
+ constraints = NO_CONSTRAINTS;
+ // use default for chosen algorithm
+ padding_scheme = "";
+
+ auto now = std::chrono::system_clock::now();
+
+ start = X509_Time(now);
+ end = X509_Time(now + std::chrono::seconds(expiration_time));
+
+ if(initial_opts.empty())
+ return;
+
+ std::vector<std::string> parsed = split_on(initial_opts, '/');
+
+ if(parsed.size() > 4)
+ throw Invalid_Argument("X.509 cert options: Too many names: "
+ + initial_opts);
+
+ if(parsed.size() >= 1) common_name = parsed[0];
+ if(parsed.size() >= 2) country = parsed[1];
+ if(parsed.size() >= 3) organization = parsed[2];
+ if(parsed.size() == 4) org_unit = parsed[3];
+ }
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509path.cpp b/comm/third_party/botan/src/lib/x509/x509path.cpp
new file mode 100644
index 0000000000..b5cdc27c2d
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509path.cpp
@@ -0,0 +1,1088 @@
+/*
+* X.509 Certificate Path Validation
+* (C) 2010,2011,2012,2014,2016 Jack Lloyd
+* (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509path.h>
+#include <botan/x509_ext.h>
+#include <botan/pk_keys.h>
+#include <botan/ocsp.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <chrono>
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+ #include <future>
+ #include <botan/http_util.h>
+#endif
+
+namespace Botan {
+
+/*
+* PKIX path validation
+*/
+CertificatePathStatusCodes
+PKIX::check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::chrono::system_clock::time_point ref_time,
+ const std::string& hostname,
+ Usage_Type usage,
+ size_t min_signature_algo_strength,
+ const std::set<std::string>& trusted_hashes)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_chain cert_path empty");
+
+ const bool self_signed_ee_cert = (cert_path.size() == 1);
+
+ X509_Time validation_time(ref_time);
+
+ CertificatePathStatusCodes cert_status(cert_path.size());
+
+ if(!hostname.empty() && !cert_path[0]->matches_dns_name(hostname))
+ cert_status[0].insert(Certificate_Status_Code::CERT_NAME_NOMATCH);
+
+ if(!cert_path[0]->allowed_usage(usage))
+ cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+
+ if(cert_path[0]->is_CA_cert() == false &&
+ cert_path[0]->has_constraints(KEY_CERT_SIGN))
+ {
+ /*
+ "If the keyCertSign bit is asserted, then the cA bit in the
+ basic constraints extension (Section 4.2.1.9) MUST also be
+ asserted." - RFC 5280
+
+ We don't bother doing this check on the rest of the path since they
+ must have the cA bit asserted or the validation will fail anyway.
+ */
+ cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ }
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ const bool at_self_signed_root = (i == cert_path.size() - 1);
+
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+ const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
+
+ if(at_self_signed_root && (issuer->is_self_signed() == false))
+ {
+ status.insert(Certificate_Status_Code::CHAIN_LACKS_TRUST_ROOT);
+ }
+
+ if(subject->issuer_dn() != issuer->subject_dn())
+ {
+ status.insert(Certificate_Status_Code::CHAIN_NAME_MISMATCH);
+ }
+
+ // Check the serial number
+ if(subject->is_serial_negative())
+ {
+ status.insert(Certificate_Status_Code::CERT_SERIAL_NEGATIVE);
+ }
+
+ // Check the subject's DN components' length
+
+ for(const auto& dn_pair : subject->subject_dn().dn_info())
+ {
+ const size_t dn_ub = X509_DN::lookup_ub(dn_pair.first);
+ // dn_pair = <OID,str>
+ if(dn_ub > 0 && dn_pair.second.size() > dn_ub)
+ {
+ status.insert(Certificate_Status_Code::DN_TOO_LONG);
+ }
+ }
+
+ // Check all certs for valid time range
+ if(validation_time < subject->not_before())
+ status.insert(Certificate_Status_Code::CERT_NOT_YET_VALID);
+
+ if(validation_time > subject->not_after())
+ status.insert(Certificate_Status_Code::CERT_HAS_EXPIRED);
+
+ // Check issuer constraints
+ if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+
+ std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+
+ // Check the signature algorithm is known
+ if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
+ {
+ status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
+ }
+ else
+ {
+ // only perform the following checks if the signature algorithm is known
+ if(!issuer_key)
+ {
+ status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
+ }
+ else
+ {
+ const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
+
+ if(sig_status != Certificate_Status_Code::VERIFIED)
+ status.insert(sig_status);
+
+ if(issuer_key->estimated_strength() < min_signature_algo_strength)
+ status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+ }
+
+ // Ignore untrusted hashes on self-signed roots
+ if(trusted_hashes.size() > 0 && !at_self_signed_root)
+ {
+ if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
+ status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+ }
+ }
+
+ // Check cert extensions
+
+ if(subject->x509_version() == 1)
+ {
+ if(subject->v2_issuer_key_id().empty() == false ||
+ subject->v2_subject_key_id().empty() == false)
+ {
+ status.insert(Certificate_Status_Code::V2_IDENTIFIERS_IN_V1_CERT);
+ }
+ }
+
+ Extensions extensions = subject->v3_extensions();
+ const auto& extensions_vec = extensions.extensions();
+ if(subject->x509_version() < 3 && !extensions_vec.empty())
+ {
+ status.insert(Certificate_Status_Code::EXT_IN_V1_V2_CERT);
+ }
+ for(auto& extension : extensions_vec)
+ {
+ extension.first->validate(*subject, *issuer, cert_path, cert_status, i);
+ }
+ if(extensions.extensions().size() != extensions.get_extension_oids().size())
+ {
+ status.insert(Certificate_Status_Code::DUPLICATE_CERT_EXTENSION);
+ }
+ }
+
+ // path len check
+ size_t max_path_length = cert_path.size();
+ for(size_t i = cert_path.size() - 1; i > 0 ; --i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
+
+ /*
+ * If the certificate was not self-issued, verify that max_path_length is
+ * greater than zero and decrement max_path_length by 1.
+ */
+ if(subject->subject_dn() != subject->issuer_dn())
+ {
+ if(max_path_length > 0)
+ {
+ --max_path_length;
+ }
+ else
+ {
+ status.insert(Certificate_Status_Code::CERT_CHAIN_TOO_LONG);
+ }
+ }
+
+ /*
+ * If pathLenConstraint is present in the certificate and is less than max_path_length,
+ * set max_path_length to the value of pathLenConstraint.
+ */
+ if(subject->path_limit() != Cert_Extension::NO_CERT_PATH_LIMIT && subject->path_limit() < max_path_length)
+ {
+ max_path_length = subject->path_limit();
+ }
+ }
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_ocsp_age)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_ocsp cert_path empty");
+
+ CertificatePathStatusCodes cert_status(cert_path.size() - 1);
+
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+ if(i < ocsp_responses.size() && (ocsp_responses.at(i) != nullptr)
+ && (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful))
+ {
+ try
+ {
+ Certificate_Status_Code ocsp_signature_status = ocsp_responses.at(i)->check_signature(trusted_certstores, cert_path);
+
+ if(ocsp_signature_status == Certificate_Status_Code::OCSP_SIGNATURE_OK)
+ {
+ // Signature ok, so check the claimed status
+ Certificate_Status_Code ocsp_status = ocsp_responses.at(i)->status_for(*ca, *subject, ref_time, max_ocsp_age);
+ status.insert(ocsp_status);
+ }
+ else
+ {
+ // Some signature problem
+ status.insert(ocsp_signature_status);
+ }
+ }
+ catch(Exception&)
+ {
+ status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
+ }
+ }
+ }
+
+ while(cert_status.size() > 0 && cert_status.back().empty())
+ cert_status.pop_back();
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+ std::chrono::system_clock::time_point ref_time)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+ CertificatePathStatusCodes cert_status(cert_path.size());
+ const X509_Time validation_time(ref_time);
+
+ for(size_t i = 0; i != cert_path.size() - 1; ++i)
+ {
+ std::set<Certificate_Status_Code>& status = cert_status.at(i);
+
+ if(i < crls.size() && crls.at(i))
+ {
+ std::shared_ptr<const X509_Certificate> subject = cert_path.at(i);
+ std::shared_ptr<const X509_Certificate> ca = cert_path.at(i+1);
+
+ if(!ca->allowed_usage(CRL_SIGN))
+ status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CRL_ISSUER);
+
+ if(validation_time < crls[i]->this_update())
+ status.insert(Certificate_Status_Code::CRL_NOT_YET_VALID);
+
+ if(validation_time > crls[i]->next_update())
+ status.insert(Certificate_Status_Code::CRL_HAS_EXPIRED);
+
+ if(crls[i]->check_signature(ca->subject_public_key()) == false)
+ status.insert(Certificate_Status_Code::CRL_BAD_SIGNATURE);
+
+ status.insert(Certificate_Status_Code::VALID_CRL_CHECKED);
+
+ if(crls[i]->is_revoked(*subject))
+ status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
+
+ std::string dp = subject->crl_distribution_point();
+ if(!dp.empty())
+ {
+ if(dp != crls[i]->crl_issuing_distribution_point())
+ {
+ status.insert(Certificate_Status_Code::NO_MATCHING_CRLDP);
+ }
+ }
+
+ for(const auto& extension : crls[i]->extensions().extensions())
+ {
+ // XXX this is wrong - the OID might be defined but the extention not full parsed
+ // for example see #1652
+
+ // is the extension critical and unknown?
+ if(extension.second && OIDS::oid2str_or_empty(extension.first->oid_of()) == "")
+ {
+ /* NIST Certificate Path Valiadation Testing document: "When an implementation does not recognize a critical extension in the
+ * crlExtensions field, it shall assume that identified certificates have been revoked and are no longer valid"
+ */
+ status.insert(Certificate_Status_Code::CERT_IS_REVOKED);
+ }
+ }
+
+ }
+ }
+
+ while(cert_status.size() > 0 && cert_status.back().empty())
+ cert_status.pop_back();
+
+ return cert_status;
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl cert_path empty");
+
+ if(certstores.empty())
+ throw Invalid_Argument("PKIX::check_crl certstores empty");
+
+ std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ BOTAN_ASSERT_NONNULL(cert_path[i]);
+ for(size_t c = 0; c != certstores.size(); ++c)
+ {
+ crls[i] = certstores[c]->find_crl_for(*cert_path[i]);
+ if(crls[i])
+ break;
+ }
+ }
+
+ return PKIX::check_crl(cert_path, crls, ref_time);
+ }
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+CertificatePathStatusCodes
+PKIX::check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout,
+ bool ocsp_check_intermediate_CAs,
+ std::chrono::seconds max_ocsp_age)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_ocsp_online cert_path empty");
+
+ std::vector<std::future<std::shared_ptr<const OCSP::Response>>> ocsp_response_futures;
+
+ size_t to_ocsp = 1;
+
+ if(ocsp_check_intermediate_CAs)
+ to_ocsp = cert_path.size() - 1;
+ if(cert_path.size() == 1)
+ to_ocsp = 0;
+
+ for(size_t i = 0; i < to_ocsp; ++i)
+ {
+ const std::shared_ptr<const X509_Certificate>& subject = cert_path.at(i);
+ const std::shared_ptr<const X509_Certificate>& issuer = cert_path.at(i+1);
+
+ if(subject->ocsp_responder() == "")
+ {
+ ocsp_response_futures.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const OCSP::Response> {
+ return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_NO_REVOCATION_URL);
+ }));
+ }
+ else
+ {
+ ocsp_response_futures.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const OCSP::Response> {
+ OCSP::Request req(*issuer, BigInt::decode(subject->serial_number()));
+
+ HTTP::Response http;
+ try
+ {
+ http = HTTP::POST_sync(subject->ocsp_responder(),
+ "application/ocsp-request",
+ req.BER_encode(),
+ /*redirects*/1,
+ timeout);
+ }
+ catch(std::exception&)
+ {
+ // log e.what() ?
+ }
+ if (http.status_code() != 200)
+ return std::make_shared<const OCSP::Response>(Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE);
+ // Check the MIME type?
+
+ return std::make_shared<const OCSP::Response>(http.body());
+ }));
+ }
+ }
+
+ std::vector<std::shared_ptr<const OCSP::Response>> ocsp_responses;
+
+ for(size_t i = 0; i < ocsp_response_futures.size(); ++i)
+ {
+ ocsp_responses.push_back(ocsp_response_futures[i].get());
+ }
+
+ return PKIX::check_ocsp(cert_path, ocsp_responses, trusted_certstores, ref_time, max_ocsp_age);
+ }
+
+CertificatePathStatusCodes
+PKIX::check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ Certificate_Store_In_Memory* crl_store,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout)
+ {
+ if(cert_path.empty())
+ throw Invalid_Argument("PKIX::check_crl_online cert_path empty");
+ if(certstores.empty())
+ throw Invalid_Argument("PKIX::check_crl_online certstores empty");
+
+ std::vector<std::future<std::shared_ptr<const X509_CRL>>> future_crls;
+ std::vector<std::shared_ptr<const X509_CRL>> crls(cert_path.size());
+
+ for(size_t i = 0; i != cert_path.size(); ++i)
+ {
+ const std::shared_ptr<const X509_Certificate>& cert = cert_path.at(i);
+ for(size_t c = 0; c != certstores.size(); ++c)
+ {
+ crls[i] = certstores[c]->find_crl_for(*cert);
+ if(crls[i])
+ break;
+ }
+
+ // TODO: check if CRL is expired and re-request?
+
+ // Only request if we don't already have a CRL
+ if(crls[i])
+ {
+ /*
+ We already have a CRL, so just insert this empty one to hold a place in the vector
+ so that indexes match up
+ */
+ future_crls.emplace_back(std::future<std::shared_ptr<const X509_CRL>>());
+ }
+ else if(cert->crl_distribution_point() == "")
+ {
+ // Avoid creating a thread for this case
+ future_crls.emplace_back(std::async(std::launch::deferred, [&]() -> std::shared_ptr<const X509_CRL> {
+ throw Not_Implemented("No CRL distribution point for this certificate");
+ }));
+ }
+ else
+ {
+ future_crls.emplace_back(std::async(std::launch::async, [&]() -> std::shared_ptr<const X509_CRL> {
+ auto http = HTTP::GET_sync(cert->crl_distribution_point(),
+ /*redirects*/ 1, timeout);
+
+ http.throw_unless_ok();
+ // check the mime type?
+ return std::make_shared<const X509_CRL>(http.body());
+ }));
+ }
+ }
+
+ for(size_t i = 0; i != future_crls.size(); ++i)
+ {
+ if(future_crls[i].valid())
+ {
+ try
+ {
+ crls[i] = future_crls[i].get();
+ }
+ catch(std::exception&)
+ {
+ // crls[i] left null
+ // todo: log exception e.what() ?
+ }
+ }
+ }
+
+ const CertificatePathStatusCodes crl_status = PKIX::check_crl(cert_path, crls, ref_time);
+
+ if(crl_store)
+ {
+ for(size_t i = 0; i != crl_status.size(); ++i)
+ {
+ if(crl_status[i].count(Certificate_Status_Code::VALID_CRL_CHECKED))
+ {
+ // better be non-null, we supposedly validated it
+ BOTAN_ASSERT_NONNULL(crls[i]);
+ crl_store->add_crl(crls[i]);
+ }
+ }
+ }
+
+ return crl_status;
+ }
+
+#endif
+
+Certificate_Status_Code
+PKIX::build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+ {
+ if(end_entity->is_self_signed())
+ {
+ return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+ }
+
+ /*
+ * This is an inelegant but functional way of preventing path loops
+ * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
+ * fingerprints in the path. If there is a duplicate, we error out.
+ * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
+ */
+ std::set<std::string> certs_seen;
+
+ cert_path.push_back(end_entity);
+ certs_seen.insert(end_entity->fingerprint("SHA-256"));
+
+ Certificate_Store_In_Memory ee_extras;
+ for(size_t i = 0; i != end_entity_extra.size(); ++i)
+ ee_extras.add_certificate(end_entity_extra[i]);
+
+ // iterate until we reach a root or cannot find the issuer
+ for(;;)
+ {
+ const X509_Certificate& last = *cert_path.back();
+ const X509_DN issuer_dn = last.issuer_dn();
+ const std::vector<uint8_t> auth_key_id = last.authority_key_id();
+
+ std::shared_ptr<const X509_Certificate> issuer;
+ bool trusted_issuer = false;
+
+ for(Certificate_Store* store : trusted_certstores)
+ {
+ issuer = store->find_cert(issuer_dn, auth_key_id);
+ if(issuer)
+ {
+ trusted_issuer = true;
+ break;
+ }
+ }
+
+ if(!issuer)
+ {
+ // fall back to searching supplemental certs
+ issuer = ee_extras.find_cert(issuer_dn, auth_key_id);
+ }
+
+ if(!issuer)
+ return Certificate_Status_Code::CERT_ISSUER_NOT_FOUND;
+
+ const std::string fprint = issuer->fingerprint("SHA-256");
+
+ if(certs_seen.count(fprint) > 0) // already seen?
+ {
+ return Certificate_Status_Code::CERT_CHAIN_LOOP;
+ }
+
+ certs_seen.insert(fprint);
+ cert_path.push_back(issuer);
+
+ if(issuer->is_self_signed())
+ {
+ if(trusted_issuer)
+ {
+ return Certificate_Status_Code::OK;
+ }
+ else
+ {
+ return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+ }
+ }
+ }
+ }
+
+/**
+ * utilities for PKIX::build_all_certificate_paths
+ */
+namespace
+{
+// <certificate, trusted?>
+using cert_maybe_trusted = std::pair<std::shared_ptr<const X509_Certificate>,bool>;
+}
+
+/**
+ * Build all possible certificate paths from the end certificate to self-signed trusted roots.
+ *
+ * All potentially valid paths are put into the cert_paths vector. If no potentially valid paths are found,
+ * one of the encountered errors is returned arbitrarily.
+ *
+ * todo add a path building function that returns detailed information on errors encountered while building
+ * the potentially numerous path candidates.
+ *
+ * Basically, a DFS is performed starting from the end certificate. A stack (vector) serves to control the DFS.
+ * At the beginning of each iteration, a pair is popped from the stack that contains (1) the next certificate
+ * to add to the path (2) a bool that indicates if the certificate is part of a trusted certstore. Ideally, we
+ * follow the unique issuer of the current certificate until a trusted root is reached. However, the issuer DN +
+ * authority key id need not be unique among the certificates used for building the path. In such a case,
+ * we consider all the matching issuers by pushing <IssuerCert, trusted?> on the stack for each of them.
+ *
+ */
+Certificate_Status_Code
+PKIX::build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths_out,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra)
+ {
+ if(!cert_paths_out.empty())
+ {
+ throw Invalid_Argument("PKIX::build_all_certificate_paths: cert_paths_out must be empty");
+ }
+
+ if(end_entity->is_self_signed())
+ {
+ return Certificate_Status_Code::CANNOT_ESTABLISH_TRUST;
+ }
+
+ /*
+ * Pile up error messages
+ */
+ std::vector<Certificate_Status_Code> stats;
+
+ Certificate_Store_In_Memory ee_extras;
+ for(size_t i = 0; i != end_entity_extra.size(); ++i)
+ {
+ ee_extras.add_certificate(end_entity_extra[i]);
+ }
+
+ /*
+ * This is an inelegant but functional way of preventing path loops
+ * (where C1 -> C2 -> C3 -> C1). We store a set of all the certificate
+ * fingerprints in the path. If there is a duplicate, we error out.
+ * TODO: save fingerprints in result struct? Maybe useful for blacklists, etc.
+ */
+ std::set<std::string> certs_seen;
+
+ // new certs are added and removed from the path during the DFS
+ // it is copied into cert_paths_out when we encounter a trusted root
+ std::vector<std::shared_ptr<const X509_Certificate>> path_so_far;
+
+ // todo can we assume that the end certificate is not trusted?
+ std::vector<cert_maybe_trusted> stack = { {end_entity, false} };
+
+ while(!stack.empty())
+ {
+ // found a deletion marker that guides the DFS, backtracing
+ if(stack.back().first == nullptr)
+ {
+ stack.pop_back();
+ std::string fprint = path_so_far.back()->fingerprint("SHA-256");
+ certs_seen.erase(fprint);
+ path_so_far.pop_back();
+ }
+ // process next cert on the path
+ else
+ {
+ std::shared_ptr<const X509_Certificate> last = stack.back().first;
+ bool trusted = stack.back().second;
+ stack.pop_back();
+
+ // certificate already seen?
+ const std::string fprint = last->fingerprint("SHA-256");
+ if(certs_seen.count(fprint) == 1)
+ {
+ stats.push_back(Certificate_Status_Code::CERT_CHAIN_LOOP);
+ // the current path ended in a loop
+ continue;
+ }
+
+ // the current path ends here
+ if(last->is_self_signed())
+ {
+ // found a trust anchor
+ if(trusted)
+ {
+ cert_paths_out.push_back(path_so_far);
+ cert_paths_out.back().push_back(last);
+
+ continue;
+ }
+ // found an untrustworthy root
+ else
+ {
+ stats.push_back(Certificate_Status_Code::CANNOT_ESTABLISH_TRUST);
+ continue;
+ }
+ }
+
+ const X509_DN issuer_dn = last->issuer_dn();
+ const std::vector<uint8_t> auth_key_id = last->authority_key_id();
+
+ // search for trusted issuers
+ std::vector<std::shared_ptr<const X509_Certificate>> trusted_issuers;
+ for(Certificate_Store* store : trusted_certstores)
+ {
+ auto new_issuers = store->find_all_certs(issuer_dn, auth_key_id);
+ trusted_issuers.insert(trusted_issuers.end(), new_issuers.begin(), new_issuers.end());
+ }
+
+ // search the supplemental certs
+ std::vector<std::shared_ptr<const X509_Certificate>> misc_issuers =
+ ee_extras.find_all_certs(issuer_dn, auth_key_id);
+
+ // if we could not find any issuers, the current path ends here
+ if(trusted_issuers.size() + misc_issuers.size() == 0)
+ {
+ stats.push_back(Certificate_Status_Code::CERT_ISSUER_NOT_FOUND);
+ continue;
+ }
+
+ // push the latest certificate onto the path_so_far
+ path_so_far.push_back(last);
+ certs_seen.emplace(fprint);
+
+ // push a deletion marker on the stack for backtracing later
+ stack.push_back({std::shared_ptr<const X509_Certificate>(nullptr),false});
+
+ for(const auto& trusted_cert : trusted_issuers)
+ {
+ stack.push_back({trusted_cert,true});
+ }
+
+ for(const auto& misc : misc_issuers)
+ {
+ stack.push_back({misc,false});
+ }
+ }
+ }
+
+ // could not construct any potentially valid path
+ if(cert_paths_out.empty())
+ {
+ if(stats.empty())
+ throw Internal_Error("X509 path building failed for unknown reasons");
+ else
+ // arbitrarily return the first error
+ return stats[0];
+ }
+ else
+ {
+ return Certificate_Status_Code::OK;
+ }
+ }
+
+
+void PKIX::merge_revocation_status(CertificatePathStatusCodes& chain_status,
+ const CertificatePathStatusCodes& crl,
+ const CertificatePathStatusCodes& ocsp,
+ bool require_rev_on_end_entity,
+ bool require_rev_on_intermediates)
+ {
+ if(chain_status.empty())
+ throw Invalid_Argument("PKIX::merge_revocation_status chain_status was empty");
+
+ for(size_t i = 0; i != chain_status.size() - 1; ++i)
+ {
+ bool had_crl = false, had_ocsp = false;
+
+ if(i < crl.size() && crl[i].size() > 0)
+ {
+ for(auto&& code : crl[i])
+ {
+ if(code == Certificate_Status_Code::VALID_CRL_CHECKED)
+ {
+ had_crl = true;
+ }
+ chain_status[i].insert(code);
+ }
+ }
+
+ if(i < ocsp.size() && ocsp[i].size() > 0)
+ {
+ for(auto&& code : ocsp[i])
+ {
+ if(code == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+ code == Certificate_Status_Code::OCSP_NO_REVOCATION_URL || // softfail
+ code == Certificate_Status_Code::OCSP_SERVER_NOT_AVAILABLE) // softfail
+ {
+ had_ocsp = true;
+ }
+
+ chain_status[i].insert(code);
+ }
+ }
+
+ if(had_crl == false && had_ocsp == false)
+ {
+ if((require_rev_on_end_entity && i == 0) ||
+ (require_rev_on_intermediates && i > 0))
+ {
+ chain_status[i].insert(Certificate_Status_Code::NO_REVOCATION_DATA);
+ }
+ }
+ }
+ }
+
+Certificate_Status_Code PKIX::overall_status(const CertificatePathStatusCodes& cert_status)
+ {
+ if(cert_status.empty())
+ throw Invalid_Argument("PKIX::overall_status empty cert status");
+
+ Certificate_Status_Code overall_status = Certificate_Status_Code::OK;
+
+ // take the "worst" error as overall
+ for(const std::set<Certificate_Status_Code>& s : cert_status)
+ {
+ if(!s.empty())
+ {
+ auto worst = *s.rbegin();
+ // Leave informative OCSP/CRL confirmations on cert-level status only
+ if(worst >= Certificate_Status_Code::FIRST_ERROR_STATUS && worst > overall_status)
+ {
+ overall_status = worst;
+ }
+ }
+ }
+ return overall_status;
+ }
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ if(end_certs.empty())
+ {
+ throw Invalid_Argument("x509_path_validate called with no subjects");
+ }
+
+ std::shared_ptr<const X509_Certificate> end_entity(std::make_shared<const X509_Certificate>(end_certs[0]));
+ std::vector<std::shared_ptr<const X509_Certificate>> end_entity_extra;
+ for(size_t i = 1; i < end_certs.size(); ++i)
+ {
+ end_entity_extra.push_back(std::make_shared<const X509_Certificate>(end_certs[i]));
+ }
+
+ std::vector<std::vector<std::shared_ptr<const X509_Certificate>>> cert_paths;
+ Certificate_Status_Code path_building_result = PKIX::build_all_certificate_paths(cert_paths, trusted_roots, end_entity, end_entity_extra);
+
+ // If we cannot successfully build a chain to a trusted self-signed root, stop now
+ if(path_building_result != Certificate_Status_Code::OK)
+ {
+ return Path_Validation_Result(path_building_result);
+ }
+
+ std::vector<Path_Validation_Result> error_results;
+ // Try validating all the potentially valid paths and return the first one to validate properly
+ for(auto cert_path : cert_paths)
+ {
+ CertificatePathStatusCodes status =
+ PKIX::check_chain(cert_path, ref_time,
+ hostname, usage,
+ restrictions.minimum_key_strength(),
+ restrictions.trusted_hashes());
+
+ CertificatePathStatusCodes crl_status =
+ PKIX::check_crl(cert_path, trusted_roots, ref_time);
+
+ CertificatePathStatusCodes ocsp_status;
+
+ if(ocsp_resp.size() > 0)
+ {
+ ocsp_status = PKIX::check_ocsp(cert_path, ocsp_resp, trusted_roots, ref_time, restrictions.max_ocsp_age());
+ }
+
+ if(ocsp_status.empty() && ocsp_timeout != std::chrono::milliseconds(0))
+ {
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+ ocsp_status = PKIX::check_ocsp_online(cert_path, trusted_roots, ref_time,
+ ocsp_timeout, restrictions.ocsp_all_intermediates());
+#else
+ ocsp_status.resize(1);
+ ocsp_status[0].insert(Certificate_Status_Code::OCSP_NO_HTTP);
+#endif
+ }
+
+ PKIX::merge_revocation_status(status, crl_status, ocsp_status,
+ restrictions.require_revocation_information(),
+ restrictions.ocsp_all_intermediates());
+
+ Path_Validation_Result pvd(status, std::move(cert_path));
+ if(pvd.successful_validation())
+ {
+ return pvd;
+ }
+ else
+ {
+ error_results.push_back(std::move(pvd));
+ }
+ }
+ return error_results[0];
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+ return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<Certificate_Store*> trusted_roots;
+ trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(end_certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Result x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname,
+ Usage_Type usage,
+ std::chrono::system_clock::time_point when,
+ std::chrono::milliseconds ocsp_timeout,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp)
+ {
+ std::vector<X509_Certificate> certs;
+ certs.push_back(end_cert);
+
+ std::vector<Certificate_Store*> trusted_roots;
+ trusted_roots.push_back(const_cast<Certificate_Store*>(&store));
+
+ return x509_path_validate(certs, restrictions, trusted_roots, hostname, usage, when, ocsp_timeout, ocsp_resp);
+ }
+
+Path_Validation_Restrictions::Path_Validation_Restrictions(bool require_rev,
+ size_t key_strength,
+ bool ocsp_intermediates,
+ std::chrono::seconds max_ocsp_age) :
+ m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_intermediates),
+ m_minimum_key_strength(key_strength),
+ m_max_ocsp_age(max_ocsp_age)
+ {
+ if(key_strength <= 80)
+ { m_trusted_hashes.insert("SHA-160"); }
+
+ m_trusted_hashes.insert("SHA-224");
+ m_trusted_hashes.insert("SHA-256");
+ m_trusted_hashes.insert("SHA-384");
+ m_trusted_hashes.insert("SHA-512");
+ }
+
+namespace {
+CertificatePathStatusCodes find_warnings(const CertificatePathStatusCodes& all_statuses)
+ {
+ CertificatePathStatusCodes warnings;
+ for(const auto& status_set_i : all_statuses)
+ {
+ std::set<Certificate_Status_Code> warning_set_i;
+ for(const auto& code : status_set_i)
+ {
+ if(code >= Certificate_Status_Code::FIRST_WARNING_STATUS &&
+ code < Certificate_Status_Code::FIRST_ERROR_STATUS)
+ {
+ warning_set_i.insert(code);
+ }
+ }
+ warnings.push_back(warning_set_i);
+ }
+ return warnings;
+ }
+}
+
+Path_Validation_Result::Path_Validation_Result(CertificatePathStatusCodes status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain) :
+ m_all_status(status),
+ m_warnings(find_warnings(m_all_status)),
+ m_cert_path(cert_chain),
+ m_overall(PKIX::overall_status(m_all_status))
+ {
+ }
+
+const X509_Certificate& Path_Validation_Result::trust_root() const
+ {
+ if(m_cert_path.empty())
+ throw Invalid_State("Path_Validation_Result::trust_root no path set");
+ if(result() != Certificate_Status_Code::VERIFIED)
+ throw Invalid_State("Path_Validation_Result::trust_root meaningless with invalid status");
+
+ return *m_cert_path[m_cert_path.size()-1];
+ }
+
+std::set<std::string> Path_Validation_Result::trusted_hashes() const
+ {
+ std::set<std::string> hashes;
+ for(size_t i = 0; i != m_cert_path.size(); ++i)
+ hashes.insert(m_cert_path[i]->hash_used_for_signature());
+ return hashes;
+ }
+
+bool Path_Validation_Result::successful_validation() const
+ {
+ return (result() == Certificate_Status_Code::VERIFIED ||
+ result() == Certificate_Status_Code::OCSP_RESPONSE_GOOD ||
+ result() == Certificate_Status_Code::VALID_CRL_CHECKED);
+ }
+
+bool Path_Validation_Result::no_warnings() const
+ {
+ for(auto status_set_i : m_warnings)
+ if(!status_set_i.empty())
+ return false;
+ return true;
+ }
+
+CertificatePathStatusCodes Path_Validation_Result::warnings() const
+ {
+ return m_warnings;
+ }
+
+std::string Path_Validation_Result::result_string() const
+ {
+ return status_string(result());
+ }
+
+const char* Path_Validation_Result::status_string(Certificate_Status_Code code)
+ {
+ if(const char* s = to_string(code))
+ return s;
+
+ return "Unknown error";
+ }
+
+std::string Path_Validation_Result::warnings_string() const
+ {
+ const std::string sep(", ");
+ std::string res;
+ for(size_t i = 0; i < m_warnings.size(); i++)
+ {
+ for(auto code : m_warnings[i])
+ res += "[" + std::to_string(i) + "] " + status_string(code) + sep;
+ }
+ // remove last sep
+ if(res.size() >= sep.size())
+ res = res.substr(0, res.size() - sep.size());
+ return res;
+ }
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509path.h b/comm/third_party/botan/src/lib/x509/x509path.h
new file mode 100644
index 0000000000..c8575fd32b
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509path.h
@@ -0,0 +1,475 @@
+/*
+* X.509 Cert Path Validation
+* (C) 2010-2011 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_CERT_PATH_VALIDATION_H_
+#define BOTAN_X509_CERT_PATH_VALIDATION_H_
+
+#include <botan/pkix_enums.h>
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/ocsp.h>
+#include <functional>
+#include <set>
+#include <chrono>
+
+#if defined(BOTAN_TARGET_OS_HAS_THREADS) && defined(BOTAN_HAS_HTTP_UTIL)
+ #define BOTAN_HAS_ONLINE_REVOCATION_CHECKS
+#endif
+
+namespace Botan {
+
+/**
+* This type represents the validation status of an entire certificate path.
+* There is one set of status codes for each certificate in the path.
+*/
+typedef std::vector<std::set<Certificate_Status_Code>> CertificatePathStatusCodes;
+
+/**
+* Specifies restrictions on the PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Restrictions final
+ {
+ public:
+ /**
+ * @param require_rev if true, revocation information is required
+
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures weaker than
+ * this are rejected. If more than 80, SHA-1 signatures are also
+ * rejected. If possible use at least setting 110.
+ *
+ * 80 bit strength requires 1024 bit RSA
+ * 110 bit strength requires 2k bit RSA
+ * 128 bit strength requires ~3k bit RSA or P-256
+ * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+ * well as end entity (if OCSP enabled in path validation request)
+ * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ Path_Validation_Restrictions(bool require_rev = false,
+ size_t minimum_key_strength = 110,
+ bool ocsp_all_intermediates = false,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+ /**
+ * @param require_rev if true, revocation information is required
+ * @param minimum_key_strength is the minimum strength (in terms of
+ * operations, eg 80 means 2^80) of a signature. Signatures
+ * weaker than this are rejected.
+ * @param ocsp_all_intermediates Make OCSP requests for all CAs as
+ * well as end entity (if OCSP enabled in path validation request)
+ * @param trusted_hashes a set of trusted hashes. Any signatures
+ * created using a hash other than one of these will be
+ * rejected.
+ * @param max_ocsp_age maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ Path_Validation_Restrictions(bool require_rev,
+ size_t minimum_key_strength,
+ bool ocsp_all_intermediates,
+ const std::set<std::string>& trusted_hashes,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero()) :
+ m_require_revocation_information(require_rev),
+ m_ocsp_all_intermediates(ocsp_all_intermediates),
+ m_trusted_hashes(trusted_hashes),
+ m_minimum_key_strength(minimum_key_strength),
+ m_max_ocsp_age(max_ocsp_age) {}
+
+ /**
+ * @return whether revocation information is required
+ */
+ bool require_revocation_information() const
+ { return m_require_revocation_information; }
+
+ /**
+ * @return whether all intermediate CAs should also be OCSPed. If false
+ * then only end entity OCSP is required/requested.
+ */
+ bool ocsp_all_intermediates() const
+ { return m_ocsp_all_intermediates; }
+
+ /**
+ * @return trusted signature hash functions
+ */
+ const std::set<std::string>& trusted_hashes() const
+ { return m_trusted_hashes; }
+
+ /**
+ * @return minimum required key strength
+ */
+ size_t minimum_key_strength() const
+ { return m_minimum_key_strength; }
+
+ /**
+ * @return maximum age of OCSP responses w/o next_update.
+ * If zero, there is no maximum age
+ */
+ std::chrono::seconds max_ocsp_age() const
+ { return m_max_ocsp_age; }
+
+ private:
+ bool m_require_revocation_information;
+ bool m_ocsp_all_intermediates;
+ std::set<std::string> m_trusted_hashes;
+ size_t m_minimum_key_strength;
+ std::chrono::seconds m_max_ocsp_age;
+ };
+
+/**
+* Represents the result of a PKIX path validation
+*/
+class BOTAN_PUBLIC_API(2,0) Path_Validation_Result final
+ {
+ public:
+ typedef Certificate_Status_Code Code;
+
+ /**
+ * @return the set of hash functions you are implicitly
+ * trusting by trusting this result.
+ */
+ std::set<std::string> trusted_hashes() const;
+
+ /**
+ * @return the trust root of the validation if successful
+ * throws an exception if the validation failed
+ */
+ const X509_Certificate& trust_root() const;
+
+ /**
+ * @return the full path from subject to trust root
+ * This path may be empty
+ */
+ const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path() const { return m_cert_path; }
+
+ /**
+ * @return true iff the validation was successful
+ */
+ bool successful_validation() const;
+
+ /**
+ * @return true iff no warnings occured during validation
+ */
+ bool no_warnings() const;
+
+ /**
+ * @return overall validation result code
+ */
+ Certificate_Status_Code result() const { return m_overall; }
+
+ /**
+ * @return a set of status codes for each certificate in the chain
+ */
+ const CertificatePathStatusCodes& all_statuses() const
+ { return m_all_status; }
+
+ /**
+ * @return the subset of status codes that are warnings
+ */
+ CertificatePathStatusCodes warnings() const;
+
+ /**
+ * @return string representation of the validation result
+ */
+ std::string result_string() const;
+
+ /**
+ * @return string representation of the warnings
+ */
+ std::string warnings_string() const;
+
+ /**
+ * @param code validation status code
+ * @return corresponding validation status message
+ */
+ static const char* status_string(Certificate_Status_Code code);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status list of validation status codes
+ * @param cert_chain the certificate chain that was validated
+ */
+ Path_Validation_Result(CertificatePathStatusCodes status,
+ std::vector<std::shared_ptr<const X509_Certificate>>&& cert_chain);
+
+ /**
+ * Create a Path_Validation_Result
+ * @param status validation status code
+ */
+ explicit Path_Validation_Result(Certificate_Status_Code status) : m_overall(status) {}
+
+ private:
+ CertificatePathStatusCodes m_all_status;
+ CertificatePathStatusCodes m_warnings;
+ std::vector<std::shared_ptr<const X509_Certificate>> m_cert_path;
+ Certificate_Status_Code m_overall;
+ };
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate (with end entity certificate in end_certs[0])
+* @param restrictions path validation restrictions
+* @param trusted_roots list of certificate stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+* note: when enabled, OCSP check is softfail by default: if the OCSP server is not
+* reachable, Path_Validation_Result::successful_validation() will return true.
+* Hardfail OCSP check can be achieve by also calling Path_Validation_Result::no_warnings().
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param trusted_roots list of stores that contain trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const std::vector<Certificate_Store*>& trusted_roots,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_cert certificate to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_cert
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_cert
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const X509_Certificate& end_cert,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+/**
+* PKIX Path Validation
+* @param end_certs certificate chain to validate
+* @param restrictions path validation restrictions
+* @param store store that contains trusted certificates
+* @param hostname if not empty, compared against the DNS name in end_certs[0]
+* @param usage if not set to UNSPECIFIED, compared against the key usage in end_certs[0]
+* @param validation_time what reference time to use for validation
+* @param ocsp_timeout timeout for OCSP operations, 0 disables OCSP check
+* @param ocsp_resp additional OCSP responses to consider (eg from peer)
+* @return result of the path validation
+*/
+Path_Validation_Result BOTAN_PUBLIC_API(2,0) x509_path_validate(
+ const std::vector<X509_Certificate>& end_certs,
+ const Path_Validation_Restrictions& restrictions,
+ const Certificate_Store& store,
+ const std::string& hostname = "",
+ Usage_Type usage = Usage_Type::UNSPECIFIED,
+ std::chrono::system_clock::time_point validation_time = std::chrono::system_clock::now(),
+ std::chrono::milliseconds ocsp_timeout = std::chrono::milliseconds(0),
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_resp = {});
+
+
+/**
+* namespace PKIX holds the building blocks that are called by x509_path_validate.
+* This allows custom validation logic to be written by applications and makes
+* for easier testing, but unless you're positive you know what you're doing you
+* probably want to just call x509_path_validate instead.
+*/
+namespace PKIX {
+
+Certificate_Status_Code
+build_all_certificate_paths(std::vector<std::vector<std::shared_ptr<const X509_Certificate>>>& cert_paths,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+
+/**
+* Build certificate path
+* @param cert_path_out output parameter, cert_path will be appended to this vector
+* @param trusted_certstores list of certificate stores that contain trusted certificates
+* @param end_entity the cert to be validated
+* @param end_entity_extra optional list of additional untrusted certs for path building
+* @return result of the path building operation (OK or error)
+*/
+Certificate_Status_Code
+BOTAN_PUBLIC_API(2,0) build_certificate_path(std::vector<std::shared_ptr<const X509_Certificate>>& cert_path_out,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ const std::shared_ptr<const X509_Certificate>& end_entity,
+ const std::vector<std::shared_ptr<const X509_Certificate>>& end_entity_extra);
+
+/**
+* Check the certificate chain, but not any revocation data
+*
+* @param cert_path path built by build_certificate_path with OK result
+* @param ref_time whatever time you want to perform the validation
+* against (normally current system clock)
+* @param hostname the hostname
+* @param usage end entity usage checks
+* @param min_signature_algo_strength 80 or 110 typically
+* Note 80 allows 1024 bit RSA and SHA-1. 110 allows 2048 bit RSA and SHA-2.
+* Using 128 requires ECC (P-256) or ~3000 bit RSA keys.
+* @param trusted_hashes set of trusted hash functions, empty means accept any
+* hash we have an OID for
+* @return vector of results on per certificate in the path, each containing a set of
+* results. If all codes in the set are < Certificate_Status_Code::FIRST_ERROR_STATUS,
+* then the result for that certificate is successful. If all results are
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_chain(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ std::chrono::system_clock::time_point ref_time,
+ const std::string& hostname,
+ Usage_Type usage,
+ size_t min_signature_algo_strength,
+ const std::set<std::string>& trusted_hashes);
+
+/**
+* Check OCSP responses for revocation information
+* @param cert_path path already validated by check_chain
+* @param ocsp_responses the OCSP responses to consider
+* @param certstores trusted roots
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param crls the list of CRLs to check, it is assumed that crls[i] (if not null)
+* is the associated CRL for the subject in cert_path[i].
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<std::shared_ptr<const X509_CRL>>& crls,
+ std::chrono::system_clock::time_point ref_time);
+
+/**
+* Check CRLs for revocation information
+* @param cert_path path already validated by check_chain
+* @param certstores a list of certificate stores to query for the CRL
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& certstores,
+ std::chrono::system_clock::time_point ref_time);
+
+#if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
+
+/**
+* Check OCSP using online (HTTP) access. Current version creates a thread and
+* network connection per OCSP request made.
+*
+* @param cert_path path already validated by check_chain
+* @param trusted_certstores a list of certstores with trusted certs
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param timeout for timing out the responses, though actually this function
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @param ocsp_check_intermediate_CAs if true also performs OCSP on any intermediate
+* CA certificates. If false, only does OCSP on the end entity cert.
+* @param max_ocsp_age maximum age of OCSP responses w/o next_update. If zero,
+* there is no maximum age
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2, 0) check_ocsp_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout,
+ bool ocsp_check_intermediate_CAs,
+ std::chrono::seconds max_ocsp_age = std::chrono::seconds::zero());
+
+/**
+* Check CRL using online (HTTP) access. Current version creates a thread and
+* network connection per CRL access.
+
+* @param cert_path path already validated by check_chain
+* @param trusted_certstores a list of certstores with trusted certs
+* @param certstore_to_recv_crls optional (nullptr to disable), all CRLs
+* retreived will be saved to this cert store.
+* @param ref_time whatever time you want to perform the validation against
+* (normally current system clock)
+* @param timeout for timing out the responses, though actually this function
+* may block for up to timeout*cert_path.size()*C for some small C.
+* @return revocation status
+*/
+CertificatePathStatusCodes
+BOTAN_PUBLIC_API(2,0) check_crl_online(const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+ const std::vector<Certificate_Store*>& trusted_certstores,
+ Certificate_Store_In_Memory* certstore_to_recv_crls,
+ std::chrono::system_clock::time_point ref_time,
+ std::chrono::milliseconds timeout);
+
+#endif
+
+/**
+* Find overall status (OK, error) of a validation
+* @param cert_status result of merge_revocation_status or check_chain
+*/
+Certificate_Status_Code BOTAN_PUBLIC_API(2,0) overall_status(const CertificatePathStatusCodes& cert_status);
+
+/**
+* Merge the results from CRL and/or OCSP checks into chain_status
+* @param chain_status the certificate status
+* @param crl_status results from check_crl
+* @param ocsp_status results from check_ocsp
+* @param require_rev_on_end_entity require valid CRL or OCSP on end-entity cert
+* @param require_rev_on_intermediates require valid CRL or OCSP on all intermediate certificates
+*/
+void BOTAN_PUBLIC_API(2,0) merge_revocation_status(CertificatePathStatusCodes& chain_status,
+ const CertificatePathStatusCodes& crl_status,
+ const CertificatePathStatusCodes& ocsp_status,
+ bool require_rev_on_end_entity,
+ bool require_rev_on_intermediates);
+
+}
+
+}
+
+#endif
diff --git a/comm/third_party/botan/src/lib/x509/x509self.cpp b/comm/third_party/botan/src/lib/x509/x509self.cpp
new file mode 100644
index 0000000000..f75827fd83
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509self.cpp
@@ -0,0 +1,152 @@
+/*
+* PKCS #10/Self Signed Cert Creation
+* (C) 1999-2008,2018 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/x509self.h>
+#include <botan/x509_key.h>
+#include <botan/x509_ext.h>
+#include <botan/x509_ca.h>
+#include <botan/der_enc.h>
+#include <botan/pubkey.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Load information from the X509_Cert_Options
+*/
+void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
+ AlternativeName& subject_alt)
+ {
+ subject_dn.add_attribute("X520.CommonName", opts.common_name);
+ subject_dn.add_attribute("X520.Country", opts.country);
+ subject_dn.add_attribute("X520.State", opts.state);
+ subject_dn.add_attribute("X520.Locality", opts.locality);
+ subject_dn.add_attribute("X520.Organization", opts.organization);
+ subject_dn.add_attribute("X520.OrganizationalUnit", opts.org_unit);
+ for(auto extra_ou : opts.more_org_units) {
+ subject_dn.add_attribute("X520.OrganizationalUnit", extra_ou);
+ }
+
+ subject_dn.add_attribute("X520.SerialNumber", opts.serial_number);
+ subject_alt = AlternativeName(opts.email, opts.uri, opts.dns, opts.ip);
+ subject_alt.add_othername(OID::from_string("PKIX.XMPPAddr"),
+ opts.xmpp, UTF8_STRING);
+
+ for(auto dns : opts.more_dns)
+ subject_alt.add_attribute("DNS", dns);
+ }
+}
+
+namespace X509 {
+
+/*
+* Create a new self-signed X.509 certificate
+*/
+X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ AlgorithmIdentifier sig_algo;
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+
+ // for now, only the padding option is used
+ std::map<std::string,std::string> sig_opts = { {"padding",opts.padding_scheme} };
+
+ const std::vector<uint8_t> pub_key = X509::BER_encode(key);
+ std::unique_ptr<PK_Signer> signer(choose_sig_format(key, sig_opts, rng, hash_fn, sig_algo));
+ BOTAN_ASSERT_NOMSG(sig_algo.get_oid().has_value());
+ load_info(opts, subject_dn, subject_alt);
+
+ Extensions extensions = opts.extensions;
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ extensions.add_new(
+ new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit),
+ true);
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add_new(new Cert_Extension::Key_Usage(constraints), true);
+ }
+
+ std::unique_ptr<Cert_Extension::Subject_Key_ID> skid(new Cert_Extension::Subject_Key_ID(pub_key, hash_fn));
+
+ extensions.add_new(new Cert_Extension::Authority_Key_ID(skid->get_key_id()));
+ extensions.add_new(skid.release());
+
+ extensions.add_new(
+ new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ extensions.add_new(
+ new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+
+ return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key,
+ opts.start, opts.end,
+ subject_dn, subject_dn,
+ extensions);
+ }
+
+/*
+* Create a PKCS #10 certificate request
+*/
+PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng)
+ {
+ X509_DN subject_dn;
+ AlternativeName subject_alt;
+ load_info(opts, subject_dn, subject_alt);
+
+ Key_Constraints constraints;
+ if(opts.is_CA)
+ {
+ constraints = Key_Constraints(KEY_CERT_SIGN | CRL_SIGN);
+ }
+ else
+ {
+ verify_cert_constraints_valid_for_key_type(key, opts.constraints);
+ constraints = opts.constraints;
+ }
+
+ Extensions extensions = opts.extensions;
+
+ extensions.add_new(new Cert_Extension::Basic_Constraints(opts.is_CA, opts.path_limit));
+
+ if(constraints != NO_CONSTRAINTS)
+ {
+ extensions.add_new(new Cert_Extension::Key_Usage(constraints));
+ }
+ extensions.add_new(new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
+ extensions.add_new(new Cert_Extension::Subject_Alternative_Name(subject_alt));
+
+ return PKCS10_Request::create(key,
+ subject_dn,
+ extensions,
+ hash_fn,
+ rng,
+ opts.padding_scheme,
+ opts.challenge);
+ }
+
+}
+
+}
diff --git a/comm/third_party/botan/src/lib/x509/x509self.h b/comm/third_party/botan/src/lib/x509/x509self.h
new file mode 100644
index 0000000000..27c30b12dc
--- /dev/null
+++ b/comm/third_party/botan/src/lib/x509/x509self.h
@@ -0,0 +1,222 @@
+/*
+* X.509 Self-Signed Certificate
+* (C) 1999-2007 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_X509_SELF_H_
+#define BOTAN_X509_SELF_H_
+
+#include <botan/x509cert.h>
+#include <botan/pkcs10.h>
+#include <botan/pkix_types.h>
+
+namespace Botan {
+
+class RandomNumberGenerator;
+class Private_Key;
+
+/**
+* Options for X.509 certificates.
+*/
+class BOTAN_PUBLIC_API(2,0) X509_Cert_Options final
+ {
+ public:
+ /**
+ * the subject common name
+ */
+ std::string common_name;
+
+ /**
+ * the subject counry
+ */
+ std::string country;
+
+ /**
+ * the subject organization
+ */
+ std::string organization;
+
+ /**
+ * the subject organizational unit
+ */
+ std::string org_unit;
+
+ /**
+ * additional subject organizational units.
+ */
+ std::vector<std::string> more_org_units;
+
+ /**
+ * the subject locality
+ */
+ std::string locality;
+
+ /**
+ * the subject state
+ */
+ std::string state;
+
+ /**
+ * the subject serial number
+ */
+ std::string serial_number;
+
+ /**
+ * the subject email adress
+ */
+ std::string email;
+
+ /**
+ * the subject URI
+ */
+ std::string uri;
+
+ /**
+ * the subject IPv4 address
+ */
+ std::string ip;
+
+ /**
+ * the subject DNS
+ */
+ std::string dns;
+
+ /**
+ * additional subject DNS entries.
+ */
+ std::vector<std::string> more_dns;
+
+ /**
+ * the subject XMPP
+ */
+ std::string xmpp;
+
+ /**
+ * the subject challenge password
+ */
+ std::string challenge;
+
+ /**
+ * the subject notBefore
+ */
+ X509_Time start;
+ /**
+ * the subject notAfter
+ */
+ X509_Time end;
+
+ /**
+ * Indicates whether the certificate request
+ */
+ bool is_CA;
+
+ /**
+ * Indicates the BasicConstraints path limit
+ */
+ size_t path_limit;
+
+ std::string padding_scheme;
+
+ /**
+ * The key constraints for the subject public key
+ */
+ Key_Constraints constraints;
+
+ /**
+ * The key extended constraints for the subject public key
+ */
+ std::vector<OID> ex_constraints;
+
+ /**
+ * Additional X.509 extensions
+ */
+ Extensions extensions;
+
+ /**
+ * Mark the certificate as a CA certificate and set the path limit.
+ * @param limit the path limit to be set in the BasicConstraints extension.
+ */
+ void CA_key(size_t limit = 1);
+
+ /**
+ * Choose a padding scheme different from the default for the key used.
+ */
+ void set_padding_scheme(const std::string& scheme);
+
+ /**
+ * Set the notBefore of the certificate.
+ * @param time the notBefore value of the certificate
+ */
+ void not_before(const std::string& time);
+
+ /**
+ * Set the notAfter of the certificate.
+ * @param time the notAfter value of the certificate
+ */
+ void not_after(const std::string& time);
+
+ /**
+ * Add the key constraints of the KeyUsage extension.
+ * @param constr the constraints to set
+ */
+ void add_constraints(Key_Constraints constr);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param oid the oid to add
+ */
+ void add_ex_constraint(const OID& oid);
+
+ /**
+ * Add constraints to the ExtendedKeyUsage extension.
+ * @param name the name to look up the oid to add
+ */
+ void add_ex_constraint(const std::string& name);
+
+ /**
+ * Construct a new options object
+ * @param opts define the common name of this object. An example for this
+ * parameter would be "common_name/country/organization/organizational_unit".
+ * @param expire_time the expiration time (from the current clock in seconds)
+ */
+ X509_Cert_Options(const std::string& opts = "",
+ uint32_t expire_time = 365 * 24 * 60 * 60);
+ };
+
+namespace X509 {
+
+/**
+* Create a self-signed X.509 certificate.
+* @param opts the options defining the certificate to create
+* @param key the private key used for signing, i.e. the key
+* associated with this self-signed certificate
+* @param hash_fn the hash function to use
+* @param rng the rng to use
+* @return newly created self-signed certificate
+*/
+BOTAN_PUBLIC_API(2,0) X509_Certificate
+create_self_signed_cert(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+/**
+* Create a PKCS#10 certificate request.
+* @param opts the options defining the request to create
+* @param key the key used to sign this request
+* @param rng the rng to use
+* @param hash_fn the hash function to use
+* @return newly created PKCS#10 request
+*/
+BOTAN_PUBLIC_API(2,0) PKCS10_Request create_cert_req(const X509_Cert_Options& opts,
+ const Private_Key& key,
+ const std::string& hash_fn,
+ RandomNumberGenerator& rng);
+
+}
+
+}
+
+#endif