From ce9326bef92b2ef4cd93a1de232b0daec0357a3f Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 14 Nov 2017 09:14:24 -0500 Subject: Move X509_DN and AlternativeName from asn1 to x509 --- src/lib/asn1/asn1_alt_name.cpp | 236 -------------------- src/lib/asn1/asn1_alt_name.h | 47 ---- src/lib/asn1/x509_dn.cpp | 388 --------------------------------- src/lib/asn1/x509_dn.h | 59 ----- src/lib/x509/asn1_alt_name.cpp | 235 ++++++++++++++++++++ src/lib/x509/asn1_alt_name.h | 58 +++++ src/lib/x509/ocsp.h | 1 + src/lib/x509/x509_dn.cpp | 407 +++++++++++++++++++++++++++++++++++ src/lib/x509/x509_dn.h | 65 ++++++ src/tests/test_pkcs11_high_level.cpp | 1 + src/tests/test_x509_dn.cpp | 4 +- 11 files changed, 769 insertions(+), 732 deletions(-) delete mode 100644 src/lib/asn1/asn1_alt_name.cpp delete mode 100644 src/lib/asn1/asn1_alt_name.h delete mode 100644 src/lib/asn1/x509_dn.cpp delete mode 100644 src/lib/asn1/x509_dn.h create mode 100644 src/lib/x509/asn1_alt_name.cpp create mode 100644 src/lib/x509/asn1_alt_name.h create mode 100644 src/lib/x509/x509_dn.cpp create mode 100644 src/lib/x509/x509_dn.h diff --git a/src/lib/asn1/asn1_alt_name.cpp b/src/lib/asn1/asn1_alt_name.cpp deleted file mode 100644 index 940312886..000000000 --- a/src/lib/asn1/asn1_alt_name.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* -* AlternativeName -* (C) 1999-2007 Jack Lloyd -* 2007 Yves Jerschow -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -/* -* Check if type is a known ASN.1 string type -*/ -bool is_string_type(ASN1_Tag tag) - { - return (tag == NUMERIC_STRING || - tag == PRINTABLE_STRING || - tag == VISIBLE_STRING || - tag == T61_STRING || - tag == IA5_STRING || - tag == UTF8_STRING || - tag == BMP_STRING); - } - -} - -/* -* 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& str) - { - if(type.empty() || str.empty()) - return; - - auto range = m_alt_info.equal_range(type); - for(auto j = range.first; j != range.second; ++j) - if(j->second == str) - return; - - multimap_insert(m_alt_info, type, str); - } - -/* -* 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)); - } - -/* -* Get the attributes of this alternative name -*/ -std::multimap AlternativeName::get_attributes() const - { - return m_alt_info; - } - -/* -* Get the otherNames -*/ -std::multimap AlternativeName::get_othernames() const - { - return m_othernames; - } - -/* -* Return all of the alternative names -*/ -std::multimap AlternativeName::contents() const - { - std::multimap 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, OIDS::lookup(i->first), i->second.value()); - - return names; - } - -/* -* 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& 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); - } - } - } - -} - -/* -* 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, "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); - - while(names.more_items()) - { - BER_Object obj = names.get_next_object(); - if((obj.class_tag != CONTEXT_SPECIFIC) && - (obj.class_tag != (CONTEXT_SPECIFIC | CONSTRUCTED))) - continue; - - const ASN1_Tag tag = obj.type_tag; - - if(tag == 0) - { - BER_Decoder othername(obj.value); - - 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.type_tag != ASN1_Tag(0) || - othername_value_outer.class_tag != - (CONTEXT_SPECIFIC | CONSTRUCTED) - ) - throw Decoding_Error("Invalid tags on otherName value"); - - BER_Decoder othername_value_inner(othername_value_outer.value); - - BER_Object value = othername_value_inner.get_next_object(); - othername_value_inner.verify_end(); - - const ASN1_Tag value_type = value.type_tag; - - if(is_string_type(value_type) && value.class_tag == UNIVERSAL) - add_othername(oid, ASN1::to_string(value), value_type); - } - } - else if(tag == 1 || tag == 2 || tag == 6) - { - if(tag == 1) add_attribute("RFC822", ASN1::to_string(obj)); - if(tag == 2) add_attribute("DNS", ASN1::to_string(obj)); - if(tag == 6) add_attribute("URI", ASN1::to_string(obj)); - } - else if(tag == 7) - { - if(obj.value.size() == 4) - { - const uint32_t ip = load_be(&obj.value[0], 0); - add_attribute("IP", ipv4_to_string(ip)); - } - } - - } - } - -} diff --git a/src/lib/asn1/asn1_alt_name.h b/src/lib/asn1/asn1_alt_name.h deleted file mode 100644 index 9a9b759d7..000000000 --- a/src/lib/asn1/asn1_alt_name.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -* Common ASN.1 Objects -* (C) 1999-2007 Jack Lloyd -* 2007 Yves Jerschow -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_ASN1_ALT_NAME_H_ -#define BOTAN_ASN1_ALT_NAME_H_ - -#include -#include -#include -#include - -namespace Botan { - -/** -* Alternative Name -*/ -class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object - { - public: - void encode_into(class DER_Encoder&) const override; - void decode_from(class BER_Decoder&) override; - - std::multimap contents() const; - - void add_attribute(const std::string&, const std::string&); - std::multimap get_attributes() const; - - void add_othername(const OID&, const std::string&, ASN1_Tag); - std::multimap get_othernames() const; - - bool has_items() const; - - AlternativeName(const std::string& = "", const std::string& = "", - const std::string& = "", const std::string& = ""); - private: - std::multimap m_alt_info; - std::multimap m_othernames; - }; - -} - -#endif diff --git a/src/lib/asn1/x509_dn.cpp b/src/lib/asn1/x509_dn.cpp deleted file mode 100644 index dd92b25ec..000000000 --- a/src/lib/asn1/x509_dn.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* -* X509_DN -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -/* -* Create an X509_DN -*/ -X509_DN::X509_DN(const std::multimap& args) - { - for(auto i = args.begin(); i != args.end(); ++i) - add_attribute(i->first, i->second); - } - -/* -* Create an X509_DN -*/ -X509_DN::X509_DN(const std::multimap& args) - { - for(auto i = args.begin(); i != args.end(); ++i) - add_attribute(OIDS::lookup(i->first), i->second); - } - -/* -* Add an attribute to a X509_DN -*/ -void X509_DN::add_attribute(const std::string& type, - const std::string& str) - { - OID oid = OIDS::lookup(type); - add_attribute(oid, str); - } - -/* -* Add an attribute to a X509_DN -*/ -void X509_DN::add_attribute(const OID& oid, const std::string& str) - { - if(str.empty()) - return; - - auto range = m_dn_info.equal_range(oid); - for(auto i = range.first; i != range.second; ++i) - if(i->second.value() == str) - return; - - multimap_insert(m_dn_info, oid, ASN1_String(str)); - m_dn_bits.clear(); - } - -/* -* Get the attributes of this X509_DN -*/ -std::multimap X509_DN::get_attributes() const - { - std::multimap retval; - for(auto i = m_dn_info.begin(); i != m_dn_info.end(); ++i) - multimap_insert(retval, i->first, i->second.value()); - return retval; - } - -/* -* Get the contents of this X.500 Name -*/ -std::multimap X509_DN::contents() const - { - std::multimap retval; - for(auto i = m_dn_info.begin(); i != m_dn_info.end(); ++i) - multimap_insert(retval, OIDS::lookup(i->first), i->second.value()); - return retval; - } - -/* -* Get a single attribute type -*/ -std::vector X509_DN::get_attribute(const std::string& attr) const - { - const OID oid = OIDS::lookup(deref_info_field(attr)); - - auto range = m_dn_info.equal_range(oid); - - std::vector values; - for(auto i = range.first; i != range.second; ++i) - values.push_back(i->second.value()); - return values; - } - -/* -* Return the BER encoded data, if any -*/ -std::vector X509_DN::get_bits() const - { - return m_dn_bits; - } - -/* -* 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(attr1.size() < attr2.size()) return true; - if(attr1.size() > attr2.size()) return false; - - for(auto p1 = attr1.begin(); p1 != attr1.end(); ++p1) - { - auto p2 = attr2.find(p1->first); - if(p2 == attr2.end()) return false; - if(p1->second > p2->second) return false; - if(p1->second < p2->second) return true; - } - return false; - } - -namespace { - -/* -* DER encode a RelativeDistinguishedName -*/ -void do_ava(DER_Encoder& encoder, - const std::multimap& dn_info, - ASN1_Tag string_type, const std::string& oid_str, - bool must_exist = false) - { - const OID oid = OIDS::lookup(oid_str); - const bool exists = (dn_info.find(oid) != dn_info.end()); - - if(!exists && must_exist) - throw Encoding_Error("X509_DN: No entry for " + oid_str); - if(!exists) return; - - auto range = dn_info.equal_range(oid); - - for(auto i = range.first; i != range.second; ++i) - { - encoder.start_cons(SET) - .start_cons(SEQUENCE) - .encode(oid) - .encode(ASN1_String(i->second, string_type)) - .end_cons() - .end_cons(); - } - } - -} - -/* -* DER encode a DistinguishedName -*/ -void X509_DN::encode_into(DER_Encoder& der) const - { - auto dn_info = get_attributes(); - - der.start_cons(SEQUENCE); - - if(!m_dn_bits.empty()) - der.raw_bytes(m_dn_bits); - else - { - do_ava(der, dn_info, PRINTABLE_STRING, "X520.Country"); - do_ava(der, dn_info, DIRECTORY_STRING, "X520.State"); - do_ava(der, dn_info, DIRECTORY_STRING, "X520.Locality"); - do_ava(der, dn_info, DIRECTORY_STRING, "X520.Organization"); - do_ava(der, dn_info, DIRECTORY_STRING, "X520.OrganizationalUnit"); - do_ava(der, dn_info, DIRECTORY_STRING, "X520.CommonName"); - do_ava(der, dn_info, PRINTABLE_STRING, "X520.SerialNumber"); - } - - der.end_cons(); - } - -/* -* Decode a BER encoded DistinguishedName -*/ -void X509_DN::decode_from(BER_Decoder& source) - { - std::vector 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) - .end_cons(); - - add_attribute(oid, str.value()); - } - } - - m_dn_bits = bits; - } - -namespace { - -std::string to_short_form(const std::string& long_id) - { - if(long_id == "X520.CommonName") - return "CN"; - - if(long_id == "X520.Organization") - return "O"; - - if(long_id == "X520.OrganizationalUnit") - return "OU"; - - return long_id; - } - -} - -std::ostream& operator<<(std::ostream& out, const X509_DN& dn) - { - std::multimap contents = dn.contents(); - - for(std::multimap::const_iterator i = contents.begin(); - i != contents.end(); ++i) - { - out << to_short_form(i->first) << "=\""; - for(char c: i->second) - { - if(c == '\\' || c == '\"') - { - out << "\\"; - } - out << c; - } - out << "\""; - - if(std::next(i) != contents.end()) - { - 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/src/lib/asn1/x509_dn.h b/src/lib/asn1/x509_dn.h deleted file mode 100644 index 09f8cf16b..000000000 --- a/src/lib/asn1/x509_dn.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -* X.509 Distinguished Name -* (C) 1999-2010 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_X509_DN_H_ -#define BOTAN_X509_DN_H_ - -#include -#include -#include -#include -#include - -namespace Botan { - -/** -* Distinguished Name -*/ -class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object - { - public: - void encode_into(class DER_Encoder&) const override; - void decode_from(class BER_Decoder&) override; - - std::multimap get_attributes() const; - std::vector get_attribute(const std::string&) const; - - std::multimap contents() const; - - void add_attribute(const std::string&, const std::string&); - void add_attribute(const OID&, const std::string&); - - static std::string deref_info_field(const std::string&); - - std::vector get_bits() const; - - bool empty() const { return m_dn_info.empty(); } - - X509_DN() = default; - explicit X509_DN(const std::multimap&); - explicit X509_DN(const std::multimap&); - private: - std::multimap m_dn_info; - std::vector m_dn_bits; - }; - -bool BOTAN_PUBLIC_API(2,0) operator==(const X509_DN&, const X509_DN&); -bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_DN&, const X509_DN&); -bool BOTAN_PUBLIC_API(2,0) operator<(const X509_DN&, const X509_DN&); - -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); - -} - -#endif diff --git a/src/lib/x509/asn1_alt_name.cpp b/src/lib/x509/asn1_alt_name.cpp new file mode 100644 index 000000000..8b3d2d6e9 --- /dev/null +++ b/src/lib/x509/asn1_alt_name.cpp @@ -0,0 +1,235 @@ +/* +* AlternativeName +* (C) 1999-2007 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +/* +* Check if type is a known ASN.1 string type +*/ +bool is_string_type(ASN1_Tag tag) + { + return (tag == NUMERIC_STRING || + tag == PRINTABLE_STRING || + tag == VISIBLE_STRING || + tag == T61_STRING || + tag == IA5_STRING || + tag == UTF8_STRING || + tag == BMP_STRING); + } + +} + +/* +* 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 AlternativeName::contents() const + { + std::multimap 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, OIDS::lookup(i->first), 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::vector AlternativeName::get_attribute(const std::string& attr) const + { + std::vector 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; + } + +/* +* 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& 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); + } + } + } + +} + +/* +* 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, "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); + + while(names.more_items()) + { + BER_Object obj = names.get_next_object(); + if((obj.class_tag != CONTEXT_SPECIFIC) && + (obj.class_tag != (CONTEXT_SPECIFIC | CONSTRUCTED))) + continue; + + const ASN1_Tag tag = obj.type_tag; + + if(tag == 0) + { + BER_Decoder othername(obj.value); + + 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.type_tag != ASN1_Tag(0) || + othername_value_outer.class_tag != + (CONTEXT_SPECIFIC | CONSTRUCTED) + ) + throw Decoding_Error("Invalid tags on otherName value"); + + BER_Decoder othername_value_inner(othername_value_outer.value); + + BER_Object value = othername_value_inner.get_next_object(); + othername_value_inner.verify_end(); + + const ASN1_Tag value_type = value.type_tag; + + if(is_string_type(value_type) && value.class_tag == UNIVERSAL) + add_othername(oid, ASN1::to_string(value), value_type); + } + } + else if(tag == 1 || tag == 2 || tag == 6) + { + if(tag == 1) add_attribute("RFC822", ASN1::to_string(obj)); + if(tag == 2) add_attribute("DNS", ASN1::to_string(obj)); + if(tag == 6) add_attribute("URI", ASN1::to_string(obj)); + } + else if(tag == 7) + { + if(obj.value.size() == 4) + { + const uint32_t ip = load_be(&obj.value[0], 0); + add_attribute("IP", ipv4_to_string(ip)); + } + } + + } + } + +} diff --git a/src/lib/x509/asn1_alt_name.h b/src/lib/x509/asn1_alt_name.h new file mode 100644 index 000000000..81f933ea0 --- /dev/null +++ b/src/lib/x509/asn1_alt_name.h @@ -0,0 +1,58 @@ +/* +* (C) 1999-2007 Jack Lloyd +* 2007 Yves Jerschow +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X509_ALT_NAME_H_ +#define BOTAN_X509_ALT_NAME_H_ + +#include +#include +#include +#include + +namespace Botan { + +/** +* Alternative Name +*/ +class BOTAN_PUBLIC_API(2,0) AlternativeName final : public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const override; + void decode_from(class BER_Decoder&) override; + + std::multimap contents() const; + + bool has_field(const std::string& attr) const; + std::vector get_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& get_attributes() const + { + return m_alt_info; + } + + const std::multimap& get_othernames() const + { + return m_othernames; + } + + 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 m_alt_info; + std::multimap m_othernames; + }; + +} + +#endif diff --git a/src/lib/x509/ocsp.h b/src/lib/x509/ocsp.h index 254de5038..33177dc59 100644 --- a/src/lib/x509/ocsp.h +++ b/src/lib/x509/ocsp.h @@ -10,6 +10,7 @@ #include #include +#include namespace Botan { diff --git a/src/lib/x509/x509_dn.cpp b/src/lib/x509/x509_dn.cpp new file mode 100644 index 000000000..ce1300e53 --- /dev/null +++ b/src/lib/x509/x509_dn.cpp @@ -0,0 +1,407 @@ +/* +* X509_DN +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +/* +* Create an X509_DN +*/ +X509_DN::X509_DN(const std::multimap& args) + { + for(auto i = args.begin(); i != args.end(); ++i) + add_attribute(i->first, i->second); + } + +/* +* Create an X509_DN +*/ +X509_DN::X509_DN(const std::multimap& args) + { + for(auto i = args.begin(); i != args.end(); ++i) + add_attribute(OIDS::lookup(i->first), i->second); + } + +/* +* Add an attribute to a X509_DN +*/ +void X509_DN::add_attribute(const std::string& type, + const std::string& str) + { + OID oid = OIDS::lookup(type); + add_attribute(oid, str); + } + +/* +* Add an attribute to a X509_DN +*/ +void X509_DN::add_attribute(const OID& oid, const std::string& str) + { + if(str.empty()) + return; + + auto range = m_dn_info.equal_range(oid); + for(auto i = range.first; i != range.second; ++i) + if(i->second.value() == str) + return; + + multimap_insert(m_dn_info, oid, ASN1_String(str)); + m_dn_bits.clear(); + } + +/* +* Get the attributes of this X509_DN +*/ +std::multimap X509_DN::get_attributes() const + { + std::multimap retval; + for(auto i = m_dn_info.begin(); i != m_dn_info.end(); ++i) + multimap_insert(retval, i->first, i->second.value()); + return retval; + } + +/* +* Get the contents of this X.500 Name +*/ +std::multimap X509_DN::contents() const + { + std::multimap retval; + for(auto i = m_dn_info.begin(); i != m_dn_info.end(); ++i) + multimap_insert(retval, OIDS::lookup(i->first), i->second.value()); + return retval; + } + +bool X509_DN::has_field(const std::string& attr) const + { + const OID oid = OIDS::lookup(deref_info_field(attr)); + auto range = m_dn_info.equal_range(oid); + return (range.first != range.second); + } + +std::string X509_DN::get_first_attribute(const std::string& attr) const + { + const OID oid = OIDS::lookup(deref_info_field(attr)); + + auto range = m_dn_info.equal_range(oid); + + if(range.first != m_dn_info.end()) + return range.first->second.value(); + + return ""; + } + +/* +* Get a single attribute type +*/ +std::vector X509_DN::get_attribute(const std::string& attr) const + { + const OID oid = OIDS::lookup(deref_info_field(attr)); + + auto range = m_dn_info.equal_range(oid); + + std::vector values; + for(auto i = range.first; i != range.second; ++i) + values.push_back(i->second.value()); + return values; + } + +const std::vector& X509_DN::get_bits() const + { + return m_dn_bits; + } + +/* +* 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(attr1.size() < attr2.size()) return true; + if(attr1.size() > attr2.size()) return false; + + for(auto p1 = attr1.begin(); p1 != attr1.end(); ++p1) + { + auto p2 = attr2.find(p1->first); + if(p2 == attr2.end()) return false; + if(p1->second > p2->second) return false; + if(p1->second < p2->second) return true; + } + return false; + } + +namespace { + +/* +* DER encode a RelativeDistinguishedName +*/ +void do_ava(DER_Encoder& encoder, + const std::multimap& dn_info, + ASN1_Tag string_type, const std::string& oid_str, + bool must_exist = false) + { + const OID oid = OIDS::lookup(oid_str); + const bool exists = (dn_info.find(oid) != dn_info.end()); + + if(!exists && must_exist) + throw Encoding_Error("X509_DN: No entry for " + oid_str); + if(!exists) return; + + auto range = dn_info.equal_range(oid); + + for(auto i = range.first; i != range.second; ++i) + { + encoder.start_cons(SET) + .start_cons(SEQUENCE) + .encode(oid) + .encode(ASN1_String(i->second, string_type)) + .end_cons() + .end_cons(); + } + } + +} + +/* +* DER encode a DistinguishedName +*/ +void X509_DN::encode_into(DER_Encoder& der) const + { + auto dn_info = get_attributes(); + + der.start_cons(SEQUENCE); + + if(!m_dn_bits.empty()) + der.raw_bytes(m_dn_bits); + else + { + do_ava(der, dn_info, PRINTABLE_STRING, "X520.Country"); + do_ava(der, dn_info, DIRECTORY_STRING, "X520.State"); + do_ava(der, dn_info, DIRECTORY_STRING, "X520.Locality"); + do_ava(der, dn_info, DIRECTORY_STRING, "X520.Organization"); + do_ava(der, dn_info, DIRECTORY_STRING, "X520.OrganizationalUnit"); + do_ava(der, dn_info, DIRECTORY_STRING, "X520.CommonName"); + do_ava(der, dn_info, PRINTABLE_STRING, "X520.SerialNumber"); + } + + der.end_cons(); + } + +/* +* Decode a BER encoded DistinguishedName +*/ +void X509_DN::decode_from(BER_Decoder& source) + { + std::vector 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) + .end_cons(); + + add_attribute(oid, str.value()); + } + } + + m_dn_bits = bits; + } + +namespace { + +std::string to_short_form(const std::string& long_id) + { + 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::ostream& operator<<(std::ostream& out, const X509_DN& dn) + { + std::multimap contents = dn.contents(); + + for(std::multimap::const_iterator i = contents.begin(); + i != contents.end(); ++i) + { + out << to_short_form(i->first) << "=\""; + for(char c: i->second) + { + if(c == '\\' || c == '\"') + { + out << "\\"; + } + out << c; + } + out << "\""; + + if(std::next(i) != contents.end()) + { + 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/src/lib/x509/x509_dn.h b/src/lib/x509/x509_dn.h new file mode 100644 index 000000000..cbd89de7c --- /dev/null +++ b/src/lib/x509/x509_dn.h @@ -0,0 +1,65 @@ +/* +* X.509 Distinguished Name +* (C) 1999-2010 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X509_DN_H_ +#define BOTAN_X509_DN_H_ + +#include +#include +#include +#include +#include + +namespace Botan { + +/** +* Distinguished Name +*/ +class BOTAN_PUBLIC_API(2,0) X509_DN final : public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const override; + void decode_from(class BER_Decoder&) override; + + bool has_field(const std::string& attr) const; + std::vector get_attribute(const std::string& attr) const; + + std::string get_first_attribute(const std::string& attr) const; + + std::multimap get_attributes() const; + std::multimap contents() const; + + void add_attribute(const std::string& key, const std::string& val); + void add_attribute(const OID& oid, const std::string& val); + + static std::string deref_info_field(const std::string& key); + + /* + * Return the BER encoded data, if any + */ + const std::vector& get_bits() const; + + bool empty() const { return m_dn_info.empty(); } + + X509_DN() = default; + explicit X509_DN(const std::multimap& vals); + explicit X509_DN(const std::multimap& vals); + private: + std::multimap m_dn_info; + std::vector m_dn_bits; + }; + +bool BOTAN_PUBLIC_API(2,0) operator==(const X509_DN&, const X509_DN&); +bool BOTAN_PUBLIC_API(2,0) operator!=(const X509_DN&, const X509_DN&); +bool BOTAN_PUBLIC_API(2,0) operator<(const X509_DN&, const X509_DN&); + +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); + +} + +#endif diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp index 7b4f749fb..1ae7f5527 100644 --- a/src/tests/test_pkcs11_high_level.cpp +++ b/src/tests/test_pkcs11_high_level.cpp @@ -47,6 +47,7 @@ #if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_PKCS11) #include + #include #endif #if defined(BOTAN_HAS_HMAC_DRBG) diff --git a/src/tests/test_x509_dn.cpp b/src/tests/test_x509_dn.cpp index 55cf05c1c..74803909b 100644 --- a/src/tests/test_x509_dn.cpp +++ b/src/tests/test_x509_dn.cpp @@ -6,14 +6,14 @@ #include "tests.h" -#if defined(BOTAN_HAS_ASN1) +#if defined(BOTAN_HAS_CERTIFICATES) #include #include #endif namespace Botan_Tests { -#if defined(BOTAN_HAS_ASN1) +#if defined(BOTAN_HAS_CERTIFICATES) class X509_DN_Comparisons_Tests final : public Text_Based_Test { public: -- cgit v1.2.3