aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/asn1
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/asn1')
-rw-r--r--src/lib/asn1/alg_id.cpp105
-rw-r--r--src/lib/asn1/alg_id.h49
-rw-r--r--src/lib/asn1/asn1_alt_name.cpp241
-rw-r--r--src/lib/asn1/asn1_alt_name.h47
-rw-r--r--src/lib/asn1/asn1_attribute.cpp60
-rw-r--r--src/lib/asn1/asn1_attribute.h36
-rw-r--r--src/lib/asn1/asn1_obj.cpp68
-rw-r--r--src/lib/asn1/asn1_obj.h124
-rw-r--r--src/lib/asn1/asn1_oid.cpp189
-rw-r--r--src/lib/asn1/asn1_oid.h96
-rw-r--r--src/lib/asn1/asn1_str.cpp148
-rw-r--r--src/lib/asn1/asn1_str.h38
-rw-r--r--src/lib/asn1/asn1_time.cpp303
-rw-r--r--src/lib/asn1/asn1_time.h57
-rw-r--r--src/lib/asn1/ber_dec.cpp561
-rw-r--r--src/lib/asn1/ber_dec.h260
-rw-r--r--src/lib/asn1/der_enc.cpp410
-rw-r--r--src/lib/asn1/der_enc.h137
-rw-r--r--src/lib/asn1/info.txt10
-rw-r--r--src/lib/asn1/oid_lookup/default.cpp243
-rw-r--r--src/lib/asn1/oid_lookup/info.txt5
-rw-r--r--src/lib/asn1/oid_lookup/oids.cpp133
-rw-r--r--src/lib/asn1/oid_lookup/oids.h65
-rw-r--r--src/lib/asn1/x509_dn.cpp311
-rw-r--r--src/lib/asn1/x509_dn.h56
25 files changed, 3752 insertions, 0 deletions
diff --git a/src/lib/asn1/alg_id.cpp b/src/lib/asn1/alg_id.cpp
new file mode 100644
index 000000000..bb8dac80a
--- /dev/null
+++ b/src/lib/asn1/alg_id.cpp
@@ -0,0 +1,105 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/alg_id.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+
+namespace Botan {
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+ const std::vector<byte>& param)
+ {
+ oid = alg_id;
+ parameters = param;
+ }
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+ const std::vector<byte>& param)
+ {
+ oid = OIDS::lookup(alg_id);
+ parameters = param;
+ }
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const OID& alg_id,
+ Encoding_Option option)
+ {
+ const byte DER_NULL[] = { 0x05, 0x00 };
+
+ oid = alg_id;
+
+ if(option == USE_NULL_PARAM)
+ parameters += std::pair<const byte*, size_t>(DER_NULL, sizeof(DER_NULL));
+ }
+
+/*
+* Create an AlgorithmIdentifier
+*/
+AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id,
+ Encoding_Option option)
+ {
+ const byte DER_NULL[] = { 0x05, 0x00 };
+
+ oid = OIDS::lookup(alg_id);
+
+ if(option == USE_NULL_PARAM)
+ parameters += std::pair<const byte*, size_t>(DER_NULL, sizeof(DER_NULL));
+ }
+
+/*
+* Compare two AlgorithmIdentifiers
+*/
+bool operator==(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+ {
+ if(a1.oid != a2.oid)
+ return false;
+ if(a1.parameters != a2.parameters)
+ return false;
+ return true;
+ }
+
+/*
+* Compare two AlgorithmIdentifiers
+*/
+bool operator!=(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
+ {
+ return !(a1 == a2);
+ }
+
+/*
+* DER encode an AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const
+ {
+ codec.start_cons(SEQUENCE)
+ .encode(oid)
+ .raw_bytes(parameters)
+ .end_cons();
+ }
+
+/*
+* Decode a BER encoded AlgorithmIdentifier
+*/
+void AlgorithmIdentifier::decode_from(BER_Decoder& codec)
+ {
+ codec.start_cons(SEQUENCE)
+ .decode(oid)
+ .raw_bytes(parameters)
+ .end_cons();
+ }
+
+}
diff --git a/src/lib/asn1/alg_id.h b/src/lib/asn1/alg_id.h
new file mode 100644
index 000000000..d8b40e700
--- /dev/null
+++ b/src/lib/asn1/alg_id.h
@@ -0,0 +1,49 @@
+/*
+* Algorithm Identifier
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ALGORITHM_IDENTIFIER_H__
+#define BOTAN_ALGORITHM_IDENTIFIER_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Algorithm Identifier
+*/
+class BOTAN_DLL AlgorithmIdentifier : public ASN1_Object
+ {
+ public:
+ enum Encoding_Option { USE_NULL_PARAM };
+
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ AlgorithmIdentifier() {}
+ AlgorithmIdentifier(const OID&, Encoding_Option);
+ AlgorithmIdentifier(const std::string&, Encoding_Option);
+
+ AlgorithmIdentifier(const OID&, const std::vector<byte>&);
+ AlgorithmIdentifier(const std::string&, const std::vector<byte>&);
+
+ OID oid;
+ std::vector<byte> parameters;
+ };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_DLL operator==(const AlgorithmIdentifier&,
+ const AlgorithmIdentifier&);
+bool BOTAN_DLL operator!=(const AlgorithmIdentifier&,
+ const AlgorithmIdentifier&);
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_alt_name.cpp b/src/lib/asn1/asn1_alt_name.cpp
new file mode 100644
index 000000000..2e7116bac
--- /dev/null
+++ b/src/lib/asn1/asn1_alt_name.cpp
@@ -0,0 +1,241 @@
+/*
+* AlternativeName
+* (C) 1999-2007 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_alt_name.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/oids.h>
+#include <botan/internal/stl_util.h>
+#include <botan/charset.h>
+#include <botan/parsing.h>
+#include <botan/loadstor.h>
+
+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 == "" || str == "")
+ return;
+
+ auto range = alt_info.equal_range(type);
+ for(auto j = range.first; j != range.second; ++j)
+ if(j->second == str)
+ return;
+
+ multimap_insert(alt_info, type, str);
+ }
+
+/*
+* Add an OtherName field
+*/
+void AlternativeName::add_othername(const OID& oid, const std::string& value,
+ ASN1_Tag type)
+ {
+ if(value == "")
+ return;
+ multimap_insert(othernames, oid, ASN1_String(value, type));
+ }
+
+/*
+* Get the attributes of this alternative name
+*/
+std::multimap<std::string, std::string> AlternativeName::get_attributes() const
+ {
+ return alt_info;
+ }
+
+/*
+* Get the otherNames
+*/
+std::multimap<OID, ASN1_String> AlternativeName::get_othernames() const
+ {
+ return othernames;
+ }
+
+/*
+* 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 = alt_info.begin(); i != alt_info.end(); ++i)
+ multimap_insert(names, i->first, i->second);
+
+ for(auto i = othernames.begin(); i != 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 (alt_info.size() > 0 || 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.iso_8859());
+ }
+ else if(type == "IP")
+ {
+ const u32bit ip = string_to_ipv4(i->second);
+ byte 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, alt_info, "RFC822", ASN1_Tag(1));
+ encode_entries(der, alt_info, "DNS", ASN1_Tag(2));
+ encode_entries(der, alt_info, "URI", ASN1_Tag(6));
+ encode_entries(der, alt_info, "IP", ASN1_Tag(7));
+
+ for(auto i = othernames.begin(); i != 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)
+ {
+ const std::string value = Charset::transcode(ASN1::to_string(obj),
+ LATIN1_CHARSET,
+ LOCAL_CHARSET);
+
+ if(tag == 1) add_attribute("RFC822", value);
+ if(tag == 2) add_attribute("DNS", value);
+ if(tag == 6) add_attribute("URI", value);
+ }
+ else if(tag == 7)
+ {
+ if(obj.value.size() == 4)
+ {
+ const u32bit ip = load_be<u32bit>(&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
new file mode 100644
index 000000000..f2c83ed2c
--- /dev/null
+++ b/src/lib/asn1/asn1_alt_name.h
@@ -0,0 +1,47 @@
+/*
+* Common ASN.1 Objects
+* (C) 1999-2007 Jack Lloyd
+* 2007 Yves Jerschow
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_ALT_NAME_H__
+#define BOTAN_ASN1_ALT_NAME_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_str.h>
+#include <botan/asn1_oid.h>
+#include <map>
+
+namespace Botan {
+
+/**
+* Alternative Name
+*/
+class BOTAN_DLL AlternativeName : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ std::multimap<std::string, std::string> contents() const;
+
+ void add_attribute(const std::string&, const std::string&);
+ std::multimap<std::string, std::string> get_attributes() const;
+
+ void add_othername(const OID&, const std::string&, ASN1_Tag);
+ std::multimap<OID, ASN1_String> get_othernames() const;
+
+ bool has_items() const;
+
+ AlternativeName(const std::string& = "", const std::string& = "",
+ const std::string& = "", const std::string& = "");
+ private:
+ std::multimap<std::string, std::string> alt_info;
+ std::multimap<OID, ASN1_String> othernames;
+ };
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_attribute.cpp b/src/lib/asn1/asn1_attribute.cpp
new file mode 100644
index 000000000..dff52bef9
--- /dev/null
+++ b/src/lib/asn1/asn1_attribute.cpp
@@ -0,0 +1,60 @@
+/*
+* Attribute
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_attribute.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<byte>& attr_value)
+ {
+ oid = attr_oid;
+ parameters = attr_value;
+ }
+
+/*
+* Create an Attribute
+*/
+Attribute::Attribute(const std::string& attr_oid,
+ const std::vector<byte>& attr_value)
+ {
+ oid = OIDS::lookup(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/src/lib/asn1/asn1_attribute.h b/src/lib/asn1/asn1_attribute.h
new file mode 100644
index 000000000..877135803
--- /dev/null
+++ b/src/lib/asn1/asn1_attribute.h
@@ -0,0 +1,36 @@
+/*
+* ASN.1 Attribute
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_ATTRIBUTE_H__
+#define BOTAN_ASN1_ATTRIBUTE_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* Attribute
+*/
+class BOTAN_DLL Attribute : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder& to) const;
+ void decode_from(class BER_Decoder& from);
+
+ OID oid;
+ std::vector<byte> parameters;
+
+ Attribute() {}
+ Attribute(const OID&, const std::vector<byte>&);
+ Attribute(const std::string&, const std::vector<byte>&);
+ };
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_obj.cpp b/src/lib/asn1/asn1_obj.cpp
new file mode 100644
index 000000000..898e91614
--- /dev/null
+++ b/src/lib/asn1/asn1_obj.cpp
@@ -0,0 +1,68 @@
+/*
+* ASN.1 Internals
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_obj.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/data_src.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+/*
+* BER Decoding Exceptions
+*/
+BER_Decoding_Error::BER_Decoding_Error(const std::string& str) :
+ Decoding_Error("BER: " + str) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str, ASN1_Tag tag) :
+ BER_Decoding_Error(str + ": " + std::to_string(tag)) {}
+
+BER_Bad_Tag::BER_Bad_Tag(const std::string& str,
+ ASN1_Tag tag1, ASN1_Tag tag2) :
+ BER_Decoding_Error(str + ": " + std::to_string(tag1) + "/" + std::to_string(tag2)) {}
+
+namespace ASN1 {
+
+/*
+* Put some arbitrary bytes into a SEQUENCE
+*/
+std::vector<byte> put_in_sequence(const std::vector<byte>& contents)
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .raw_bytes(contents)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* Convert a BER object into a string object
+*/
+std::string to_string(const BER_Object& obj)
+ {
+ return std::string(reinterpret_cast<const char*>(&obj.value[0]),
+ obj.value.size());
+ }
+
+/*
+* Do heuristic tests for BER data
+*/
+bool maybe_BER(DataSource& source)
+ {
+ byte first_byte;
+ if(!source.peek_byte(first_byte))
+ throw Stream_IO_Error("ASN1::maybe_BER: Source was empty");
+
+ if(first_byte == (SEQUENCE | CONSTRUCTED))
+ return true;
+ return false;
+ }
+
+}
+
+}
diff --git a/src/lib/asn1/asn1_obj.h b/src/lib/asn1/asn1_obj.h
new file mode 100644
index 000000000..564f4ecdb
--- /dev/null
+++ b/src/lib/asn1/asn1_obj.h
@@ -0,0 +1,124 @@
+/*
+* ASN.1 Internals
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_H__
+#define BOTAN_ASN1_H__
+
+#include <botan/secmem.h>
+#include <botan/exceptn.h>
+
+namespace Botan {
+
+/**
+* ASN.1 Type and Class Tags
+*/
+enum ASN1_Tag {
+ UNIVERSAL = 0x00,
+ APPLICATION = 0x40,
+ CONTEXT_SPECIFIC = 0x80,
+
+ CONSTRUCTED = 0x20,
+
+ PRIVATE = CONSTRUCTED | CONTEXT_SPECIFIC,
+
+ EOC = 0x00,
+ BOOLEAN = 0x01,
+ INTEGER = 0x02,
+ BIT_STRING = 0x03,
+ OCTET_STRING = 0x04,
+ NULL_TAG = 0x05,
+ OBJECT_ID = 0x06,
+ ENUMERATED = 0x0A,
+ SEQUENCE = 0x10,
+ SET = 0x11,
+
+ UTF8_STRING = 0x0C,
+ NUMERIC_STRING = 0x12,
+ PRINTABLE_STRING = 0x13,
+ T61_STRING = 0x14,
+ IA5_STRING = 0x16,
+ VISIBLE_STRING = 0x1A,
+ BMP_STRING = 0x1E,
+
+ UTC_TIME = 0x17,
+ GENERALIZED_TIME = 0x18,
+
+ NO_OBJECT = 0xFF00,
+ DIRECTORY_STRING = 0xFF01
+};
+
+/**
+* Basic ASN.1 Object Interface
+*/
+class BOTAN_DLL ASN1_Object
+ {
+ public:
+ /**
+ * Encode whatever this object is into to
+ * @param to the DER_Encoder that will be written to
+ */
+ virtual void encode_into(class DER_Encoder& to) const = 0;
+
+ /**
+ * Decode whatever this object is from from
+ * @param from the BER_Decoder that will be read from
+ */
+ virtual void decode_from(class BER_Decoder& from) = 0;
+
+ virtual ~ASN1_Object() {}
+ };
+
+/**
+* BER Encoded Object
+*/
+class BOTAN_DLL BER_Object
+ {
+ public:
+ void assert_is_a(ASN1_Tag, ASN1_Tag);
+
+ ASN1_Tag type_tag, class_tag;
+ secure_vector<byte> value;
+ };
+
+/*
+* ASN.1 Utility Functions
+*/
+class DataSource;
+
+namespace ASN1 {
+
+std::vector<byte> put_in_sequence(const std::vector<byte>& val);
+std::string to_string(const BER_Object& obj);
+
+/**
+* Heuristics tests; is this object possibly BER?
+* @param src a data source that will be peeked at but not modified
+*/
+bool maybe_BER(DataSource& src);
+
+}
+
+/**
+* General BER Decoding Error Exception
+*/
+struct BOTAN_DLL BER_Decoding_Error : public Decoding_Error
+ {
+ BER_Decoding_Error(const std::string&);
+ };
+
+/**
+* Exception For Incorrect BER Taggings
+*/
+struct BOTAN_DLL BER_Bad_Tag : public BER_Decoding_Error
+ {
+ BER_Bad_Tag(const std::string& msg, ASN1_Tag tag);
+ BER_Bad_Tag(const std::string& msg, ASN1_Tag tag1, ASN1_Tag tag2);
+ };
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_oid.cpp b/src/lib/asn1/asn1_oid.cpp
new file mode 100644
index 000000000..964315080
--- /dev/null
+++ b/src/lib/asn1/asn1_oid.cpp
@@ -0,0 +1,189 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_oid.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/internal/bit_ops.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+/*
+* ASN.1 OID Constructor
+*/
+OID::OID(const std::string& oid_str)
+ {
+ if(oid_str != "")
+ {
+ try
+ {
+ id = parse_asn1_oid(oid_str);
+ }
+ catch(...)
+ {
+ throw Invalid_OID(oid_str);
+ }
+
+ if(id.size() < 2 || id[0] > 2)
+ throw Invalid_OID(oid_str);
+ if((id[0] == 0 || id[0] == 1) && id[1] > 39)
+ throw Invalid_OID(oid_str);
+ }
+ }
+
+/*
+* Clear the current OID
+*/
+void OID::clear()
+ {
+ id.clear();
+ }
+
+/*
+* Return this OID as a string
+*/
+std::string OID::as_string() const
+ {
+ std::string oid_str;
+ for(size_t i = 0; i != id.size(); ++i)
+ {
+ oid_str += std::to_string(id[i]);
+ if(i != id.size() - 1)
+ oid_str += '.';
+ }
+ return oid_str;
+ }
+
+/*
+* OID equality comparison
+*/
+bool OID::operator==(const OID& oid) const
+ {
+ if(id.size() != oid.id.size())
+ return false;
+ for(size_t i = 0; i != id.size(); ++i)
+ if(id[i] != oid.id[i])
+ return false;
+ return true;
+ }
+
+/*
+* Append another component to the OID
+*/
+OID& OID::operator+=(u32bit component)
+ {
+ id.push_back(component);
+ return (*this);
+ }
+
+/*
+* Append another component to the OID
+*/
+OID operator+(const OID& oid, u32bit component)
+ {
+ OID new_oid(oid);
+ new_oid += component;
+ return new_oid;
+ }
+
+/*
+* OID inequality comparison
+*/
+bool operator!=(const OID& a, const OID& b)
+ {
+ return !(a == b);
+ }
+
+/*
+* Compare two OIDs
+*/
+bool operator<(const OID& a, const OID& b)
+ {
+ const std::vector<u32bit>& oid1 = a.get_id();
+ const std::vector<u32bit>& oid2 = b.get_id();
+
+ if(oid1.size() < oid2.size())
+ return true;
+ if(oid1.size() > oid2.size())
+ return false;
+ for(size_t i = 0; i != oid1.size(); ++i)
+ {
+ if(oid1[i] < oid2[i])
+ return true;
+ if(oid1[i] > oid2[i])
+ return false;
+ }
+ return false;
+ }
+
+/*
+* DER encode an OBJECT IDENTIFIER
+*/
+void OID::encode_into(DER_Encoder& der) const
+ {
+ if(id.size() < 2)
+ throw Invalid_Argument("OID::encode_into: OID is invalid");
+
+ std::vector<byte> encoding;
+ encoding.push_back(40 * id[0] + id[1]);
+
+ for(size_t i = 2; i != id.size(); ++i)
+ {
+ if(id[i] == 0)
+ encoding.push_back(0);
+ else
+ {
+ size_t blocks = high_bit(id[i]) + 6;
+ blocks = (blocks - (blocks % 7)) / 7;
+
+ for(size_t j = 0; j != blocks - 1; ++j)
+ encoding.push_back(0x80 | ((id[i] >> 7*(blocks-j-1)) & 0x7F));
+ encoding.push_back(id[i] & 0x7F);
+ }
+ }
+ der.add_object(OBJECT_ID, UNIVERSAL, encoding);
+ }
+
+/*
+* Decode a BER encoded OBJECT IDENTIFIER
+*/
+void OID::decode_from(BER_Decoder& decoder)
+ {
+ BER_Object obj = decoder.get_next_object();
+ if(obj.type_tag != OBJECT_ID || obj.class_tag != UNIVERSAL)
+ throw BER_Bad_Tag("Error decoding OID, unknown tag",
+ obj.type_tag, obj.class_tag);
+ if(obj.value.size() < 2)
+ throw BER_Decoding_Error("OID encoding is too short");
+
+
+ clear();
+ id.push_back(obj.value[0] / 40);
+ id.push_back(obj.value[0] % 40);
+
+ size_t i = 0;
+ while(i != obj.value.size() - 1)
+ {
+ u32bit component = 0;
+ while(i != obj.value.size() - 1)
+ {
+ ++i;
+
+ if(component >> (32-7))
+ throw Decoding_Error("OID component overflow");
+
+ component = (component << 7) + (obj.value[i] & 0x7F);
+
+ if(!(obj.value[i] & 0x80))
+ break;
+ }
+ id.push_back(component);
+ }
+ }
+
+}
diff --git a/src/lib/asn1/asn1_oid.h b/src/lib/asn1/asn1_oid.h
new file mode 100644
index 000000000..a0b1edeba
--- /dev/null
+++ b/src/lib/asn1/asn1_oid.h
@@ -0,0 +1,96 @@
+/*
+* ASN.1 OID
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_OID_H__
+#define BOTAN_ASN1_OID_H__
+
+#include <botan/asn1_obj.h>
+#include <string>
+#include <vector>
+
+namespace Botan {
+
+/**
+* This class represents ASN.1 object identifiers.
+*/
+class BOTAN_DLL OID : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ /**
+ * Find out whether this OID is empty
+ * @return true is no OID value is set
+ */
+ bool empty() const { return id.size() == 0; }
+
+ /**
+ * Get this OID as list (vector) of its components.
+ * @return vector representing this OID
+ */
+ const std::vector<u32bit>& get_id() const { return id; }
+
+ /**
+ * Get this OID as a string
+ * @return string representing this OID
+ */
+ std::string as_string() const;
+
+ /**
+ * Compare two OIDs.
+ * @return true if they are equal, false otherwise
+ */
+ bool operator==(const OID&) const;
+
+ /**
+ * Reset this instance to an empty OID.
+ */
+ void clear();
+
+ /**
+ * Add a component to this OID.
+ * @param new_comp the new component to add to the end of this OID
+ * @return reference to *this
+ */
+ OID& operator+=(u32bit new_comp);
+
+ /**
+ * Construct an OID from a string.
+ * @param str a string in the form "a.b.c" etc., where a,b,c are numbers
+ */
+ OID(const std::string& str = "");
+ private:
+ std::vector<u32bit> id;
+ };
+
+/**
+* Append another component onto the OID.
+* @param oid the OID to add the new component to
+* @param new_comp the new component to add
+*/
+OID BOTAN_DLL operator+(const OID& oid, u32bit new_comp);
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is not equal to b
+*/
+bool BOTAN_DLL operator!=(const OID& a, const OID& b);
+
+/**
+* Compare two OIDs.
+* @param a the first OID
+* @param b the second OID
+* @return true if a is lexicographically smaller than b
+*/
+bool BOTAN_DLL operator<(const OID& a, const OID& b);
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_str.cpp b/src/lib/asn1/asn1_str.cpp
new file mode 100644
index 000000000..44db189f9
--- /dev/null
+++ b/src/lib/asn1/asn1_str.cpp
@@ -0,0 +1,148 @@
+/*
+* Simple ASN.1 String Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_str.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/charset.h>
+#include <botan/parsing.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Choose an encoding for the string
+*/
+ASN1_Tag choose_encoding(const std::string& str,
+ const std::string& type)
+ {
+ static const byte IS_PRINTABLE[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+
+ for(size_t i = 0; i != str.size(); ++i)
+ {
+ if(!IS_PRINTABLE[static_cast<byte>(str[i])])
+ {
+ if(type == "utf8") return UTF8_STRING;
+ if(type == "latin1") return T61_STRING;
+ throw Invalid_Argument("choose_encoding: Bad string type " + type);
+ }
+ }
+ return PRINTABLE_STRING;
+ }
+
+}
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str, ASN1_Tag t) : tag(t)
+ {
+ iso_8859_str = Charset::transcode(str, LOCAL_CHARSET, LATIN1_CHARSET);
+
+ if(tag == DIRECTORY_STRING)
+ tag = choose_encoding(iso_8859_str, "latin1");
+
+ if(tag != NUMERIC_STRING &&
+ tag != PRINTABLE_STRING &&
+ tag != VISIBLE_STRING &&
+ tag != T61_STRING &&
+ tag != IA5_STRING &&
+ tag != UTF8_STRING &&
+ tag != BMP_STRING)
+ throw Invalid_Argument("ASN1_String: Unknown string type " +
+ std::to_string(tag));
+ }
+
+/*
+* Create an ASN1_String
+*/
+ASN1_String::ASN1_String(const std::string& str)
+ {
+ iso_8859_str = Charset::transcode(str, LOCAL_CHARSET, LATIN1_CHARSET);
+ tag = choose_encoding(iso_8859_str, "latin1");
+ }
+
+/*
+* Return this string in ISO 8859-1 encoding
+*/
+std::string ASN1_String::iso_8859() const
+ {
+ return iso_8859_str;
+ }
+
+/*
+* Return this string in local encoding
+*/
+std::string ASN1_String::value() const
+ {
+ return Charset::transcode(iso_8859_str, LATIN1_CHARSET, LOCAL_CHARSET);
+ }
+
+/*
+* Return the type of this string object
+*/
+ASN1_Tag ASN1_String::tagging() const
+ {
+ return tag;
+ }
+
+/*
+* DER encode an ASN1_String
+*/
+void ASN1_String::encode_into(DER_Encoder& encoder) const
+ {
+ std::string value = iso_8859();
+ if(tagging() == UTF8_STRING)
+ value = Charset::transcode(value, LATIN1_CHARSET, UTF8_CHARSET);
+ encoder.add_object(tagging(), UNIVERSAL, value);
+ }
+
+/*
+* Decode a BER encoded ASN1_String
+*/
+void ASN1_String::decode_from(BER_Decoder& source)
+ {
+ BER_Object obj = source.get_next_object();
+
+ Character_Set charset_is;
+
+ if(obj.type_tag == BMP_STRING)
+ charset_is = UCS2_CHARSET;
+ else if(obj.type_tag == UTF8_STRING)
+ charset_is = UTF8_CHARSET;
+ else
+ charset_is = LATIN1_CHARSET;
+
+ *this = ASN1_String(
+ Charset::transcode(ASN1::to_string(obj), charset_is, LOCAL_CHARSET),
+ obj.type_tag);
+ }
+
+}
diff --git a/src/lib/asn1/asn1_str.h b/src/lib/asn1/asn1_str.h
new file mode 100644
index 000000000..42f1ef5ae
--- /dev/null
+++ b/src/lib/asn1/asn1_str.h
@@ -0,0 +1,38 @@
+/*
+* ASN.1 string type
+* (C) 1999-2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_STRING_H__
+#define BOTAN_ASN1_STRING_H__
+
+#include <botan/asn1_obj.h>
+
+namespace Botan {
+
+/**
+* Simple String
+*/
+class BOTAN_DLL ASN1_String : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ std::string value() const;
+ std::string iso_8859() const;
+
+ ASN1_Tag tagging() const;
+
+ ASN1_String(const std::string& = "");
+ ASN1_String(const std::string&, ASN1_Tag);
+ private:
+ std::string iso_8859_str;
+ ASN1_Tag tag;
+ };
+
+}
+
+#endif
diff --git a/src/lib/asn1/asn1_time.cpp b/src/lib/asn1/asn1_time.cpp
new file mode 100644
index 000000000..32f214e53
--- /dev/null
+++ b/src/lib/asn1/asn1_time.cpp
@@ -0,0 +1,303 @@
+/*
+* X.509 Time Types
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/asn1_time.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/charset.h>
+#include <botan/parsing.h>
+#include <botan/calendar.h>
+
+namespace Botan {
+
+/*
+* Create an X509_Time
+*/
+X509_Time::X509_Time(const std::string& time_str)
+ {
+ set_to(time_str);
+ }
+
+/*
+* Create a X509_Time from a time point
+*/
+X509_Time::X509_Time(const std::chrono::system_clock::time_point& time)
+ {
+ calendar_point cal = calendar_value(time);
+
+ year = cal.year;
+ month = cal.month;
+ day = cal.day;
+ hour = cal.hour;
+ minute = cal.minutes;
+ second = cal.seconds;
+
+ tag = (year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
+ }
+
+/*
+* Create an X509_Time
+*/
+X509_Time::X509_Time(const std::string& t_spec, ASN1_Tag t) : tag(t)
+ {
+ set_to(t_spec, tag);
+ }
+
+/*
+* Set the time with a human readable string
+*/
+void X509_Time::set_to(const std::string& time_str)
+ {
+ if(time_str == "")
+ {
+ year = month = day = hour = minute = second = 0;
+ tag = NO_OBJECT;
+ return;
+ }
+
+ std::vector<std::string> params;
+ std::string current;
+
+ for(size_t j = 0; j != time_str.size(); ++j)
+ {
+ if(Charset::is_digit(time_str[j]))
+ current += time_str[j];
+ else
+ {
+ if(current != "")
+ params.push_back(current);
+ current.clear();
+ }
+ }
+ if(current != "")
+ params.push_back(current);
+
+ if(params.size() < 3 || params.size() > 6)
+ throw Invalid_Argument("Invalid time specification " + time_str);
+
+ year = to_u32bit(params[0]);
+ month = to_u32bit(params[1]);
+ day = to_u32bit(params[2]);
+ hour = (params.size() >= 4) ? to_u32bit(params[3]) : 0;
+ minute = (params.size() >= 5) ? to_u32bit(params[4]) : 0;
+ second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
+
+ tag = (year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
+
+ if(!passes_sanity_check())
+ throw Invalid_Argument("Invalid time specification " + time_str);
+ }
+
+/*
+* Set the time with an ISO time format string
+*/
+void X509_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
+ {
+ if(spec_tag == GENERALIZED_TIME)
+ {
+ if(t_spec.size() != 13 && t_spec.size() != 15)
+ throw Invalid_Argument("Invalid GeneralizedTime: " + t_spec);
+ }
+ else if(spec_tag == UTC_TIME)
+ {
+ if(t_spec.size() != 11 && t_spec.size() != 13)
+ throw Invalid_Argument("Invalid UTCTime: " + t_spec);
+ }
+ else
+ {
+ throw Invalid_Argument("Invalid time tag " + std::to_string(spec_tag) + " val " + t_spec);
+ }
+
+ if(t_spec[t_spec.size()-1] != 'Z')
+ throw Invalid_Argument("Invalid time encoding: " + t_spec);
+
+ const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4;
+
+ std::vector<std::string> params;
+ std::string current;
+
+ for(size_t j = 0; j != YEAR_SIZE; ++j)
+ current += t_spec[j];
+ params.push_back(current);
+ current.clear();
+
+ for(size_t j = YEAR_SIZE; j != t_spec.size() - 1; ++j)
+ {
+ current += t_spec[j];
+ if(current.size() == 2)
+ {
+ params.push_back(current);
+ current.clear();
+ }
+ }
+
+ year = to_u32bit(params[0]);
+ month = to_u32bit(params[1]);
+ day = to_u32bit(params[2]);
+ hour = to_u32bit(params[3]);
+ minute = to_u32bit(params[4]);
+ second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
+ tag = spec_tag;
+
+ if(spec_tag == UTC_TIME)
+ {
+ if(year >= 50) year += 1900;
+ else year += 2000;
+ }
+
+ if(!passes_sanity_check())
+ throw Invalid_Argument("Invalid time specification " + t_spec);
+ }
+
+/*
+* DER encode a X509_Time
+*/
+void X509_Time::encode_into(DER_Encoder& der) const
+ {
+ if(tag != GENERALIZED_TIME && tag != UTC_TIME)
+ throw Invalid_Argument("X509_Time: Bad encoding tag");
+
+ der.add_object(tag, UNIVERSAL,
+ Charset::transcode(as_string(),
+ LOCAL_CHARSET,
+ LATIN1_CHARSET));
+ }
+
+/*
+* Decode a BER encoded X509_Time
+*/
+void X509_Time::decode_from(BER_Decoder& source)
+ {
+ BER_Object ber_time = source.get_next_object();
+
+ set_to(Charset::transcode(ASN1::to_string(ber_time),
+ LATIN1_CHARSET,
+ LOCAL_CHARSET),
+ ber_time.type_tag);
+ }
+
+/*
+* Return a string representation of the time
+*/
+std::string X509_Time::as_string() const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("X509_Time::as_string: No time set");
+
+ u32bit full_year = year;
+
+ if(tag == UTC_TIME)
+ {
+ if(year < 1950 || year >= 2050)
+ throw Encoding_Error("X509_Time: The time " + readable_string() +
+ " cannot be encoded as a UTCTime");
+
+ full_year = (year >= 2000) ? (year - 2000) : (year - 1900);
+ }
+
+ std::string repr = std::to_string(full_year*10000000000 +
+ month*100000000 +
+ day*1000000 +
+ hour*10000 +
+ minute*100 +
+ second) + "Z";
+
+ u32bit desired_size = (tag == UTC_TIME) ? 13 : 15;
+
+ while(repr.size() < desired_size)
+ repr = "0" + repr;
+
+ return repr;
+ }
+
+/*
+* Return if the time has been set somehow
+*/
+bool X509_Time::time_is_set() const
+ {
+ return (year != 0);
+ }
+
+/*
+* Return a human readable string representation
+*/
+std::string X509_Time::readable_string() const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("X509_Time::readable_string: No time set");
+
+ std::string output(24, 0);
+
+ std::sprintf(&output[0], "%04d/%02d/%02d %02d:%02d:%02d UTC",
+ year, month, day, hour, minute, second);
+
+ output.resize(23); // remove trailing null
+
+ return output;
+ }
+
+/*
+* Do a general sanity check on the time
+*/
+bool X509_Time::passes_sanity_check() const
+ {
+ if(year < 1950 || year > 2100)
+ return false;
+ if(month == 0 || month > 12)
+ return false;
+ if(day == 0 || day > 31)
+ return false;
+ if(hour >= 24 || minute > 60 || second > 60)
+ return false;
+ return true;
+ }
+
+/*
+* Compare this time against another
+*/
+s32bit X509_Time::cmp(const X509_Time& other) const
+ {
+ if(time_is_set() == false)
+ throw Invalid_State("X509_Time::cmp: No time set");
+
+ const s32bit EARLIER = -1, LATER = 1, SAME_TIME = 0;
+
+ if(year < other.year) return EARLIER;
+ if(year > other.year) return LATER;
+ if(month < other.month) return EARLIER;
+ if(month > other.month) return LATER;
+ if(day < other.day) return EARLIER;
+ if(day > other.day) return LATER;
+ if(hour < other.hour) return EARLIER;
+ if(hour > other.hour) return LATER;
+ if(minute < other.minute) return EARLIER;
+ if(minute > other.minute) return LATER;
+ if(second < other.second) return EARLIER;
+ if(second > other.second) return LATER;
+
+ return SAME_TIME;
+ }
+
+/*
+* Compare two X509_Times for in various ways
+*/
+bool operator==(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) == 0); }
+bool operator!=(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) != 0); }
+
+bool operator<=(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) <= 0); }
+bool operator>=(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) >= 0); }
+
+bool operator<(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) < 0); }
+bool operator>(const X509_Time& t1, const X509_Time& t2)
+ { return (t1.cmp(t2) > 0); }
+
+}
diff --git a/src/lib/asn1/asn1_time.h b/src/lib/asn1/asn1_time.h
new file mode 100644
index 000000000..95baacd86
--- /dev/null
+++ b/src/lib/asn1/asn1_time.h
@@ -0,0 +1,57 @@
+/*
+* ASN.1 Time Representation
+* (C) 1999-2007,2012 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_ASN1_TIME_H__
+#define BOTAN_ASN1_TIME_H__
+
+#include <botan/asn1_obj.h>
+#include <chrono>
+
+namespace Botan {
+
+/**
+* X.509 Time
+*/
+class BOTAN_DLL X509_Time : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ std::string as_string() const;
+ std::string readable_string() const;
+ bool time_is_set() const;
+
+ std::string to_string() const { return readable_string(); }
+
+ s32bit cmp(const X509_Time&) const;
+
+ void set_to(const std::string&);
+ void set_to(const std::string&, ASN1_Tag);
+
+ X509_Time(const std::chrono::system_clock::time_point& time);
+ X509_Time(const std::string& = "");
+ X509_Time(const std::string&, ASN1_Tag);
+ private:
+ bool passes_sanity_check() const;
+ u32bit year, month, day, hour, minute, second;
+ ASN1_Tag tag;
+ };
+
+/*
+* Comparison Operations
+*/
+bool BOTAN_DLL operator==(const X509_Time&, const X509_Time&);
+bool BOTAN_DLL operator!=(const X509_Time&, const X509_Time&);
+bool BOTAN_DLL operator<=(const X509_Time&, const X509_Time&);
+bool BOTAN_DLL operator>=(const X509_Time&, const X509_Time&);
+bool BOTAN_DLL operator<(const X509_Time&, const X509_Time&);
+bool BOTAN_DLL operator>(const X509_Time&, const X509_Time&);
+
+}
+
+#endif
diff --git a/src/lib/asn1/ber_dec.cpp b/src/lib/asn1/ber_dec.cpp
new file mode 100644
index 000000000..81b2e34f6
--- /dev/null
+++ b/src/lib/asn1/ber_dec.cpp
@@ -0,0 +1,561 @@
+/*
+* BER Decoder
+* (C) 1999-2008 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/ber_dec.h>
+#include <botan/bigint.h>
+#include <botan/get_byte.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* BER decode an ASN.1 type tag
+*/
+size_t decode_tag(DataSource* ber, ASN1_Tag& type_tag, ASN1_Tag& class_tag)
+ {
+ byte b;
+ if(!ber->read_byte(b))
+ {
+ class_tag = type_tag = NO_OBJECT;
+ return 0;
+ }
+
+ if((b & 0x1F) != 0x1F)
+ {
+ type_tag = ASN1_Tag(b & 0x1F);
+ class_tag = ASN1_Tag(b & 0xE0);
+ return 1;
+ }
+
+ size_t tag_bytes = 1;
+ class_tag = ASN1_Tag(b & 0xE0);
+
+ size_t tag_buf = 0;
+ while(true)
+ {
+ if(!ber->read_byte(b))
+ throw BER_Decoding_Error("Long-form tag truncated");
+ if(tag_buf & 0xFF000000)
+ throw BER_Decoding_Error("Long-form tag overflowed 32 bits");
+ ++tag_bytes;
+ tag_buf = (tag_buf << 7) | (b & 0x7F);
+ if((b & 0x80) == 0) break;
+ }
+ type_tag = ASN1_Tag(tag_buf);
+ return tag_bytes;
+ }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource*);
+
+/*
+* BER decode an ASN.1 length field
+*/
+size_t decode_length(DataSource* ber, size_t& field_size)
+ {
+ byte b;
+ if(!ber->read_byte(b))
+ throw BER_Decoding_Error("Length field not found");
+ field_size = 1;
+ if((b & 0x80) == 0)
+ return b;
+
+ field_size += (b & 0x7F);
+ if(field_size == 1) return find_eoc(ber);
+ if(field_size > 5)
+ throw BER_Decoding_Error("Length field is too large");
+
+ size_t length = 0;
+
+ for(size_t i = 0; i != field_size - 1; ++i)
+ {
+ if(get_byte(0, length) != 0)
+ throw BER_Decoding_Error("Field length overflow");
+ if(!ber->read_byte(b))
+ throw BER_Decoding_Error("Corrupted length field");
+ length = (length << 8) | b;
+ }
+ return length;
+ }
+
+/*
+* BER decode an ASN.1 length field
+*/
+size_t decode_length(DataSource* ber)
+ {
+ size_t dummy;
+ return decode_length(ber, dummy);
+ }
+
+/*
+* Find the EOC marker
+*/
+size_t find_eoc(DataSource* ber)
+ {
+ secure_vector<byte> buffer(DEFAULT_BUFFERSIZE), data;
+
+ while(true)
+ {
+ const size_t got = ber->peek(&buffer[0], buffer.size(), data.size());
+ if(got == 0)
+ break;
+
+ data += std::make_pair(&buffer[0], got);
+ }
+
+ DataSource_Memory source(data);
+ data.clear();
+
+ size_t length = 0;
+ while(true)
+ {
+ ASN1_Tag type_tag, class_tag;
+ size_t tag_size = decode_tag(&source, type_tag, class_tag);
+ if(type_tag == NO_OBJECT)
+ break;
+
+ size_t length_size = 0;
+ size_t item_size = decode_length(&source, length_size);
+ source.discard_next(item_size);
+
+ length += item_size + length_size + tag_size;
+
+ if(type_tag == EOC)
+ break;
+ }
+ return length;
+ }
+
+}
+
+/*
+* Check a type invariant on BER data
+*/
+void BER_Object::assert_is_a(ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(this->type_tag != type_tag || this->class_tag != class_tag)
+ throw BER_Decoding_Error("Tag mismatch when decoding got " +
+ std::to_string(this->type_tag) + "/" +
+ std::to_string(this->class_tag) + " expected " +
+ std::to_string(type_tag) + "/" +
+ std::to_string(class_tag));
+ }
+
+/*
+* Check if more objects are there
+*/
+bool BER_Decoder::more_items() const
+ {
+ if(source->end_of_data() && (pushed.type_tag == NO_OBJECT))
+ return false;
+ return true;
+ }
+
+/*
+* Verify that no bytes remain in the source
+*/
+BER_Decoder& BER_Decoder::verify_end()
+ {
+ if(!source->end_of_data() || (pushed.type_tag != NO_OBJECT))
+ throw Invalid_State("BER_Decoder::verify_end called, but data remains");
+ return (*this);
+ }
+
+/*
+* Save all the bytes remaining in the source
+*/
+BER_Decoder& BER_Decoder::raw_bytes(secure_vector<byte>& out)
+ {
+ out.clear();
+ byte buf;
+ while(source->read_byte(buf))
+ out.push_back(buf);
+ return (*this);
+ }
+
+BER_Decoder& BER_Decoder::raw_bytes(std::vector<byte>& out)
+ {
+ out.clear();
+ byte buf;
+ while(source->read_byte(buf))
+ out.push_back(buf);
+ return (*this);
+ }
+
+/*
+* Discard all the bytes remaining in the source
+*/
+BER_Decoder& BER_Decoder::discard_remaining()
+ {
+ byte buf;
+ while(source->read_byte(buf))
+ ;
+ return (*this);
+ }
+
+/*
+* Return the BER encoding of the next object
+*/
+BER_Object BER_Decoder::get_next_object()
+ {
+ BER_Object next;
+
+ if(pushed.type_tag != NO_OBJECT)
+ {
+ next = pushed;
+ pushed.class_tag = pushed.type_tag = NO_OBJECT;
+ return next;
+ }
+
+ decode_tag(source, next.type_tag, next.class_tag);
+ if(next.type_tag == NO_OBJECT)
+ return next;
+
+ size_t length = decode_length(source);
+ next.value.resize(length);
+ if(source->read(&next.value[0], length) != length)
+ throw BER_Decoding_Error("Value truncated");
+
+ if(next.type_tag == EOC && next.class_tag == UNIVERSAL)
+ return get_next_object();
+
+ return next;
+ }
+
+BER_Decoder& BER_Decoder::get_next(BER_Object& ber)
+ {
+ ber = get_next_object();
+ return (*this);
+ }
+
+/*
+* Push a object back into the stream
+*/
+void BER_Decoder::push_back(const BER_Object& obj)
+ {
+ if(pushed.type_tag != NO_OBJECT)
+ throw Invalid_State("BER_Decoder: Only one push back is allowed");
+ pushed = obj;
+ }
+
+/*
+* Begin decoding a CONSTRUCTED type
+*/
+BER_Decoder BER_Decoder::start_cons(ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, ASN1_Tag(class_tag | CONSTRUCTED));
+
+ BER_Decoder result(&obj.value[0], obj.value.size());
+ result.parent = this;
+ return result;
+ }
+
+/*
+* Finish decoding a CONSTRUCTED type
+*/
+BER_Decoder& BER_Decoder::end_cons()
+ {
+ if(!parent)
+ throw Invalid_State("BER_Decoder::end_cons called with NULL parent");
+ if(!source->end_of_data())
+ throw Decoding_Error("BER_Decoder::end_cons called with data left");
+ return (*parent);
+ }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(DataSource& src)
+ {
+ source = &src;
+ owns = false;
+ pushed.type_tag = pushed.class_tag = NO_OBJECT;
+ parent = nullptr;
+ }
+
+/*
+* BER_Decoder Constructor
+ */
+BER_Decoder::BER_Decoder(const byte data[], size_t length)
+ {
+ source = new DataSource_Memory(data, length);
+ owns = true;
+ pushed.type_tag = pushed.class_tag = NO_OBJECT;
+ parent = nullptr;
+ }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const secure_vector<byte>& data)
+ {
+ source = new DataSource_Memory(data);
+ owns = true;
+ pushed.type_tag = pushed.class_tag = NO_OBJECT;
+ parent = nullptr;
+ }
+
+/*
+* BER_Decoder Constructor
+*/
+BER_Decoder::BER_Decoder(const std::vector<byte>& data)
+ {
+ source = new DataSource_Memory(&data[0], data.size());
+ owns = true;
+ pushed.type_tag = pushed.class_tag = NO_OBJECT;
+ parent = nullptr;
+ }
+
+/*
+* BER_Decoder Copy Constructor
+*/
+BER_Decoder::BER_Decoder(const BER_Decoder& other)
+ {
+ source = other.source;
+ owns = false;
+ if(other.owns)
+ {
+ other.owns = false;
+ owns = true;
+ }
+ pushed.type_tag = pushed.class_tag = NO_OBJECT;
+ parent = other.parent;
+ }
+
+/*
+* BER_Decoder Destructor
+*/
+BER_Decoder::~BER_Decoder()
+ {
+ if(owns)
+ delete source;
+ source = nullptr;
+ }
+
+/*
+* Request for an object to decode itself
+*/
+BER_Decoder& BER_Decoder::decode(ASN1_Object& obj,
+ ASN1_Tag, ASN1_Tag)
+ {
+ obj.decode_from(*this);
+ return (*this);
+ }
+
+/*
+* Decode a BER encoded NULL
+*/
+BER_Decoder& BER_Decoder::decode_null()
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(NULL_TAG, UNIVERSAL);
+ if(obj.value.size())
+ throw BER_Decoding_Error("NULL object had nonzero size");
+ return (*this);
+ }
+
+/*
+* Decode a BER encoded BOOLEAN
+*/
+BER_Decoder& BER_Decoder::decode(bool& out)
+ {
+ return decode(out, BOOLEAN, UNIVERSAL);
+ }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(size_t& out)
+ {
+ return decode(out, INTEGER, UNIVERSAL);
+ }
+
+/*
+* Decode a BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(BigInt& out)
+ {
+ return decode(out, INTEGER, UNIVERSAL);
+ }
+
+BER_Decoder& BER_Decoder::decode_octet_string_bigint(BigInt& out)
+ {
+ secure_vector<byte> out_vec;
+ decode(out_vec, OCTET_STRING);
+ out = BigInt::decode(&out_vec[0], out_vec.size());
+ return (*this);
+ }
+
+std::vector<byte> BER_Decoder::get_next_octet_string()
+ {
+ std::vector<byte> out_vec;
+ decode(out_vec, OCTET_STRING);
+ return out_vec;
+ }
+
+/*
+* Decode a BER encoded BOOLEAN
+*/
+BER_Decoder& BER_Decoder::decode(bool& out,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(obj.value.size() != 1)
+ throw BER_Decoding_Error("BER boolean value had invalid size");
+
+ out = (obj.value[0]) ? true : false;
+ return (*this);
+ }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(size_t& out,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ BigInt integer;
+ decode(integer, type_tag, class_tag);
+
+ if(integer.bits() > 32)
+ throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+ out = 0;
+ for(size_t i = 0; i != 4; ++i)
+ out = (out << 8) | integer.byte_at(3-i);
+
+ return (*this);
+ }
+
+/*
+* Decode a small BER encoded INTEGER
+*/
+u64bit BER_Decoder::decode_constrained_integer(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t T_bytes)
+ {
+ if(T_bytes > 8)
+ throw BER_Decoding_Error("Can't decode small integer over 8 bytes");
+
+ BigInt integer;
+ decode(integer, type_tag, class_tag);
+
+ if(integer.bits() > 8*T_bytes)
+ throw BER_Decoding_Error("Decoded integer value larger than expected");
+
+ u64bit out = 0;
+ for(size_t i = 0; i != 8; ++i)
+ out = (out << 8) | integer.byte_at(7-i);
+
+ return out;
+ }
+
+/*
+* Decode a BER encoded INTEGER
+*/
+BER_Decoder& BER_Decoder::decode(BigInt& out,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(obj.value.empty())
+ out = 0;
+ else
+ {
+ const bool negative = (obj.value[0] & 0x80) ? true : false;
+
+ if(negative)
+ {
+ for(size_t i = obj.value.size(); i > 0; --i)
+ if(obj.value[i-1]--)
+ break;
+ for(size_t i = 0; i != obj.value.size(); ++i)
+ obj.value[i] = ~obj.value[i];
+ }
+
+ out = BigInt(&obj.value[0], obj.value.size());
+
+ if(negative)
+ out.flip_sign();
+ }
+
+ return (*this);
+ }
+
+/*
+* BER decode a BIT STRING or OCTET STRING
+*/
+BER_Decoder& BER_Decoder::decode(secure_vector<byte>& out, ASN1_Tag real_type)
+ {
+ return decode(out, real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* BER decode a BIT STRING or OCTET STRING
+*/
+BER_Decoder& BER_Decoder::decode(std::vector<byte>& out, ASN1_Tag real_type)
+ {
+ return decode(out, real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* BER decode a BIT STRING or OCTET STRING
+*/
+BER_Decoder& BER_Decoder::decode(secure_vector<byte>& buffer,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(real_type == OCTET_STRING)
+ buffer = obj.value;
+ else
+ {
+ if(obj.value[0] >= 8)
+ throw BER_Decoding_Error("Bad number of unused bits in BIT STRING");
+
+ buffer.resize(obj.value.size() - 1);
+ copy_mem(&buffer[0], &obj.value[1], obj.value.size() - 1);
+ }
+ return (*this);
+ }
+
+BER_Decoder& BER_Decoder::decode(std::vector<byte>& buffer,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
+
+ BER_Object obj = get_next_object();
+ obj.assert_is_a(type_tag, class_tag);
+
+ if(real_type == OCTET_STRING)
+ buffer = unlock(obj.value);
+ else
+ {
+ if(obj.value[0] >= 8)
+ throw BER_Decoding_Error("Bad number of unused bits in BIT STRING");
+
+ buffer.resize(obj.value.size() - 1);
+ copy_mem(&buffer[0], &obj.value[1], obj.value.size() - 1);
+ }
+ return (*this);
+ }
+
+}
diff --git a/src/lib/asn1/ber_dec.h b/src/lib/asn1/ber_dec.h
new file mode 100644
index 000000000..ebfa91c85
--- /dev/null
+++ b/src/lib/asn1/ber_dec.h
@@ -0,0 +1,260 @@
+/*
+* BER Decoder
+* (C) 1999-2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_BER_DECODER_H__
+#define BOTAN_BER_DECODER_H__
+
+#include <botan/asn1_oid.h>
+#include <botan/data_src.h>
+
+namespace Botan {
+
+/**
+* BER Decoding Object
+*/
+class BOTAN_DLL BER_Decoder
+ {
+ public:
+ BER_Object get_next_object();
+
+ std::vector<byte> get_next_octet_string();
+
+ void push_back(const BER_Object& obj);
+
+ bool more_items() const;
+ BER_Decoder& verify_end();
+ BER_Decoder& discard_remaining();
+
+ BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL);
+ BER_Decoder& end_cons();
+
+ BER_Decoder& get_next(BER_Object& ber);
+
+ BER_Decoder& raw_bytes(secure_vector<byte>& v);
+ BER_Decoder& raw_bytes(std::vector<byte>& v);
+
+ BER_Decoder& decode_null();
+ BER_Decoder& decode(bool& v);
+ BER_Decoder& decode(size_t& v);
+ BER_Decoder& decode(class BigInt& v);
+ BER_Decoder& decode(std::vector<byte>& v, ASN1_Tag type_tag);
+ BER_Decoder& decode(secure_vector<byte>& v, ASN1_Tag type_tag);
+
+ BER_Decoder& decode(bool& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(size_t& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(class BigInt& v,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(std::vector<byte>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(secure_vector<byte>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ BER_Decoder& decode(class ASN1_Object& obj,
+ ASN1_Tag type_tag = NO_OBJECT,
+ ASN1_Tag class_tag = NO_OBJECT);
+
+ BER_Decoder& decode_octet_string_bigint(class BigInt& b);
+
+ u64bit decode_constrained_integer(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t T_bytes);
+
+ template<typename T> BER_Decoder& decode_integer_type(T& out)
+ {
+ return decode_integer_type<T>(out, INTEGER, UNIVERSAL);
+ }
+
+ template<typename T>
+ BER_Decoder& decode_integer_type(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ out = decode_constrained_integer(type_tag, class_tag, sizeof(out));
+ return (*this);
+ }
+
+ template<typename T>
+ BER_Decoder& decode_optional(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const T& default_value = T());
+
+ template<typename T>
+ BER_Decoder& decode_optional_implicit(
+ T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ ASN1_Tag real_type,
+ ASN1_Tag real_class,
+ const T& default_value = T());
+
+ template<typename T>
+ BER_Decoder& decode_list(std::vector<T>& out,
+ ASN1_Tag type_tag = SEQUENCE,
+ ASN1_Tag class_tag = UNIVERSAL);
+
+ template<typename T>
+ BER_Decoder& decode_and_check(const T& expected,
+ const std::string& error_msg)
+ {
+ T actual;
+ decode(actual);
+
+ if(actual != expected)
+ throw Decoding_Error(error_msg);
+
+ return (*this);
+ }
+
+ /*
+ * Decode an OPTIONAL string type
+ */
+ template<typename Alloc>
+ BER_Decoder& decode_optional_string(std::vector<byte, Alloc>& out,
+ ASN1_Tag real_type,
+ u16bit type_no,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+ {
+ BER_Object obj = get_next_object();
+
+ ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+ if(obj.type_tag == type_tag && obj.class_tag == class_tag)
+ {
+ if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+ BER_Decoder(obj.value).decode(out, real_type).verify_end();
+ else
+ {
+ push_back(obj);
+ decode(out, real_type, type_tag, class_tag);
+ }
+ }
+ else
+ {
+ out.clear();
+ push_back(obj);
+ }
+
+ return (*this);
+ }
+
+ BER_Decoder& operator=(const BER_Decoder&) = delete;
+
+ BER_Decoder(DataSource&);
+
+ BER_Decoder(const byte[], size_t);
+
+ BER_Decoder(const secure_vector<byte>&);
+
+ BER_Decoder(const std::vector<byte>& vec);
+
+ BER_Decoder(const BER_Decoder&);
+ ~BER_Decoder();
+ private:
+ BER_Decoder* parent;
+ DataSource* source;
+ BER_Object pushed;
+ mutable bool owns;
+ };
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional(T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const T& default_value)
+ {
+ BER_Object obj = get_next_object();
+
+ if(obj.type_tag == type_tag && obj.class_tag == class_tag)
+ {
+ if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+ BER_Decoder(obj.value).decode(out).verify_end();
+ else
+ {
+ push_back(obj);
+ decode(out, type_tag, class_tag);
+ }
+ }
+ else
+ {
+ out = default_value;
+ push_back(obj);
+ }
+
+ return (*this);
+ }
+
+/*
+* Decode an OPTIONAL or DEFAULT element
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_optional_implicit(
+ T& out,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ ASN1_Tag real_type,
+ ASN1_Tag real_class,
+ const T& default_value)
+ {
+ BER_Object obj = get_next_object();
+
+ if(obj.type_tag == type_tag && obj.class_tag == class_tag)
+ {
+ obj.type_tag = real_type;
+ obj.class_tag = real_class;
+ push_back(obj);
+ decode(out, real_type, real_class);
+ }
+ else
+ {
+ out = default_value;
+ push_back(obj);
+ }
+
+ return (*this);
+ }
+/*
+* Decode a list of homogenously typed values
+*/
+template<typename T>
+BER_Decoder& BER_Decoder::decode_list(std::vector<T>& vec,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ BER_Decoder list = start_cons(type_tag, class_tag);
+
+ while(list.more_items())
+ {
+ T value;
+ list.decode(value);
+ vec.push_back(value);
+ }
+
+ list.end_cons();
+
+ return (*this);
+ }
+
+}
+
+#endif
diff --git a/src/lib/asn1/der_enc.cpp b/src/lib/asn1/der_enc.cpp
new file mode 100644
index 000000000..c1e5fd45c
--- /dev/null
+++ b/src/lib/asn1/der_enc.cpp
@@ -0,0 +1,410 @@
+/*
+* DER Encoder
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/der_enc.h>
+#include <botan/asn1_obj.h>
+#include <botan/bigint.h>
+#include <botan/get_byte.h>
+#include <botan/parsing.h>
+#include <botan/internal/bit_ops.h>
+#include <algorithm>
+
+namespace Botan {
+
+namespace {
+
+/*
+* DER encode an ASN.1 type tag
+*/
+secure_vector<byte> encode_tag(ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if((class_tag | 0xE0) != 0xE0)
+ throw Encoding_Error("DER_Encoder: Invalid class tag " +
+ std::to_string(class_tag));
+
+ secure_vector<byte> encoded_tag;
+ if(type_tag <= 30)
+ encoded_tag.push_back(static_cast<byte>(type_tag | class_tag));
+ else
+ {
+ size_t blocks = high_bit(type_tag) + 6;
+ blocks = (blocks - (blocks % 7)) / 7;
+
+ encoded_tag.push_back(class_tag | 0x1F);
+ for(size_t i = 0; i != blocks - 1; ++i)
+ encoded_tag.push_back(0x80 | ((type_tag >> 7*(blocks-i-1)) & 0x7F));
+ encoded_tag.push_back(type_tag & 0x7F);
+ }
+
+ return encoded_tag;
+ }
+
+/*
+* DER encode an ASN.1 length field
+*/
+secure_vector<byte> encode_length(size_t length)
+ {
+ secure_vector<byte> encoded_length;
+ if(length <= 127)
+ encoded_length.push_back(static_cast<byte>(length));
+ else
+ {
+ const size_t top_byte = significant_bytes(length);
+
+ encoded_length.push_back(static_cast<byte>(0x80 | top_byte));
+
+ for(size_t i = sizeof(length) - top_byte; i != sizeof(length); ++i)
+ encoded_length.push_back(get_byte(i, length));
+ }
+ return encoded_length;
+ }
+
+}
+
+/*
+* Return the encoded SEQUENCE/SET
+*/
+secure_vector<byte> DER_Encoder::DER_Sequence::get_contents()
+ {
+ const ASN1_Tag real_class_tag = ASN1_Tag(class_tag | CONSTRUCTED);
+
+ if(type_tag == SET)
+ {
+ std::sort(set_contents.begin(), set_contents.end());
+ for(size_t i = 0; i != set_contents.size(); ++i)
+ contents += set_contents[i];
+ set_contents.clear();
+ }
+
+ secure_vector<byte> result;
+ result += encode_tag(type_tag, real_class_tag);
+ result += encode_length(contents.size());
+ result += contents;
+ contents.clear();
+
+ return result;
+ }
+
+/*
+* Add an encoded value to the SEQUENCE/SET
+*/
+void DER_Encoder::DER_Sequence::add_bytes(const byte data[], size_t length)
+ {
+ if(type_tag == SET)
+ set_contents.push_back(secure_vector<byte>(data, data + length));
+ else
+ contents += std::make_pair(data, length);
+ }
+
+/*
+* Return the type and class taggings
+*/
+ASN1_Tag DER_Encoder::DER_Sequence::tag_of() const
+ {
+ return ASN1_Tag(type_tag | class_tag);
+ }
+
+/*
+* DER_Sequence Constructor
+*/
+DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Tag t1, ASN1_Tag t2) :
+ type_tag(t1), class_tag(t2)
+ {
+ }
+
+/*
+* Return the encoded contents
+*/
+secure_vector<byte> DER_Encoder::get_contents()
+ {
+ if(subsequences.size() != 0)
+ throw Invalid_State("DER_Encoder: Sequence hasn't been marked done");
+
+ secure_vector<byte> output;
+ std::swap(output, contents);
+ return output;
+ }
+
+/*
+* Start a new ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::start_cons(ASN1_Tag type_tag,
+ ASN1_Tag class_tag)
+ {
+ subsequences.push_back(DER_Sequence(type_tag, class_tag));
+ return (*this);
+ }
+
+/*
+* Finish the current ASN.1 SEQUENCE/SET/EXPLICIT
+*/
+DER_Encoder& DER_Encoder::end_cons()
+ {
+ if(subsequences.empty())
+ throw Invalid_State("DER_Encoder::end_cons: No such sequence");
+
+ secure_vector<byte> seq = subsequences[subsequences.size()-1].get_contents();
+ subsequences.pop_back();
+ raw_bytes(seq);
+ return (*this);
+ }
+
+/*
+* Start a new ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::start_explicit(u16bit type_no)
+ {
+ ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+
+ if(type_tag == SET)
+ throw Internal_Error("DER_Encoder.start_explicit(SET); cannot perform");
+
+ return start_cons(type_tag, CONTEXT_SPECIFIC);
+ }
+
+/*
+* Finish the current ASN.1 EXPLICIT encoding
+*/
+DER_Encoder& DER_Encoder::end_explicit()
+ {
+ return end_cons();
+ }
+
+/*
+* Write raw bytes into the stream
+*/
+DER_Encoder& DER_Encoder::raw_bytes(const secure_vector<byte>& val)
+ {
+ return raw_bytes(&val[0], val.size());
+ }
+
+DER_Encoder& DER_Encoder::raw_bytes(const std::vector<byte>& val)
+ {
+ return raw_bytes(&val[0], val.size());
+ }
+
+/*
+* Write raw bytes into the stream
+*/
+DER_Encoder& DER_Encoder::raw_bytes(const byte bytes[], size_t length)
+ {
+ if(subsequences.size())
+ subsequences[subsequences.size()-1].add_bytes(bytes, length);
+ else
+ contents += std::make_pair(bytes, length);
+
+ return (*this);
+ }
+
+/*
+* Encode a NULL object
+*/
+DER_Encoder& DER_Encoder::encode_null()
+ {
+ return add_object(NULL_TAG, UNIVERSAL, nullptr, 0);
+ }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true)
+ {
+ return encode(is_true, BOOLEAN, UNIVERSAL);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n)
+ {
+ return encode(BigInt(n), INTEGER, UNIVERSAL);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n)
+ {
+ return encode(n, INTEGER, UNIVERSAL);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const secure_vector<byte>& bytes,
+ ASN1_Tag real_type)
+ {
+ return encode(&bytes[0], bytes.size(),
+ real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const std::vector<byte>& bytes,
+ ASN1_Tag real_type)
+ {
+ return encode(&bytes[0], bytes.size(),
+ real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* Encode this object
+*/
+DER_Encoder& DER_Encoder::encode(const byte bytes[], size_t length,
+ ASN1_Tag real_type)
+ {
+ return encode(bytes, length, real_type, real_type, UNIVERSAL);
+ }
+
+/*
+* DER encode a BOOLEAN
+*/
+DER_Encoder& DER_Encoder::encode(bool is_true,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ byte val = is_true ? 0xFF : 0x00;
+ return add_object(type_tag, class_tag, &val, 1);
+ }
+
+/*
+* DER encode a small INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(size_t n,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ return encode(BigInt(n), type_tag, class_tag);
+ }
+
+/*
+* DER encode an INTEGER
+*/
+DER_Encoder& DER_Encoder::encode(const BigInt& n,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(n == 0)
+ return add_object(type_tag, class_tag, 0);
+
+ bool extra_zero = (n.bits() % 8 == 0);
+ secure_vector<byte> contents(extra_zero + n.bytes());
+ BigInt::encode(&contents[extra_zero], n);
+ if(n < 0)
+ {
+ for(size_t i = 0; i != contents.size(); ++i)
+ contents[i] = ~contents[i];
+ for(size_t i = contents.size(); i > 0; --i)
+ if(++contents[i-1])
+ break;
+ }
+
+ return add_object(type_tag, class_tag, contents);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const secure_vector<byte>& bytes,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ return encode(&bytes[0], bytes.size(),
+ real_type, type_tag, class_tag);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const std::vector<byte>& bytes,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ return encode(&bytes[0], bytes.size(),
+ real_type, type_tag, class_tag);
+ }
+
+/*
+* DER encode an OCTET STRING or BIT STRING
+*/
+DER_Encoder& DER_Encoder::encode(const byte bytes[], size_t length,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if(real_type != OCTET_STRING && real_type != BIT_STRING)
+ throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string");
+
+ if(real_type == BIT_STRING)
+ {
+ secure_vector<byte> encoded;
+ encoded.push_back(0);
+ encoded += std::make_pair(bytes, length);
+ return add_object(type_tag, class_tag, encoded);
+ }
+ else
+ return add_object(type_tag, class_tag, bytes, length);
+ }
+
+/*
+* Conditionally write some values to the stream
+*/
+DER_Encoder& DER_Encoder::encode_if(bool cond, DER_Encoder& codec)
+ {
+ if(cond)
+ return raw_bytes(codec.get_contents());
+ return (*this);
+ }
+
+DER_Encoder& DER_Encoder::encode_if(bool cond, const ASN1_Object& obj)
+ {
+ if(cond)
+ encode(obj);
+ return (*this);
+ }
+
+/*
+* Request for an object to encode itself
+*/
+DER_Encoder& DER_Encoder::encode(const ASN1_Object& obj)
+ {
+ obj.encode_into(*this);
+ return (*this);
+ }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const byte rep[], size_t length)
+ {
+ secure_vector<byte> buffer;
+ buffer += encode_tag(type_tag, class_tag);
+ buffer += encode_length(length);
+ buffer += std::make_pair(rep, length);
+
+ return raw_bytes(buffer);
+ }
+
+/*
+* Write the encoding of the byte(s)
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::string& rep_str)
+ {
+ const byte* rep = reinterpret_cast<const byte*>(rep_str.data());
+ const size_t rep_len = rep_str.size();
+ return add_object(type_tag, class_tag, rep, rep_len);
+ }
+
+/*
+* Write the encoding of the byte
+*/
+DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag,
+ ASN1_Tag class_tag, byte rep)
+ {
+ return add_object(type_tag, class_tag, &rep, 1);
+ }
+
+}
diff --git a/src/lib/asn1/der_enc.h b/src/lib/asn1/der_enc.h
new file mode 100644
index 000000000..61efb27b1
--- /dev/null
+++ b/src/lib/asn1/der_enc.h
@@ -0,0 +1,137 @@
+/*
+* DER Encoder
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_DER_ENCODER_H__
+#define BOTAN_DER_ENCODER_H__
+
+#include <botan/asn1_obj.h>
+#include <vector>
+
+namespace Botan {
+
+class BigInt;
+class ASN1_Object;
+
+/**
+* General DER Encoding Object
+*/
+class BOTAN_DLL DER_Encoder
+ {
+ public:
+ secure_vector<byte> get_contents();
+
+ std::vector<byte> get_contents_unlocked()
+ { return unlock(get_contents()); }
+
+ DER_Encoder& start_cons(ASN1_Tag type_tag,
+ ASN1_Tag class_tag = UNIVERSAL);
+ DER_Encoder& end_cons();
+
+ DER_Encoder& start_explicit(u16bit type_tag);
+ DER_Encoder& end_explicit();
+
+ DER_Encoder& raw_bytes(const byte val[], size_t len);
+ DER_Encoder& raw_bytes(const secure_vector<byte>& val);
+ DER_Encoder& raw_bytes(const std::vector<byte>& val);
+
+ DER_Encoder& encode_null();
+ DER_Encoder& encode(bool b);
+ DER_Encoder& encode(size_t s);
+ DER_Encoder& encode(const BigInt& n);
+ DER_Encoder& encode(const secure_vector<byte>& v, ASN1_Tag real_type);
+ DER_Encoder& encode(const std::vector<byte>& v, ASN1_Tag real_type);
+ DER_Encoder& encode(const byte val[], size_t len, ASN1_Tag real_type);
+
+ DER_Encoder& encode(bool b,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(size_t s,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const BigInt& n,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const std::vector<byte>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const secure_vector<byte>& v,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ DER_Encoder& encode(const byte v[], size_t len,
+ ASN1_Tag real_type,
+ ASN1_Tag type_tag,
+ ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+
+ template<typename T>
+ DER_Encoder& encode_optional(const T& value, const T& default_value)
+ {
+ if(value != default_value)
+ encode(value);
+ return (*this);
+ }
+
+ template<typename T>
+ DER_Encoder& encode_list(const std::vector<T>& values)
+ {
+ for(size_t i = 0; i != values.size(); ++i)
+ encode(values[i]);
+ return (*this);
+ }
+
+ DER_Encoder& encode(const ASN1_Object& obj);
+ DER_Encoder& encode_if(bool pred, DER_Encoder& enc);
+ DER_Encoder& encode_if(bool pred, const ASN1_Object& obj);
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const byte rep[], size_t length);
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::vector<byte>& rep)
+ {
+ return add_object(type_tag, class_tag, &rep[0], rep.size());
+ }
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const secure_vector<byte>& rep)
+ {
+ return add_object(type_tag, class_tag, &rep[0], rep.size());
+ }
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ const std::string& str);
+
+ DER_Encoder& add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
+ byte val);
+
+ private:
+ class DER_Sequence
+ {
+ public:
+ ASN1_Tag tag_of() const;
+ secure_vector<byte> get_contents();
+ void add_bytes(const byte[], size_t);
+ DER_Sequence(ASN1_Tag, ASN1_Tag);
+ private:
+ ASN1_Tag type_tag, class_tag;
+ secure_vector<byte> contents;
+ std::vector< secure_vector<byte> > set_contents;
+ };
+
+ secure_vector<byte> contents;
+ std::vector<DER_Sequence> subsequences;
+ };
+
+}
+
+#endif
diff --git a/src/lib/asn1/info.txt b/src/lib/asn1/info.txt
new file mode 100644
index 000000000..19b75bc4b
--- /dev/null
+++ b/src/lib/asn1/info.txt
@@ -0,0 +1,10 @@
+define ASN1 20131128
+
+load_on auto
+
+<requires>
+alloc
+bigint
+filters
+oid_lookup
+</requires>
diff --git a/src/lib/asn1/oid_lookup/default.cpp b/src/lib/asn1/oid_lookup/default.cpp
new file mode 100644
index 000000000..6904ec0ff
--- /dev/null
+++ b/src/lib/asn1/oid_lookup/default.cpp
@@ -0,0 +1,243 @@
+/*
+* OID Registry
+* (C) 1999-2010,2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/oids.h>
+
+namespace Botan {
+
+namespace OIDS {
+
+/*
+* Load all of the default OIDs
+*/
+void set_defaults()
+ {
+ /* Public key types */
+ OIDS::add_oidstr("1.2.840.113549.1.1.1", "RSA");
+ OIDS::add_oidstr("2.5.8.1.1", "RSA"); // RSA alternate
+ OIDS::add_oidstr("1.2.840.10040.4.1", "DSA");
+ OIDS::add_oidstr("1.2.840.10046.2.1", "DH");
+ OIDS::add_oidstr("1.3.6.1.4.1.3029.1.2.1", "ElGamal");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.1.1", "RW");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.1.2", "NR");
+
+ // X9.62 ecPublicKey, valid for ECDSA and ECDH (RFC 3279 sec 2.3.5)
+ OIDS::add_oidstr("1.2.840.10045.2.1", "ECDSA");
+
+ /*
+ * This is an OID defined for ECDH keys though rarely used for such.
+ * In this configuration it is accepted on decoding, but not used for
+ * encoding. You can enable it for encoding by calling
+ * OIDS::add_str2oid("ECDH", "1.3.132.1.12")
+ * from your application code.
+ */
+ OIDS::add_oid2str(OID("1.3.132.1.12"), "ECDH");
+
+ OIDS::add_oidstr("1.2.643.2.2.19", "GOST-34.10"); // RFC 4491
+
+ /* Ciphers */
+ OIDS::add_oidstr("1.3.14.3.2.7", "DES/CBC");
+ OIDS::add_oidstr("1.2.840.113549.3.7", "TripleDES/CBC");
+ OIDS::add_oidstr("1.2.840.113549.3.2", "RC2/CBC");
+ OIDS::add_oidstr("1.2.840.113533.7.66.10", "CAST-128/CBC");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.2", "AES-128/CBC");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.22", "AES-192/CBC");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.42", "AES-256/CBC");
+ OIDS::add_oidstr("1.2.410.200004.1.4", "SEED/CBC"); // RFC 4010
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.3.1", "Serpent/CBC");
+
+ /* Hash Functions */
+ OIDS::add_oidstr("1.2.840.113549.2.5", "MD5");
+ OIDS::add_oidstr("1.3.6.1.4.1.11591.12.2", "Tiger(24,3)");
+
+ OIDS::add_oidstr("1.3.14.3.2.26", "SHA-160");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.2.4", "SHA-224");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.2.1", "SHA-256");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.2.2", "SHA-384");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.2.3", "SHA-512");
+
+ /* MACs */
+ OIDS::add_oidstr("1.2.840.113549.2.7", "HMAC(SHA-160)");
+ OIDS::add_oidstr("1.2.840.113549.2.8", "HMAC(SHA-224)");
+ OIDS::add_oidstr("1.2.840.113549.2.9", "HMAC(SHA-256)");
+ OIDS::add_oidstr("1.2.840.113549.2.10", "HMAC(SHA-384)");
+ OIDS::add_oidstr("1.2.840.113549.2.11", "HMAC(SHA-512)");
+
+ /* Key Wrap */
+ OIDS::add_oidstr("1.2.840.113549.1.9.16.3.6", "KeyWrap.TripleDES");
+ OIDS::add_oidstr("1.2.840.113549.1.9.16.3.7", "KeyWrap.RC2");
+ OIDS::add_oidstr("1.2.840.113533.7.66.15", "KeyWrap.CAST-128");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.5", "KeyWrap.AES-128");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.25", "KeyWrap.AES-192");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.1.45", "KeyWrap.AES-256");
+
+ /* Compression */
+ OIDS::add_oidstr("1.2.840.113549.1.9.16.3.8", "Compression.Zlib");
+
+ /* Public key signature schemes */
+ OIDS::add_oidstr("1.2.840.113549.1.1.1", "RSA/EME-PKCS1-v1_5");
+ OIDS::add_oidstr("1.2.840.113549.1.1.2", "RSA/EMSA3(MD2)");
+ OIDS::add_oidstr("1.2.840.113549.1.1.4", "RSA/EMSA3(MD5)");
+ OIDS::add_oidstr("1.2.840.113549.1.1.5", "RSA/EMSA3(SHA-160)");
+ OIDS::add_oidstr("1.2.840.113549.1.1.11", "RSA/EMSA3(SHA-256)");
+ OIDS::add_oidstr("1.2.840.113549.1.1.12", "RSA/EMSA3(SHA-384)");
+ OIDS::add_oidstr("1.2.840.113549.1.1.13", "RSA/EMSA3(SHA-512)");
+ OIDS::add_oidstr("1.3.36.3.3.1.2", "RSA/EMSA3(RIPEMD-160)");
+
+ OIDS::add_oidstr("1.2.840.10040.4.3", "DSA/EMSA1(SHA-160)");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.3.1", "DSA/EMSA1(SHA-224)");
+ OIDS::add_oidstr("2.16.840.1.101.3.4.3.2", "DSA/EMSA1(SHA-256)");
+
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.1", "ECDSA/EMSA1_BSI(SHA-160)");
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.2", "ECDSA/EMSA1_BSI(SHA-224)");
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.3", "ECDSA/EMSA1_BSI(SHA-256)");
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.4", "ECDSA/EMSA1_BSI(SHA-384)");
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.5", "ECDSA/EMSA1_BSI(SHA-512)");
+ OIDS::add_oidstr("0.4.0.127.0.7.1.1.4.1.6", "ECDSA/EMSA1_BSI(RIPEMD-160)");
+
+ OIDS::add_oidstr("1.2.840.10045.4.1", "ECDSA/EMSA1(SHA-160)");
+ OIDS::add_oidstr("1.2.840.10045.4.3.1", "ECDSA/EMSA1(SHA-224)");
+ OIDS::add_oidstr("1.2.840.10045.4.3.2", "ECDSA/EMSA1(SHA-256)");
+ OIDS::add_oidstr("1.2.840.10045.4.3.3", "ECDSA/EMSA1(SHA-384)");
+ OIDS::add_oidstr("1.2.840.10045.4.3.4", "ECDSA/EMSA1(SHA-512)");
+
+ OIDS::add_oidstr("1.2.643.2.2.3", "GOST-34.10/EMSA1(GOST-R-34.11-94)");
+
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.1", "RW/EMSA2(RIPEMD-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.2", "RW/EMSA2(SHA-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.3", "RW/EMSA2(SHA-224)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.4", "RW/EMSA2(SHA-256)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.5", "RW/EMSA2(SHA-384)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.1.6", "RW/EMSA2(SHA-512)");
+
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.1", "RW/EMSA4(RIPEMD-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.2", "RW/EMSA4(SHA-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.3", "RW/EMSA4(SHA-224)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.4", "RW/EMSA4(SHA-256)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.5", "RW/EMSA4(SHA-384)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.1.2.6", "RW/EMSA4(SHA-512)");
+
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.1", "NR/EMSA2(RIPEMD-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.2", "NR/EMSA2(SHA-160)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.3", "NR/EMSA2(SHA-224)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.4", "NR/EMSA2(SHA-256)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.5", "NR/EMSA2(SHA-384)");
+ OIDS::add_oidstr("1.3.6.1.4.1.25258.2.2.1.6", "NR/EMSA2(SHA-512)");
+
+ OIDS::add_oidstr("2.5.4.3", "X520.CommonName");
+ OIDS::add_oidstr("2.5.4.4", "X520.Surname");
+ OIDS::add_oidstr("2.5.4.5", "X520.SerialNumber");
+ OIDS::add_oidstr("2.5.4.6", "X520.Country");
+ OIDS::add_oidstr("2.5.4.7", "X520.Locality");
+ OIDS::add_oidstr("2.5.4.8", "X520.State");
+ OIDS::add_oidstr("2.5.4.10", "X520.Organization");
+ OIDS::add_oidstr("2.5.4.11", "X520.OrganizationalUnit");
+ OIDS::add_oidstr("2.5.4.12", "X520.Title");
+ OIDS::add_oidstr("2.5.4.42", "X520.GivenName");
+ OIDS::add_oidstr("2.5.4.43", "X520.Initials");
+ OIDS::add_oidstr("2.5.4.44", "X520.GenerationalQualifier");
+ OIDS::add_oidstr("2.5.4.46", "X520.DNQualifier");
+ OIDS::add_oidstr("2.5.4.65", "X520.Pseudonym");
+
+ OIDS::add_oidstr("1.2.840.113549.1.5.12", "PKCS5.PBKDF2");
+ OIDS::add_oidstr("1.2.840.113549.1.5.1", "PBE-PKCS5v15(MD2,DES/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.4", "PBE-PKCS5v15(MD2,RC2/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.3", "PBE-PKCS5v15(MD5,DES/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.6", "PBE-PKCS5v15(MD5,RC2/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.10", "PBE-PKCS5v15(SHA-160,DES/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.11", "PBE-PKCS5v15(SHA-160,RC2/CBC)");
+ OIDS::add_oidstr("1.2.840.113549.1.5.13", "PBE-PKCS5v20");
+
+ OIDS::add_oidstr("1.2.840.113549.1.9.1", "PKCS9.EmailAddress");
+ OIDS::add_oidstr("1.2.840.113549.1.9.2", "PKCS9.UnstructuredName");
+ OIDS::add_oidstr("1.2.840.113549.1.9.3", "PKCS9.ContentType");
+ OIDS::add_oidstr("1.2.840.113549.1.9.4", "PKCS9.MessageDigest");
+ OIDS::add_oidstr("1.2.840.113549.1.9.7", "PKCS9.ChallengePassword");
+ OIDS::add_oidstr("1.2.840.113549.1.9.14", "PKCS9.ExtensionRequest");
+
+ OIDS::add_oidstr("1.2.840.113549.1.7.1", "CMS.DataContent");
+ OIDS::add_oidstr("1.2.840.113549.1.7.2", "CMS.SignedData");
+ OIDS::add_oidstr("1.2.840.113549.1.7.3", "CMS.EnvelopedData");
+ OIDS::add_oidstr("1.2.840.113549.1.7.5", "CMS.DigestedData");
+ OIDS::add_oidstr("1.2.840.113549.1.7.6", "CMS.EncryptedData");
+ OIDS::add_oidstr("1.2.840.113549.1.9.16.1.2", "CMS.AuthenticatedData");
+ OIDS::add_oidstr("1.2.840.113549.1.9.16.1.9", "CMS.CompressedData");
+
+ OIDS::add_oidstr("2.5.29.14", "X509v3.SubjectKeyIdentifier");
+ OIDS::add_oidstr("2.5.29.15", "X509v3.KeyUsage");
+ OIDS::add_oidstr("2.5.29.17", "X509v3.SubjectAlternativeName");
+ OIDS::add_oidstr("2.5.29.18", "X509v3.IssuerAlternativeName");
+ OIDS::add_oidstr("2.5.29.19", "X509v3.BasicConstraints");
+ OIDS::add_oidstr("2.5.29.20", "X509v3.CRLNumber");
+ OIDS::add_oidstr("2.5.29.21", "X509v3.ReasonCode");
+ OIDS::add_oidstr("2.5.29.23", "X509v3.HoldInstructionCode");
+ OIDS::add_oidstr("2.5.29.24", "X509v3.InvalidityDate");
+ OIDS::add_oidstr("2.5.29.31", "X509v3.CRLDistributionPoints");
+ OIDS::add_oidstr("2.5.29.32", "X509v3.CertificatePolicies");
+ OIDS::add_oidstr("2.5.29.35", "X509v3.AuthorityKeyIdentifier");
+ OIDS::add_oidstr("2.5.29.36", "X509v3.PolicyConstraints");
+ OIDS::add_oidstr("2.5.29.37", "X509v3.ExtendedKeyUsage");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.1.1", "PKIX.AuthorityInformationAccess");
+
+ OIDS::add_oidstr("2.5.29.32.0", "X509v3.AnyPolicy");
+
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.1", "PKIX.ServerAuth");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.2", "PKIX.ClientAuth");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.3", "PKIX.CodeSigning");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.4", "PKIX.EmailProtection");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.5", "PKIX.IPsecEndSystem");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.6", "PKIX.IPsecTunnel");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.7", "PKIX.IPsecUser");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.8", "PKIX.TimeStamping");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.3.9", "PKIX.OCSPSigning");
+
+ OIDS::add_oidstr("1.3.6.1.5.5.7.8.5", "PKIX.XMPPAddr");
+
+ OIDS::add_oidstr("1.3.6.1.5.5.7.48.1", "PKIX.OCSP");
+ OIDS::add_oidstr("1.3.6.1.5.5.7.48.1.1", "PKIX.OCSP.BasicResponse");
+
+ /* ECC domain parameters */
+ OIDS::add_oidstr("1.3.132.0.6", "secp112r1");
+ OIDS::add_oidstr("1.3.132.0.7", "secp112r2");
+ OIDS::add_oidstr("1.3.132.0.8", "secp160r1");
+ OIDS::add_oidstr("1.3.132.0.9", "secp160k1");
+ OIDS::add_oidstr("1.3.132.0.10", "secp256k1");
+ OIDS::add_oidstr("1.3.132.0.28", "secp128r1");
+ OIDS::add_oidstr("1.3.132.0.29", "secp128r2");
+ OIDS::add_oidstr("1.3.132.0.30", "secp160r2");
+ OIDS::add_oidstr("1.3.132.0.31", "secp192k1");
+ OIDS::add_oidstr("1.3.132.0.32", "secp224k1");
+ OIDS::add_oidstr("1.3.132.0.33", "secp224r1");
+ OIDS::add_oidstr("1.3.132.0.34", "secp384r1");
+ OIDS::add_oidstr("1.3.132.0.35", "secp521r1");
+
+ OIDS::add_oidstr("1.2.840.10045.3.1.1", "secp192r1");
+ OIDS::add_oidstr("1.2.840.10045.3.1.2", "x962_p192v2");
+ OIDS::add_oidstr("1.2.840.10045.3.1.3", "x962_p192v3");
+ OIDS::add_oidstr("1.2.840.10045.3.1.4", "x962_p239v1");
+ OIDS::add_oidstr("1.2.840.10045.3.1.5", "x962_p239v2");
+ OIDS::add_oidstr("1.2.840.10045.3.1.6", "x962_p239v3");
+ OIDS::add_oidstr("1.2.840.10045.3.1.7", "secp256r1");
+
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.1", "brainpool160r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.3", "brainpool192r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.5", "brainpool224r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.7", "brainpool256r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.9", "brainpool320r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.11", "brainpool384r1");
+ OIDS::add_oidstr("1.3.36.3.3.2.8.1.1.13", "brainpool512r1");
+
+ OIDS::add_oidstr("1.2.643.2.2.35.1", "gost_256A");
+ OIDS::add_oidstr("1.2.643.2.2.36.0", "gost_256A");
+
+ /* CVC */
+ OIDS::add_oidstr("0.4.0.127.0.7.3.1.2.1", "CertificateHolderAuthorizationTemplate");
+ }
+
+}
+
+}
diff --git a/src/lib/asn1/oid_lookup/info.txt b/src/lib/asn1/oid_lookup/info.txt
new file mode 100644
index 000000000..57b51e7d8
--- /dev/null
+++ b/src/lib/asn1/oid_lookup/info.txt
@@ -0,0 +1,5 @@
+define OID_LOOKUP 20131128
+
+<requires>
+asn1
+</requires>
diff --git a/src/lib/asn1/oid_lookup/oids.cpp b/src/lib/asn1/oid_lookup/oids.cpp
new file mode 100644
index 000000000..54944d1a2
--- /dev/null
+++ b/src/lib/asn1/oid_lookup/oids.cpp
@@ -0,0 +1,133 @@
+/*
+* OID Registry
+* (C) 1999-2008,2013 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/oids.h>
+#include <mutex>
+
+namespace Botan {
+
+namespace OIDS {
+
+namespace {
+
+class OID_Map
+ {
+ public:
+ void add_oid(const OID& oid, const std::string& str)
+ {
+ add_str2oid(oid, str);
+ add_oid2str(oid, str);
+ }
+
+ void add_str2oid(const OID& oid, const std::string& str)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ auto i = m_str2oid.find(str);
+ if(i == m_str2oid.end())
+ m_str2oid.insert(std::make_pair(str, oid));
+ }
+
+ void add_oid2str(const OID& oid, const std::string& str)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ auto i = m_oid2str.find(oid);
+ if(i == m_oid2str.end())
+ m_oid2str.insert(std::make_pair(oid, str));
+ }
+
+ std::string lookup(const OID& oid)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ auto i = m_oid2str.find(oid);
+ if(i != m_oid2str.end())
+ return i->second;
+
+ return "";
+ }
+
+ OID lookup(const std::string& str)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ auto i = m_str2oid.find(str);
+ if(i != m_str2oid.end())
+ return i->second;
+
+ // Try to parse as plain OID
+ try
+ {
+ return OID(str);
+ }
+ catch(...) {}
+
+ throw Lookup_Error("No object identifier found for " + str);
+ }
+
+ bool have_oid(const std::string& str)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return m_str2oid.find(str) != m_str2oid.end();
+ }
+
+ private:
+ std::mutex m_mutex;
+ std::map<std::string, OID> m_str2oid;
+ std::map<OID, std::string> m_oid2str;
+ };
+
+OID_Map& global_oid_map()
+ {
+ static OID_Map map;
+ return map;
+ }
+
+}
+
+void add_oid(const OID& oid, const std::string& name)
+ {
+ global_oid_map().add_oid(oid, name);
+ }
+
+void add_oidstr(const char* oidstr, const char* name)
+ {
+ add_oid(OID(oidstr), name);
+ }
+
+void add_oid2str(const OID& oid, const std::string& name)
+ {
+ global_oid_map().add_oid2str(oid, name);
+ }
+
+void add_str2oid(const OID& oid, const std::string& name)
+ {
+ global_oid_map().add_oid2str(oid, name);
+ }
+
+std::string lookup(const OID& oid)
+ {
+ return global_oid_map().lookup(oid);
+ }
+
+OID lookup(const std::string& name)
+ {
+ return global_oid_map().lookup(name);
+ }
+
+bool have_oid(const std::string& name)
+ {
+ return global_oid_map().have_oid(name);
+ }
+
+bool name_of(const OID& oid, const std::string& name)
+ {
+ return (oid == lookup(name));
+ }
+
+}
+
+}
diff --git a/src/lib/asn1/oid_lookup/oids.h b/src/lib/asn1/oid_lookup/oids.h
new file mode 100644
index 000000000..27533202f
--- /dev/null
+++ b/src/lib/asn1/oid_lookup/oids.h
@@ -0,0 +1,65 @@
+/*
+* OID Registry
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_OIDS_H__
+#define BOTAN_OIDS_H__
+
+#include <botan/asn1_oid.h>
+
+namespace Botan {
+
+namespace OIDS {
+
+/**
+* Register an OID to string mapping.
+* @param oid the oid to register
+* @param name the name to be associated with the oid
+*/
+BOTAN_DLL void add_oid(const OID& oid, const std::string& name);
+
+BOTAN_DLL void add_oid2str(const OID& oid, const std::string& name);
+BOTAN_DLL void add_str2oid(const OID& oid, const std::string& name);
+
+BOTAN_DLL void add_oidstr(const char* oidstr, const char* name);
+
+/**
+* See if an OID exists in the internal table.
+* @param oid the oid to check for
+* @return true if the oid is registered
+*/
+BOTAN_DLL bool have_oid(const std::string& oid);
+
+/**
+* Resolve an OID
+* @param oid the OID to look up
+* @return name associated with this OID
+*/
+BOTAN_DLL std::string lookup(const OID& oid);
+
+/**
+* Find the OID to a name. The lookup will be performed in the
+* general OID section of the configuration.
+* @param name the name to resolve
+* @return OID associated with the specified name
+*/
+BOTAN_DLL OID lookup(const std::string& name);
+
+/**
+* Tests whether the specified OID stands for the specified name.
+* @param oid the OID to check
+* @param name the name to check
+* @return true if the specified OID stands for the specified name
+*/
+BOTAN_DLL bool name_of(const OID& oid, const std::string& name);
+
+BOTAN_DLL void set_defaults();
+
+}
+
+}
+
+#endif
diff --git a/src/lib/asn1/x509_dn.cpp b/src/lib/asn1/x509_dn.cpp
new file mode 100644
index 000000000..672e18f18
--- /dev/null
+++ b/src/lib/asn1/x509_dn.cpp
@@ -0,0 +1,311 @@
+/*
+* X509_DN
+* (C) 1999-2007 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#include <botan/x509_dn.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>
+
+namespace Botan {
+
+/*
+* Create an empty X509_DN
+*/
+X509_DN::X509_DN()
+ {
+ }
+
+/*
+* Create an X509_DN
+*/
+X509_DN::X509_DN(const std::multimap<OID, std::string>& 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<std::string, std::string>& 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 == "")
+ return;
+
+ auto range = dn_info.equal_range(oid);
+ for(auto i = range.first; i != range.second; ++i)
+ if(i->second.value() == str)
+ return;
+
+ multimap_insert(dn_info, oid, ASN1_String(str));
+ 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 = dn_info.begin(); i != dn_info.end(); ++i)
+ 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 = dn_info.begin(); i != dn_info.end(); ++i)
+ multimap_insert(retval, OIDS::lookup(i->first), i->second.value());
+ return retval;
+ }
+
+/*
+* Get a single attribute type
+*/
+std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const
+ {
+ const OID oid = OIDS::lookup(deref_info_field(attr));
+
+ auto range = dn_info.equal_range(oid);
+
+ std::vector<std::string> 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<byte> X509_DN::get_bits() const
+ {
+ return 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") return "X520.CommonName";
+ if(info == "SerialNumber") return "X520.SerialNumber";
+ if(info == "Country") return "X520.Country";
+ if(info == "Organization") return "X520.Organization";
+ if(info == "Organizational Unit" || info == "OrgUnit")
+ return "X520.OrganizationalUnit";
+ if(info == "Locality") return "X520.Locality";
+ if(info == "State" || info == "Province") 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<OID, std::string>& 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(!dn_bits.empty())
+ der.raw_bytes(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<byte> 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)
+ .verify_end()
+ .end_cons();
+
+ add_attribute(oid, str.value());
+ }
+ }
+
+ 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<std::string, std::string> contents = dn.contents();
+
+ for(std::multimap<std::string, std::string>::const_iterator i = contents.begin();
+ i != contents.end(); ++i)
+ {
+ out << to_short_form(i->first) << "=" << i->second << ' ';
+ }
+ return out;
+ }
+
+}
diff --git a/src/lib/asn1/x509_dn.h b/src/lib/asn1/x509_dn.h
new file mode 100644
index 000000000..e37fe6627
--- /dev/null
+++ b/src/lib/asn1/x509_dn.h
@@ -0,0 +1,56 @@
+/*
+* X.509 Distinguished Name
+* (C) 1999-2010 Jack Lloyd
+*
+* Distributed under the terms of the Botan license
+*/
+
+#ifndef BOTAN_X509_DN_H__
+#define BOTAN_X509_DN_H__
+
+#include <botan/asn1_obj.h>
+#include <botan/asn1_oid.h>
+#include <botan/asn1_str.h>
+#include <map>
+#include <iosfwd>
+
+namespace Botan {
+
+/**
+* Distinguished Name
+*/
+class BOTAN_DLL X509_DN : public ASN1_Object
+ {
+ public:
+ void encode_into(class DER_Encoder&) const;
+ void decode_from(class BER_Decoder&);
+
+ std::multimap<OID, std::string> get_attributes() const;
+ std::vector<std::string> get_attribute(const std::string&) const;
+
+ std::multimap<std::string, std::string> 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<byte> get_bits() const;
+
+ X509_DN();
+ X509_DN(const std::multimap<OID, std::string>&);
+ X509_DN(const std::multimap<std::string, std::string>&);
+ private:
+ std::multimap<OID, ASN1_String> dn_info;
+ std::vector<byte> dn_bits;
+ };
+
+bool BOTAN_DLL operator==(const X509_DN&, const X509_DN&);
+bool BOTAN_DLL operator!=(const X509_DN&, const X509_DN&);
+bool BOTAN_DLL operator<(const X509_DN&, const X509_DN&);
+
+BOTAN_DLL std::ostream& operator<<(std::ostream& out, const X509_DN& dn);
+
+}
+
+#endif