diff options
author | Jack Lloyd <[email protected]> | 2017-11-14 09:19:50 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-11-14 16:19:44 -0500 |
commit | af1158dd7707d09bcaf47fa78868d0cedf99c0e1 (patch) | |
tree | 088aa218604232611d5c0f9b092a88c507732aba /src/lib | |
parent | dbcdaa424c98ad10ff9657f3574c130a11139228 (diff) |
Refactor certificate extension handling
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/x509/x509_ext.cpp | 383 | ||||
-rw-r--r-- | src/lib/x509/x509_ext.h | 332 |
2 files changed, 441 insertions, 274 deletions
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp index 6e4c29d42..682cc1cc7 100644 --- a/src/lib/x509/x509_ext.cpp +++ b/src/lib/x509/x509_ext.cpp @@ -8,10 +8,11 @@ #include <botan/x509_ext.h> #include <botan/x509cert.h> -#include <botan/sha160.h> +#include <botan/datastor.h> #include <botan/der_enc.h> #include <botan/ber_dec.h> #include <botan/oids.h> +#include <botan/hash.h> #include <botan/internal/bit_ops.h> #include <algorithm> #include <sstream> @@ -19,138 +20,177 @@ namespace Botan { /* -* List of X.509 Certificate Extensions +* Create a Certificate_Extension object of some kind to handle */ -Certificate_Extension* Extensions::create_extension(const OID& oid, bool critical) +Certificate_Extension* +Extensions::create_extn_obj(const OID& oid, + bool critical, + const std::vector<uint8_t>& body) { -#define X509_EXTENSION(NAME, TYPE) \ - if(oid == OIDS::lookup(NAME)) { return new Cert_Extension::TYPE(); } + const std::string oid_str = oid.as_string(); - X509_EXTENSION("X509v3.KeyUsage", Key_Usage); - X509_EXTENSION("X509v3.BasicConstraints", Basic_Constraints); - X509_EXTENSION("X509v3.SubjectKeyIdentifier", Subject_Key_ID); - X509_EXTENSION("X509v3.AuthorityKeyIdentifier", Authority_Key_ID); - X509_EXTENSION("X509v3.ExtendedKeyUsage", Extended_Key_Usage); - X509_EXTENSION("X509v3.IssuerAlternativeName", Issuer_Alternative_Name); - X509_EXTENSION("X509v3.SubjectAlternativeName", Subject_Alternative_Name); - X509_EXTENSION("X509v3.NameConstraints", Name_Constraints); - X509_EXTENSION("X509v3.CertificatePolicies", Certificate_Policies); - X509_EXTENSION("X509v3.CRLDistributionPoints", CRL_Distribution_Points); - X509_EXTENSION("PKIX.AuthorityInformationAccess", Authority_Information_Access); - X509_EXTENSION("X509v3.CRLNumber", CRL_Number); - X509_EXTENSION("X509v3.ReasonCode", CRL_ReasonCode); + Certificate_Extension* extn = nullptr; - return critical ? new Cert_Extension::Unknown_Critical_Extension(oid) : nullptr; + if(oid == Cert_Extension::Subject_Key_ID::static_oid()) + { + extn = new Cert_Extension::Subject_Key_ID; + } + else if(oid == Cert_Extension::Key_Usage::static_oid()) + { + extn = new Cert_Extension::Key_Usage; + } + else if(oid == Cert_Extension::Subject_Alternative_Name::static_oid()) + { + extn = new Cert_Extension::Subject_Alternative_Name; + } + else if(oid == Cert_Extension::Issuer_Alternative_Name::static_oid()) + { + extn = new Cert_Extension::Issuer_Alternative_Name; + } + else if(oid == Cert_Extension::Basic_Constraints::static_oid()) + { + extn = new Cert_Extension::Basic_Constraints; + } + else if(oid == Cert_Extension::CRL_Number::static_oid()) + { + extn = new Cert_Extension::CRL_Number; + } + else if(oid == Cert_Extension::CRL_ReasonCode::static_oid()) + { + extn = new Cert_Extension::CRL_ReasonCode; + } + else if(oid == Cert_Extension::Authority_Key_ID::static_oid()) + { + extn = new Cert_Extension::Authority_Key_ID; + } + else if(oid == Cert_Extension::Name_Constraints::static_oid()) + { + extn = new Cert_Extension::Name_Constraints; + } + else if(oid == Cert_Extension::CRL_Distribution_Points::static_oid()) + { + extn = new Cert_Extension::CRL_Distribution_Points; + } + else if(oid == Cert_Extension::Certificate_Policies::static_oid()) + { + extn = new Cert_Extension::Certificate_Policies; + } + else if(oid == Cert_Extension::Extended_Key_Usage::static_oid()) + { + extn = new Cert_Extension::Extended_Key_Usage; + } + else if(oid == Cert_Extension::Authority_Information_Access::static_oid()) + { + extn = new Cert_Extension::Authority_Information_Access; + } + else + { + // some other unknown extension type + extn = new Cert_Extension::Unknown_Extension(oid, critical); + } + + try + { + extn->decode_inner(body); + } + catch(Decoding_Error& e) + { + throw Decoding_Error("Decoding X.509 extension " + oid.as_string() + " failed", e.what()); + } + return extn; } /* -* Extensions Copy Constructor +* Validate the extension (the default implementation is a NOP) */ -Extensions::Extensions(const Extensions& extensions) : ASN1_Object() +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) { - *this = extensions; } /* -* Extensions Assignment Operator +* Add a new cert */ -Extensions& Extensions::operator=(const Extensions& other) +void Extensions::add(Certificate_Extension* extn, bool critical) { - if(this == &other) - return *this; - - m_extensions.clear(); - - for(size_t i = 0; i != other.m_extensions.size(); ++i) - m_extensions.push_back( - std::make_pair(std::unique_ptr<Certificate_Extension>(other.m_extensions[i].first->copy()), - other.m_extensions[i].second)); - - m_extensions_raw = other.m_extensions_raw; - m_throw_on_unknown_critical = other.m_throw_on_unknown_critical; + // sanity check: we don't want to have the same extension more than once + if(m_extension_info.count(extn->oid_of()) > 0) + throw Invalid_Argument(extn->oid_name() + " extension already present in Extensions::add"); - return (*this); + const OID oid = extn->oid_of(); + Extensions_Info info(critical, extn); + m_extension_oids.push_back(oid); + m_extension_info.emplace(oid, info); } -/* -* Return the OID of this extension -*/ -OID Certificate_Extension::oid_of() const +void Extensions::replace(Certificate_Extension* extn, bool critical) { - return OIDS::lookup(oid_name()); + // Remove it if it existed + m_extension_info.erase(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); } -/* -* 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) +bool Extensions::extension_set(const OID& oid) const { + return (m_extension_info.find(oid) != m_extension_info.end()); } -void Extensions::add(Certificate_Extension* extn, bool critical) +bool Extensions::critical_extension_set(const OID& oid) const { - // sanity check: we don't want to have the same extension more than once - for(const auto& ext : m_extensions) - { - if(ext.first->oid_of() == extn->oid_of()) - { - throw Invalid_Argument(extn->oid_name() + " extension already present"); - } - } - - if(m_extensions_raw.count(extn->oid_of()) > 0) - { - throw Invalid_Argument(extn->oid_name() + " extension already present"); - } - - m_extensions.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(extn), critical)); - m_extensions_raw.emplace(extn->oid_of(), std::make_pair(extn->encode_inner(), critical)); + auto i = m_extension_info.find(oid); + if(i != m_extension_info.end()) + return i->second.is_critical(); + return false; } -void Extensions::replace(Certificate_Extension* extn, bool critical) +const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const { - for(auto it = m_extensions.begin(); it != m_extensions.end(); ++it) - { - if(it->first->oid_of() == extn->oid_of()) - { - m_extensions.erase(it); - break; - } - } + auto extn = m_extension_info.find(oid); + if(extn == m_extension_info.end()) + return nullptr; - m_extensions.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(extn), critical)); - m_extensions_raw[extn->oid_of()] = std::make_pair(extn->encode_inner(), critical); + return &extn->second.obj(); } std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const { - for(auto& ext : m_extensions) + if(const Certificate_Extension* ext = this->get_extension_object(oid)) { - if(ext.first->oid_of() == oid) - { - return std::unique_ptr<Certificate_Extension>(ext.first->copy()); - } + 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_extensions) + for(auto&& ext : m_extension_info) { - exts.push_back(std::make_pair(std::unique_ptr<Certificate_Extension>(ext.first->copy()), ext.second)); + 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 { - return m_extensions_raw; + 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; } /* @@ -158,44 +198,20 @@ std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw( */ void Extensions::encode_into(DER_Encoder& to_object) const { - // encode any known extensions - for(size_t i = 0; i != m_extensions.size(); ++i) + for(auto ext_info : m_extension_info) { - const Certificate_Extension* ext = m_extensions[i].first.get(); - const bool is_critical = m_extensions[i].second; - - const bool should_encode = ext->should_encode(); + const OID& oid = ext_info.first; + const bool should_encode = ext_info.second.obj().should_encode(); if(should_encode) { - to_object.start_cons(SEQUENCE) - .encode(ext->oid_of()) - .encode_optional(is_critical, false) - .encode(ext->encode_inner(), OCTET_STRING) - .end_cons(); - } - } - - // encode any unknown extensions - for(const auto& ext_raw : m_extensions_raw) - { - const bool is_critical = ext_raw.second.second; - const OID oid = ext_raw.first; - const std::vector<uint8_t> value = ext_raw.second.first; - - auto pos = std::find_if(std::begin(m_extensions), std::end(m_extensions), - [&oid](const std::pair<std::unique_ptr<Certificate_Extension>, bool>& ext) -> bool - { - return ext.first->oid_of() == oid; - }); + const bool is_critical = ext_info.second.is_critical(); + const std::vector<uint8_t>& ext_value = ext_info.second.bits(); - if(pos == std::end(m_extensions)) - { - // not found in m_extensions, must be unknown to_object.start_cons(SEQUENCE) .encode(oid) .encode_optional(is_critical, false) - .encode(value, OCTET_STRING) + .encode(ext_value, OCTET_STRING) .end_cons(); } } @@ -206,47 +222,29 @@ void Extensions::encode_into(DER_Encoder& to_object) const */ void Extensions::decode_from(BER_Decoder& from_source) { - m_extensions.clear(); - m_extensions_raw.clear(); + m_extension_oids.clear(); + m_extension_info.clear(); BER_Decoder sequence = from_source.start_cons(SEQUENCE); while(sequence.more_items()) { OID oid; - std::vector<uint8_t> value; bool critical; + std::vector<uint8_t> bits; sequence.start_cons(SEQUENCE) - .decode(oid) - .decode_optional(critical, BOOLEAN, UNIVERSAL, false) - .decode(value, OCTET_STRING) - .end_cons(); - - m_extensions_raw.emplace(oid, std::make_pair(value, critical)); - - std::unique_ptr<Certificate_Extension> ext(create_extension(oid, critical)); + .decode(oid) + .decode_optional(critical, BOOLEAN, UNIVERSAL, false) + .decode(bits, OCTET_STRING) + .end_cons(); - if(!ext && critical && m_throw_on_unknown_critical) - throw Decoding_Error("Encountered unknown X.509 extension marked " - "as critical; OID = " + oid.as_string()); + Extensions_Info info(critical, bits, + create_extn_obj(oid, critical, bits)); - if(ext) - { - try - { - ext->decode_inner(value); - } - catch(std::exception& e) - { - throw Decoding_Error("Exception while decoding extension " + - oid.as_string() + ": " + e.what()); - } - - m_extensions.push_back(std::make_pair(std::move(ext), critical)); - } + m_extension_oids.push_back(oid); + m_extension_info.emplace(oid, info); } - sequence.verify_end(); } @@ -256,14 +254,14 @@ void Extensions::decode_from(BER_Decoder& from_source) void Extensions::contents_to(Data_Store& subject_info, Data_Store& issuer_info) const { - for(size_t i = 0; i != m_extensions.size(); ++i) + for(auto&& m_extn_info : m_extension_info) { - m_extensions[i].first->contents_to(subject_info, issuer_info); - subject_info.add(m_extensions[i].first->oid_name() + ".is_critical", (m_extensions[i].second ? 1 : 0)); + 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 { /* @@ -402,8 +400,15 @@ void Subject_Key_ID::contents_to(Data_Store& subject, Data_Store&) const /* * Subject_Key_ID Constructor */ -Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key) : m_key_id(unlock(SHA_160().process(pub_key))) - {} +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()); + } /* * Encode the extension @@ -439,61 +444,50 @@ void Authority_Key_ID::contents_to(Data_Store&, Data_Store& issuer) const /* * Encode the extension */ -std::vector<uint8_t> Alternative_Name::encode_inner() const +std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const { return DER_Encoder().encode(m_alt_name).get_contents_unlocked(); } /* -* Decode the extension +* Encode the extension */ -void Alternative_Name::decode_inner(const std::vector<uint8_t>& in) +std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const { - BER_Decoder(in).decode(m_alt_name); + return DER_Encoder().encode(m_alt_name).get_contents_unlocked(); } /* -* Return a textual representation +* Decode the extension */ -void Alternative_Name::contents_to(Data_Store& subject_info, - Data_Store& issuer_info) const +void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in) { - std::multimap<std::string, std::string> contents = - get_alt_name().contents(); - - if(m_oid_name_str == "X509v3.SubjectAlternativeName") - subject_info.add(contents); - else if(m_oid_name_str == "X509v3.IssuerAlternativeName") - issuer_info.add(contents); - else - throw Internal_Error("In Alternative_Name, unknown type " + - m_oid_name_str); + BER_Decoder(in).decode(m_alt_name); } /* -* Alternative_Name Constructor +* Decode the extension */ -Alternative_Name::Alternative_Name(const AlternativeName& alt_name, - const std::string& oid_name_str) : - m_oid_name_str(oid_name_str), - m_alt_name(alt_name) - {} +void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in) + { + BER_Decoder(in).decode(m_alt_name); + } /* -* Subject_Alternative_Name Constructor +* Return a textual representation */ -Subject_Alternative_Name::Subject_Alternative_Name( - const AlternativeName& name) : - Alternative_Name(name, "X509v3.SubjectAlternativeName") +void Subject_Alternative_Name::contents_to(Data_Store& subject_info, + Data_Store&) const { + subject_info.add(get_alt_name().contents()); } /* -* Issuer_Alternative_Name Constructor +* Return a textual representation */ -Issuer_Alternative_Name::Issuer_Alternative_Name(const AlternativeName& name) : - Alternative_Name(name, "X509v3.IssuerAlternativeName") +void Issuer_Alternative_Name::contents_to(Data_Store&, Data_Store& issuer_info) const { + issuer_info.add(get_alt_name().contents()); } /* @@ -849,22 +843,26 @@ std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf) { - BER_Decoder(buf).decode_list(m_distribution_points).verify_end(); - } + BER_Decoder(buf) + .decode_list(m_distribution_points) + .verify_end(); -void CRL_Distribution_Points::contents_to(Data_Store& info, Data_Store&) const - { for(size_t i = 0; i != m_distribution_points.size(); ++i) { auto point = m_distribution_points[i].point().contents(); auto uris = point.equal_range("URI"); - for(auto uri = uris.first; uri != uris.second; ++uri) - info.add("CRL.DistributionPoint", uri->second); + m_crl_distribution_urls.push_back(uri->second); } } +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"); @@ -880,17 +878,20 @@ void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& .end_cons().end_cons(); } -std::vector<uint8_t> Unknown_Critical_Extension::encode_inner() const +std::vector<uint8_t> Unknown_Extension::encode_inner() const { - throw Not_Implemented("Unknown_Critical_Extension encoding"); + return m_bytes; } -void Unknown_Critical_Extension::decode_inner(const std::vector<uint8_t>&) +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_Critical_Extension::contents_to(Data_Store&, Data_Store&) const +void Unknown_Extension::contents_to(Data_Store&, Data_Store&) const { + // No information store } } diff --git a/src/lib/x509/x509_ext.h b/src/lib/x509/x509_ext.h index 69647616f..2243d6deb 100644 --- a/src/lib/x509/x509_ext.h +++ b/src/lib/x509/x509_ext.h @@ -12,7 +12,6 @@ #include <botan/asn1_oid.h> #include <botan/asn1_alt_name.h> #include <botan/cert_status.h> -#include <botan/datastor.h> #include <botan/name_constraint.h> #include <botan/key_constraint.h> #include <botan/crl_ent.h> @@ -20,6 +19,7 @@ namespace Botan { +class Data_Store; class X509_Certificate; /** @@ -31,7 +31,15 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension /** * @return OID representing this extension */ - virtual OID oid_of() const; + virtual OID oid_of() const = 0; + + /* + * @return specific OID name + * If possible OIDS table should match oid_name to OIDS, ie + * OIDS::lookup(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 @@ -49,11 +57,6 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension Data_Store& issuer) const = 0; /* - * @return specific OID name - */ - virtual std::string oid_name() const = 0; - - /* * Callback visited during path validation. * * An extension can implement this callback to inspect @@ -87,13 +90,65 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Extension 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)) + { + if(const T* extn_as_T = dynamic_cast<const T*>(extn)) + { + return extn_as_T; + } + else + { + throw Exception("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 the certificate extension + * @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 */ @@ -110,67 +165,103 @@ class BOTAN_PUBLIC_API(2,0) Extensions final : public ASN1_Object * 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 Pointer to extension with oid, nullptr if not found. + * @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. - * Only the unknown extensions, that is, extensions - * types that are not declared in this header, are searched - * for by this function. - * @return Pointer to extension with oid, nullptr if not found. + * 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) - { - try + std::unique_ptr<T> get_raw(const OID& oid) const { - if(m_extensions_raw.count(oid) > 0) + auto extn_info = m_extension_info.find(oid); + + if(extn_info != m_extension_info.end()) { - std::unique_ptr<T> ext(new T); - ext->decode_inner(m_extensions_raw[oid].first); - return std::move(ext); + // 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 std::move(ext); + } } + return nullptr; } - catch(std::exception& e) - { - throw Decoding_Error("Exception while decoding extension " + - oid.as_string() + ": " + e.what()); - } - return nullptr; - } /** - * Returns the list of extensions together with the corresponding - * criticality flag. Only contains the known extensions - * types declared in this header. + * 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, known as well as unknown extensions. + * Contains all extensions, including any extensions encoded as Unknown_Extension */ std::map<OID, std::pair<std::vector<uint8_t>, bool>> extensions_raw() const; - Extensions& operator=(const Extensions&); + Extensions() {} - Extensions(const Extensions&); + Extensions(const Extensions&) = default; + Extensions& operator=(const Extensions&) = default; - /** - * @param st whether to throw an exception when encountering an unknown - * extension type during decoding - */ - explicit Extensions(bool st = true) : m_throw_on_unknown_critical(st) {} +#if !defined(BOTAN_BUILD_COMPILER_IS_MSVC_2013) + Extensions(Extensions&&) = default; + Extensions& operator=(Extensions&&) = default; +#endif private: - static Certificate_Extension* create_extension(const OID&, bool); + static Certificate_Extension* create_extn_obj(const OID& oid, + bool critical, + const std::vector<uint8_t>& body); - std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> m_extensions; - bool m_throw_on_unknown_critical; - std::map<OID, std::pair<std::vector<uint8_t>, bool>> m_extensions_raw; + class Extensions_Info + { + public: + Extensions_Info(bool critical, + Certificate_Extension* ext) : + m_critical(critical), + m_bits(ext->encode_inner()), + m_obj(ext) + {} + + Extensions_Info(bool critical, + const std::vector<uint8_t>& encoding, + Certificate_Extension* ext) : + m_critical(critical), + m_bits(encoding), + m_obj(ext) + {} + + 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: + bool m_critical = false; + std::vector<uint8_t> m_bits; + std::shared_ptr<const Certificate_Extension> m_obj; + }; + + std::vector<OID> m_extension_oids; + std::map<OID, Extensions_Info> m_extension_info; }; namespace Cert_Extension { @@ -192,6 +283,9 @@ class BOTAN_PUBLIC_API(2,0) Basic_Constraints final : public Certificate_Extensi 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"; } @@ -216,6 +310,9 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_Extension 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"; } @@ -234,13 +331,21 @@ class BOTAN_PUBLIC_API(2,0) Key_Usage final : public Certificate_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); } - Subject_Key_ID() = default; - explicit Subject_Key_ID(const std::vector<uint8_t>&); + 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(); } - std::vector<uint8_t> get_key_id() const { return m_key_id; } private: std::string oid_name() const override { return "X509v3.SubjectKeyIdentifier"; } @@ -265,7 +370,10 @@ class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extensio Authority_Key_ID() = default; explicit Authority_Key_ID(const std::vector<uint8_t>& k) : m_key_id(k) {} - std::vector<uint8_t> get_key_id() const { return 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.35"); } + OID oid_of() const override { return static_oid(); } private: std::string oid_name() const override @@ -280,52 +388,59 @@ class BOTAN_PUBLIC_API(2,0) Authority_Key_ID final : public Certificate_Extensio }; /** -* Alternative Name Extension Base Class +* Subject Alternative Name Extension */ -class BOTAN_PUBLIC_API(2,0) Alternative_Name : public Certificate_Extension +class BOTAN_PUBLIC_API(2,4) Subject_Alternative_Name final : public Certificate_Extension { public: - AlternativeName get_alt_name() const { return m_alt_name; } + const AlternativeName& get_alt_name() const { return m_alt_name; } - protected: - Alternative_Name(const AlternativeName&, const std::string& oid_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()); } - Alternative_Name(const std::string&, const std::string&); + explicit Subject_Alternative_Name(const AlternativeName& name = AlternativeName()) : + m_alt_name(name) {} private: - std::string oid_name() const override { return m_oid_name_str; } + 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; - std::string m_oid_name_str; AlternativeName m_alt_name; }; /** -* Subject Alternative Name Extension +* Issuer Alternative Name Extension */ -class BOTAN_PUBLIC_API(2,0) Subject_Alternative_Name final : public Alternative_Name +class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Certificate_Extension { public: - Subject_Alternative_Name* copy() const override - { return new Subject_Alternative_Name(get_alt_name()); } + const AlternativeName& get_alt_name() const { return m_alt_name; } - explicit Subject_Alternative_Name(const AlternativeName& = AlternativeName()); - }; + static OID static_oid() { return OID("2.5.29.18"); } + OID oid_of() const override { return static_oid(); } -/** -* Issuer Alternative Name Extension -*/ -class BOTAN_PUBLIC_API(2,0) Issuer_Alternative_Name final : public Alternative_Name - { - public: Issuer_Alternative_Name* copy() const override { return new Issuer_Alternative_Name(get_alt_name()); } - explicit Issuer_Alternative_Name(const AlternativeName& = AlternativeName()); + 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; }; /** @@ -340,11 +455,13 @@ class BOTAN_PUBLIC_API(2,0) Extended_Key_Usage final : public Certificate_Extens Extended_Key_Usage() = default; explicit Extended_Key_Usage(const std::vector<OID>& o) : m_oids(o) {} - std::vector<OID> get_oids() const { return m_oids; } + 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"; } + 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; @@ -371,6 +488,11 @@ class BOTAN_PUBLIC_API(2,0) Name_Constraints final : public Certificate_Extensio 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"; } @@ -395,8 +517,14 @@ class BOTAN_PUBLIC_API(2,0) Certificate_Policies final : public Certificate_Exte 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(); } + private: std::string oid_name() const override { return "X509v3.CertificatePolicies"; } @@ -420,6 +548,11 @@ class BOTAN_PUBLIC_API(2,0) Authority_Information_Access final : public Certific explicit Authority_Information_Access(const std::string& ocsp) : m_ocsp_responder(ocsp) {} + 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(); } + private: std::string oid_name() const override { return "PKIX.AuthorityInformationAccess"; } @@ -447,6 +580,9 @@ class BOTAN_PUBLIC_API(2,0) CRL_Number final : public Certificate_Extension 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"; } @@ -472,6 +608,9 @@ class BOTAN_PUBLIC_API(2,0) CRL_ReasonCode final : public Certificate_Extension 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"; } @@ -508,9 +647,15 @@ class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_E explicit CRL_Distribution_Points(const std::vector<Distribution_Point>& points) : m_distribution_points(points) {} - std::vector<Distribution_Point> distribution_points() const + 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"; } @@ -523,44 +668,65 @@ class BOTAN_PUBLIC_API(2,0) CRL_Distribution_Points final : public Certificate_E void contents_to(Data_Store&, Data_Store&) const override; std::vector<Distribution_Point> m_distribution_points; + std::vector<std::string> m_crl_distribution_urls; }; /** -* An unknown X.509 extension marked as critical -* Will always add a failure to the path validation result. +* An unknown X.509 extension +* Will add a failure to the path validation result, if critical */ -class BOTAN_PUBLIC_API(2,0) Unknown_Critical_Extension final : public Certificate_Extension +class BOTAN_PUBLIC_API(2,4) Unknown_Extension final : public Certificate_Extension { public: - explicit Unknown_Critical_Extension(OID oid) : m_oid(oid) {} + Unknown_Extension(const OID& oid, bool critical) : + m_oid(oid), m_critical(critical) {} - Unknown_Critical_Extension* copy() const override - { return new Unknown_Critical_Extension(m_oid); } + 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 { - cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION); + if(m_critical) + { + cert_status.at(pos).insert(Certificate_Status_Code::UNKNOWN_CRITICAL_EXTENSION); + } } private: - std::string oid_name() const override - { return "Unknown OID name"; } + std::string oid_name() const override { return ""; } - bool should_encode() const override { return false; } + 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; }; -} + } } |