diff options
author | lloyd <[email protected]> | 2008-09-28 22:14:54 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2008-09-28 22:14:54 +0000 |
commit | 31204986023619c385d378e79a6511bb81ef7b78 (patch) | |
tree | ce272a6585dc070f750f6875b0450d53145b935d /src/asn1 | |
parent | 42c0fe76ab6d9625d0e51c68b8dd187322c991bd (diff) |
Move almost all of the ASN.1, BER, and DER codec related code into new
module asn1
Move hex and base64 codecs into new codecs directory. Also move zlib and
bzip2 to codecs from compress.
Diffstat (limited to 'src/asn1')
-rw-r--r-- | src/asn1/alg_id.cpp | 101 | ||||
-rw-r--r-- | src/asn1/alg_id.h | 47 | ||||
-rw-r--r-- | src/asn1/asn1_alt.cpp | 226 | ||||
-rw-r--r-- | src/asn1/asn1_att.cpp | 58 | ||||
-rw-r--r-- | src/asn1/asn1_dn.cpp | 302 | ||||
-rw-r--r-- | src/asn1/asn1_int.cpp | 66 | ||||
-rw-r--r-- | src/asn1/asn1_int.h | 106 | ||||
-rw-r--r-- | src/asn1/asn1_ku.cpp | 41 | ||||
-rw-r--r-- | src/asn1/asn1_obj.h | 158 | ||||
-rw-r--r-- | src/asn1/asn1_oid.cpp | 174 | ||||
-rw-r--r-- | src/asn1/asn1_oid.h | 50 | ||||
-rw-r--r-- | src/asn1/asn1_str.cpp | 161 | ||||
-rw-r--r-- | src/asn1/asn1_tm.cpp | 295 | ||||
-rw-r--r-- | src/asn1/ber_dec.cpp | 469 | ||||
-rw-r--r-- | src/asn1/ber_dec.h | 130 | ||||
-rw-r--r-- | src/asn1/der_enc.cpp | 391 | ||||
-rw-r--r-- | src/asn1/der_enc.h | 89 | ||||
-rw-r--r-- | src/asn1/modinfo.txt | 23 |
18 files changed, 2887 insertions, 0 deletions
diff --git a/src/asn1/alg_id.cpp b/src/asn1/alg_id.cpp new file mode 100644 index 000000000..5e5db73b3 --- /dev/null +++ b/src/asn1/alg_id.cpp @@ -0,0 +1,101 @@ +/************************************************* +* Algorithm Identifier Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#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 MemoryRegion<byte>& param) + { + oid = alg_id; + parameters = param; + } + +/************************************************* +* Create an AlgorithmIdentifier * +*************************************************/ +AlgorithmIdentifier::AlgorithmIdentifier(const std::string& alg_id, + const MemoryRegion<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.append(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.append(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/asn1/alg_id.h b/src/asn1/alg_id.h new file mode 100644 index 000000000..8187fc070 --- /dev/null +++ b/src/asn1/alg_id.h @@ -0,0 +1,47 @@ +/************************************************* +* Algorithm Identifier Header File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#ifndef BOTAN_ALGORITHM_IDENTIFIER_H__ +#define BOTAN_ALGORITHM_IDENTIFIER_H__ + +#include <botan/asn1_int.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 MemoryRegion<byte>&); + AlgorithmIdentifier(const std::string&, const MemoryRegion<byte>&); + + OID oid; + SecureVector<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/asn1/asn1_alt.cpp b/src/asn1/asn1_alt.cpp new file mode 100644 index 000000000..035f918cb --- /dev/null +++ b/src/asn1/asn1_alt.cpp @@ -0,0 +1,226 @@ +/************************************************* +* AlternativeName Source File * +* (C) 1999-2007 Jack Lloyd * +* 2007 Yves Jerschow * +*************************************************/ + +#include <botan/asn1_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/oids.h> +#include <botan/stl_util.h> +#include <botan/charset.h> +#include <botan/parsing.h> +#include <botan/loadstor.h> + +namespace Botan { + +/************************************************* +* Create an AlternativeName * +*************************************************/ +AlternativeName::AlternativeName(const std::string& email_addr, + const std::string& uri, + const std::string& dns, + const std::string& ip) + { + add_attribute("RFC822", email_addr); + add_attribute("DNS", dns); + add_attribute("URI", uri); + add_attribute("IP", ip); + } + +/************************************************* +* Add an attribute to an alternative name * +*************************************************/ +void AlternativeName::add_attribute(const std::string& type, + const std::string& str) + { + if(type == "" || str == "") + return; + + typedef std::multimap<std::string, std::string>::iterator iter; + std::pair<iter, iter> range = alt_info.equal_range(type); + for(iter 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; + + typedef std::multimap<std::string, std::string>::const_iterator rdn_iter; + for(rdn_iter j = alt_info.begin(); j != alt_info.end(); ++j) + multimap_insert(names, j->first, j->second); + + typedef std::multimap<OID, ASN1_String>::const_iterator on_iter; + for(on_iter j = othernames.begin(); j != othernames.end(); ++j) + multimap_insert(names, OIDS::lookup(j->first), j->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) + { + typedef std::multimap<std::string, std::string>::const_iterator iter; + + std::pair<iter, iter> range = attr.equal_range(type); + for(iter j = range.first; j != range.second; ++j) + { + if(type == "RFC822" || type == "DNS" || type == "URI") + { + ASN1_String asn1_string(j->second, IA5_STRING); + encoder.add_object(tagging, CONTEXT_SPECIFIC, asn1_string.iso_8859()); + } + else if(type == "IP") + { + u32bit ip = string_to_ipv4(j->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)); + + std::multimap<OID, ASN1_String>::const_iterator i; + for(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; + + 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(); + + 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) + { + u32bit ip = load_be<u32bit>(obj.value.begin(), 0); + add_attribute("IP", ipv4_to_string(ip)); + } + } + + } + } + +} diff --git a/src/asn1/asn1_att.cpp b/src/asn1/asn1_att.cpp new file mode 100644 index 000000000..7c16ff3a5 --- /dev/null +++ b/src/asn1/asn1_att.cpp @@ -0,0 +1,58 @@ +/************************************************* +* Attribute Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_obj.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 MemoryRegion<byte>& attr_value) + { + oid = attr_oid; + parameters = attr_value; + } + +/************************************************* +* Create an Attribute * +*************************************************/ +Attribute::Attribute(const std::string& attr_oid, + const MemoryRegion<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/asn1/asn1_dn.cpp b/src/asn1/asn1_dn.cpp new file mode 100644 index 000000000..3fd0c09b0 --- /dev/null +++ b/src/asn1/asn1_dn.cpp @@ -0,0 +1,302 @@ +/************************************************* +* X509_DN Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/parsing.h> +#include <botan/stl_util.h> +#include <botan/oids.h> + +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) + { + std::multimap<OID, std::string>::const_iterator j; + for(j = args.begin(); j != args.end(); ++j) + add_attribute(j->first, j->second); + } + +/************************************************* +* Create an X509_DN * +*************************************************/ +X509_DN::X509_DN(const std::multimap<std::string, std::string>& args) + { + std::multimap<std::string, std::string>::const_iterator j; + for(j = args.begin(); j != args.end(); ++j) + add_attribute(OIDS::lookup(j->first), j->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; + + typedef std::multimap<OID, ASN1_String>::iterator rdn_iter; + + std::pair<rdn_iter, rdn_iter> range = dn_info.equal_range(oid); + for(rdn_iter j = range.first; j != range.second; ++j) + if(j->second.value() == str) + return; + + multimap_insert(dn_info, oid, ASN1_String(str)); + dn_bits.destroy(); + } + +/************************************************* +* Get the attributes of this X509_DN * +*************************************************/ +std::multimap<OID, std::string> X509_DN::get_attributes() const + { + typedef std::multimap<OID, ASN1_String>::const_iterator rdn_iter; + + std::multimap<OID, std::string> retval; + for(rdn_iter j = dn_info.begin(); j != dn_info.end(); ++j) + multimap_insert(retval, j->first, j->second.value()); + return retval; + } + +/************************************************* +* Get the contents of this X.500 Name * +*************************************************/ +std::multimap<std::string, std::string> X509_DN::contents() const + { + typedef std::multimap<OID, ASN1_String>::const_iterator rdn_iter; + + std::multimap<std::string, std::string> retval; + for(rdn_iter j = dn_info.begin(); j != dn_info.end(); ++j) + multimap_insert(retval, OIDS::lookup(j->first), j->second.value()); + return retval; + } + +/************************************************* +* Get a single attribute type * +*************************************************/ +std::vector<std::string> X509_DN::get_attribute(const std::string& attr) const + { + typedef std::multimap<OID, ASN1_String>::const_iterator rdn_iter; + + const OID oid = OIDS::lookup(deref_info_field(attr)); + std::pair<rdn_iter, rdn_iter> range = dn_info.equal_range(oid); + + std::vector<std::string> values; + for(rdn_iter j = range.first; j != range.second; ++j) + values.push_back(j->second.value()); + return values; + } + +/************************************************* +* Handle the decoding operation of a DN * +*************************************************/ +void X509_DN::do_decode(const MemoryRegion<byte>& bits) + { + 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; + } + +/************************************************* +* Return the BER encoded data, if any * +*************************************************/ +MemoryVector<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) + { + typedef std::multimap<OID, std::string>::const_iterator rdn_iter; + + std::multimap<OID, std::string> attr1 = dn1.get_attributes(); + std::multimap<OID, std::string> attr2 = dn2.get_attributes(); + + if(attr1.size() != attr2.size()) return false; + + rdn_iter p1 = attr1.begin(); + rdn_iter 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); + } + +/************************************************* +* Compare two X509_DNs * +*************************************************/ +bool operator<(const X509_DN& dn1, const X509_DN& dn2) + { + typedef std::multimap<OID, std::string>::const_iterator rdn_iter; + + std::multimap<OID, std::string> attr1 = dn1.get_attributes(); + std::multimap<OID, std::string> attr2 = dn2.get_attributes(); + + if(attr1.size() < attr2.size()) return true; + if(attr1.size() > attr2.size()) return false; + + for(rdn_iter p1 = attr1.begin(); p1 != attr1.end(); ++p1) + { + std::multimap<OID, std::string>::const_iterator p2; + 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) + { + typedef std::multimap<OID, std::string>::const_iterator rdn_iter; + + 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; + + std::pair<rdn_iter, rdn_iter> range = dn_info.equal_range(oid); + + for(rdn_iter j = range.first; j != range.second; ++j) + { + encoder.start_cons(SET) + .start_cons(SEQUENCE) + .encode(oid) + .encode(ASN1_String(j->second, string_type)) + .end_cons() + .end_cons(); + } + } + +} + +/************************************************* +* DER encode a DistinguishedName * +*************************************************/ +void X509_DN::encode_into(DER_Encoder& der) const + { + std::multimap<OID, std::string> dn_info = get_attributes(); + + der.start_cons(SEQUENCE); + + if(dn_bits.has_items()) + der.raw_bytes(dn_bits); + else + { + do_ava(der, dn_info, PRINTABLE_STRING, "X520.Country", true); + 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", true); + 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) + { + dn_info.clear(); + + source.start_cons(SEQUENCE) + .raw_bytes(dn_bits) + .end_cons(); + + do_decode(dn_bits); + } + +} diff --git a/src/asn1/asn1_int.cpp b/src/asn1/asn1_int.cpp new file mode 100644 index 000000000..e837dedf0 --- /dev/null +++ b/src/asn1/asn1_int.cpp @@ -0,0 +1,66 @@ +/************************************************* +* ASN.1 Internals Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_int.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 + ": " + to_string(tag)) {} + +BER_Bad_Tag::BER_Bad_Tag(const std::string& str, + ASN1_Tag tag1, ASN1_Tag tag2) : + BER_Decoding_Error(str + ": " + to_string(tag1) + "/" + to_string(tag2)) {} + +namespace ASN1 { + +/************************************************* +* Put some arbitrary bytes into a SEQUENCE * +*************************************************/ +SecureVector<byte> put_in_sequence(const MemoryRegion<byte>& contents) + { + return DER_Encoder() + .start_cons(SEQUENCE) + .raw_bytes(contents) + .end_cons() + .get_contents(); + } + +/************************************************* +* 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.begin()), + 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/asn1/asn1_int.h b/src/asn1/asn1_int.h new file mode 100644 index 000000000..3e0562b9c --- /dev/null +++ b/src/asn1/asn1_int.h @@ -0,0 +1,106 @@ +/************************************************* +* ASN.1 Internals Header File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#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, + PRIVATE = 0xC0, + + CONSTRUCTED = 0x20, + + 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: + virtual void encode_into(class DER_Encoder&) const = 0; + virtual void decode_from(class BER_Decoder&) = 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; + SecureVector<byte> value; + }; + +/************************************************* +* ASN.1 Utility Functions * +*************************************************/ +class DataSource; + +namespace ASN1 { + +SecureVector<byte> put_in_sequence(const MemoryRegion<byte>&); +std::string to_string(const BER_Object&); +bool maybe_BER(DataSource&); + +} + +/************************************************* +* General BER Decoding Error Exception * +*************************************************/ +struct BER_Decoding_Error : public Decoding_Error + { + BER_Decoding_Error(const std::string&); + }; + +/************************************************* +* Exception For Incorrect BER Taggings * +*************************************************/ +struct BER_Bad_Tag : public BER_Decoding_Error + { + BER_Bad_Tag(const std::string&, ASN1_Tag); + BER_Bad_Tag(const std::string&, ASN1_Tag, ASN1_Tag); + }; + +} + +#endif diff --git a/src/asn1/asn1_ku.cpp b/src/asn1/asn1_ku.cpp new file mode 100644 index 000000000..32c5944dd --- /dev/null +++ b/src/asn1/asn1_ku.cpp @@ -0,0 +1,41 @@ +/************************************************* +* KeyUsage Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> + +namespace Botan { + +namespace BER { + +/************************************************* +* Decode a BER encoded KeyUsage * +*************************************************/ +void decode(BER_Decoder& source, Key_Constraints& key_usage) + { + BER_Object obj = source.get_next_object(); + + if(obj.type_tag != BIT_STRING || obj.class_tag != UNIVERSAL) + throw BER_Bad_Tag("Bad tag for usage constraint", + obj.type_tag, obj.class_tag); + if(obj.value.size() != 2 && obj.value.size() != 3) + throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint"); + if(obj.value[0] >= 8) + throw BER_Decoding_Error("Invalid unused bits in usage constraint"); + + const byte mask = (0xFF << obj.value[0]); + obj.value[obj.value.size()-1] &= mask; + + u16bit usage = 0; + for(u32bit j = 1; j != obj.value.size(); ++j) + usage = (obj.value[j] << 8) | usage; + + key_usage = Key_Constraints(usage); + } + +} + +} diff --git a/src/asn1/asn1_obj.h b/src/asn1/asn1_obj.h new file mode 100644 index 000000000..67645ca08 --- /dev/null +++ b/src/asn1/asn1_obj.h @@ -0,0 +1,158 @@ +/************************************************* +* Common ASN.1 Objects Header File * +* (C) 1999-2007 Jack Lloyd * +* 2007 Yves Jerschow * +*************************************************/ + +#ifndef BOTAN_ASN1_OBJ_H__ +#define BOTAN_ASN1_OBJ_H__ + +#include <botan/asn1_int.h> +#include <botan/asn1_oid.h> +#include <botan/alg_id.h> +#include <vector> +#include <map> + +namespace Botan { + +/************************************************* +* Attribute * +*************************************************/ +class BOTAN_DLL Attribute : public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const; + void decode_from(class BER_Decoder&); + + OID oid; + MemoryVector<byte> parameters; + + Attribute() {} + Attribute(const OID&, const MemoryRegion<byte>&); + Attribute(const std::string&, const MemoryRegion<byte>&); + }; + +/************************************************* +* 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; + + s32bit cmp(const X509_Time&) const; + + void set_to(const std::string&); + void set_to(const std::string&, ASN1_Tag); + + X509_Time(u64bit); + 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; + }; + +/************************************************* +* 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; + }; + +/************************************************* +* 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&); + + void do_decode(const MemoryRegion<byte>&); + MemoryVector<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; + MemoryVector<byte> dn_bits; + }; + +/************************************************* +* 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; + }; + +/************************************************* +* 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_DN&, const X509_DN&); +bool BOTAN_DLL operator!=(const X509_DN&, const X509_DN&); +bool BOTAN_DLL operator<(const X509_DN&, const X509_DN&); + +/************************************************* +* Helper Functions * +*************************************************/ +bool BOTAN_DLL is_string_type(ASN1_Tag); + +} + +#endif diff --git a/src/asn1/asn1_oid.cpp b/src/asn1/asn1_oid.cpp new file mode 100644 index 000000000..80968ed8f --- /dev/null +++ b/src/asn1/asn1_oid.cpp @@ -0,0 +1,174 @@ +/************************************************* +* ASN.1 OID Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_oid.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/bit_ops.h> +#include <botan/parsing.h> + +namespace Botan { + +/************************************************* +* ASN.1 OID Constructor * +*************************************************/ +OID::OID(const std::string& oid_str) + { + if(oid_str != "") + { + id = parse_asn1_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(u32bit j = 0; j != id.size(); ++j) + { + oid_str += to_string(id[j]); + if(j != 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(u32bit j = 0; j != id.size(); ++j) + if(id[j] != oid.id[j]) + 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) + { + std::vector<u32bit> oid1 = a.get_id(); + std::vector<u32bit> oid2 = b.get_id(); + + if(oid1.size() < oid2.size()) + return true; + if(oid1.size() > oid2.size()) + return false; + for(u32bit j = 0; j != oid1.size(); ++j) + { + if(oid1[j] < oid2[j]) + return true; + if(oid1[j] > oid2[j]) + 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"); + + MemoryVector<byte> encoding; + encoding.append(40 * id[0] + id[1]); + + for(u32bit j = 2; j != id.size(); ++j) + { + if(id[j] == 0) + encoding.append(0); + else + { + u32bit blocks = high_bit(id[j]) + 6; + blocks = (blocks - (blocks % 7)) / 7; + + for(u32bit k = 0; k != blocks - 1; ++k) + encoding.append(0x80 | ((id[j] >> 7*(blocks-k-1)) & 0x7F)); + encoding.append(id[j] & 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); + + u32bit j = 0; + while(j != obj.value.size() - 1) + { + u32bit component = 0; + while(j != obj.value.size() - 1) + { + ++j; + component = (component << 7) + (obj.value[j] & 0x7F); + if(!(obj.value[j] & 0x80)) + break; + } + id.push_back(component); + } + } + +} diff --git a/src/asn1/asn1_oid.h b/src/asn1/asn1_oid.h new file mode 100644 index 000000000..73d0079a8 --- /dev/null +++ b/src/asn1/asn1_oid.h @@ -0,0 +1,50 @@ +/************************************************* +* ASN.1 OID Header File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#ifndef BOTAN_ASN1_OID_H__ +#define BOTAN_ASN1_OID_H__ + +#include <botan/asn1_int.h> +#include <string> +#include <vector> + +namespace Botan { + +/************************************************* +* ASN.1 Object Identifier * +*************************************************/ +class BOTAN_DLL OID : public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const; + void decode_from(class BER_Decoder&); + + bool is_empty() const { return id.size() == 0; } + std::vector<u32bit> get_id() const { return id; } + std::string as_string() const; + + bool operator==(const OID&) const; + void clear(); + + OID& operator+=(u32bit); + OID(const std::string& = ""); + private: + std::vector<u32bit> id; + }; + +/************************************************* +* Append another component onto the OID * +*************************************************/ +OID BOTAN_DLL operator+(const OID&, u32bit); + +/************************************************* +* Compare two OIDs * +*************************************************/ +bool BOTAN_DLL operator!=(const OID&, const OID&); +bool BOTAN_DLL operator<(const OID&, const OID&); + +} + +#endif diff --git a/src/asn1/asn1_str.cpp b/src/asn1/asn1_str.cpp new file mode 100644 index 000000000..52dcdc55e --- /dev/null +++ b/src/asn1/asn1_str.cpp @@ -0,0 +1,161 @@ +/************************************************* +* Simple ASN.1 String Types Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/charset.h> +#include <botan/parsing.h> +#include <botan/libstate.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(u32bit j = 0; j != str.size(); ++j) + { + if(!IS_PRINTABLE[static_cast<byte>(str[j])]) + { + if(type == "utf8") return UTF8_STRING; + if(type == "latin1") return T61_STRING; + throw Invalid_Argument("Bad setting for x509/ca/str_type: " + type); + } + } + return PRINTABLE_STRING; + } + +} + +/************************************************* +* Check if type is a known ASN.1 string type * +*************************************************/ +bool is_string_type(ASN1_Tag tag) + { + if(tag == NUMERIC_STRING || tag == PRINTABLE_STRING || + tag == VISIBLE_STRING || tag == T61_STRING || tag == IA5_STRING || + tag == UTF8_STRING || tag == BMP_STRING) + return true; + return false; + } + +/************************************************* +* 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, + global_state().option("x509/ca/str_type")); + + 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 " + + 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, + global_state().option("x509/ca/str_type")); + } + +/************************************************* +* 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/asn1/asn1_tm.cpp b/src/asn1/asn1_tm.cpp new file mode 100644 index 000000000..7cd051af7 --- /dev/null +++ b/src/asn1/asn1_tm.cpp @@ -0,0 +1,295 @@ +/************************************************* +* X.509 Time Types Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/asn1_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/charset.h> +#include <botan/parsing.h> +#include <ctime> + +namespace Botan { + +namespace { + +/************************************************* +* Convert a time_t to a struct tm * +*************************************************/ +std::tm get_tm(u64bit timer) + { + std::time_t time_val = static_cast<std::time_t>(timer); + + std::tm* tm_p = std::gmtime(&time_val); + if(tm_p == 0) + throw Encoding_Error("X509_Time: gmtime could not encode " + + to_string(timer)); + return (*tm_p); + } + +} + +/************************************************* +* Create an X509_Time * +*************************************************/ +X509_Time::X509_Time(const std::string& time_str) + { + set_to(time_str); + } + +/************************************************* +* Create an X509_Time * +*************************************************/ +X509_Time::X509_Time(u64bit timer) + { + std::tm time_info = get_tm(timer); + + year = time_info.tm_year + 1900; + month = time_info.tm_mon + 1; + day = time_info.tm_mday; + hour = time_info.tm_hour; + minute = time_info.tm_min; + second = time_info.tm_sec; + + if(year >= 2050) + tag = GENERALIZED_TIME; + else + tag = 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; + return; + } + + std::vector<std::string> params; + std::string current; + + for(u32bit 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; + + if(year >= 2050) + tag = GENERALIZED_TIME; + else + tag = 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 tag) + { + if(tag != GENERALIZED_TIME && tag != UTC_TIME) + throw Invalid_Argument("X509_Time: Invalid tag " + to_string(tag)); + if(tag == GENERALIZED_TIME && t_spec.size() != 13 && t_spec.size() != 15) + throw Invalid_Argument("Invalid GeneralizedTime: " + t_spec); + if(tag == UTC_TIME && t_spec.size() != 11 && t_spec.size() != 13) + throw Invalid_Argument("Invalid UTCTime: " + t_spec); + if(t_spec[t_spec.size()-1] != 'Z') + throw Invalid_Argument("Invalid time encoding: " + t_spec); + + const u32bit YEAR_SIZE = (tag == UTC_TIME) ? 2 : 4; + + std::vector<std::string> params; + std::string current; + + for(u32bit j = 0; j != YEAR_SIZE; ++j) + current += t_spec[j]; + params.push_back(current); + current.clear(); + + for(u32bit 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; + + if(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"); + + std::string asn1rep; + if(tag == GENERALIZED_TIME) + asn1rep = to_string(year, 4); + else + { + if(year < 1950 || year >= 2050) + throw Encoding_Error("X509_Time: The time " + readable_string() + + " cannot be encoded as a UTCTime"); + u32bit asn1year = (year >= 2000) ? (year - 2000) : (year - 1900); + asn1rep = to_string(asn1year, 2); + } + asn1rep += to_string(month, 2) + to_string(day, 2); + asn1rep += to_string(hour, 2) + to_string(minute, 2) + to_string(second, 2); + asn1rep += "Z"; + return asn1rep; + } + +/************************************************* +* 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 readable; + readable += to_string(year, 4) + "/"; + readable += to_string(month ) + "/"; + readable += to_string(day ) + " "; + readable += to_string(hour ) + ":"; + readable += to_string(minute, 2) + ":"; + readable += to_string(second, 2) + " UTC"; + return readable; + } + +/************************************************* +* 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); } + +} diff --git a/src/asn1/ber_dec.cpp b/src/asn1/ber_dec.cpp new file mode 100644 index 000000000..c725a5af9 --- /dev/null +++ b/src/asn1/ber_dec.cpp @@ -0,0 +1,469 @@ +/************************************************* +* BER Decoder Source File * +* (C) 1999-2008 Jack Lloyd * +*************************************************/ + +#include <botan/ber_dec.h> +#include <botan/bigint.h> +#include <botan/loadstor.h> + +namespace Botan { + +namespace { + +/************************************************* +* BER decode an ASN.1 type tag * +*************************************************/ +u32bit 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; + } + + u32bit tag_bytes = 1; + class_tag = ASN1_Tag(b & 0xE0); + + u32bit 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 * +*************************************************/ +u32bit find_eoc(DataSource*); + +/************************************************* +* BER decode an ASN.1 length field * +*************************************************/ +u32bit decode_length(DataSource* ber, u32bit& 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"); + + u32bit length = 0; + + for(u32bit j = 0; j != field_size - 1; ++j) + { + 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 * +*************************************************/ +u32bit decode_length(DataSource* ber) + { + u32bit dummy; + return decode_length(ber, dummy); + } + +/************************************************* +* Find the EOC marker * +*************************************************/ +u32bit find_eoc(DataSource* ber) + { + SecureVector<byte> buffer(DEFAULT_BUFFERSIZE), data; + + while(true) + { + const u32bit got = ber->peek(buffer, buffer.size(), data.size()); + if(got == 0) + break; + data.append(buffer, got); + } + + DataSource_Memory source(data); + data.destroy(); + + u32bit length = 0; + while(true) + { + ASN1_Tag type_tag, class_tag; + u32bit tag_size = decode_tag(&source, type_tag, class_tag); + if(type_tag == NO_OBJECT) + break; + + u32bit length_size = 0; + u32bit 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"); + } + +/************************************************* +* 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(MemoryRegion<byte>& out) + { + out.destroy(); + byte buf; + while(source->read_byte(buf)) + out.append(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; + + u32bit length = decode_length(source); + next.value.create(length); + if(source->read(next.value, length) != length) + throw BER_Decoding_Error("Value truncated"); + + if(next.type_tag == EOC && next.class_tag == UNIVERSAL) + return get_next_object(); + + return next; + } + +/************************************************* +* 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, 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 = 0; + } + +/************************************************* +* BER_Decoder Constructor * + *************************************************/ +BER_Decoder::BER_Decoder(const byte data[], u32bit length) + { + source = new DataSource_Memory(data, length); + owns = true; + pushed.type_tag = pushed.class_tag = NO_OBJECT; + parent = 0; + } + +/************************************************* +* BER_Decoder Constructor * +*************************************************/ +BER_Decoder::BER_Decoder(const MemoryRegion<byte>& data) + { + source = new DataSource_Memory(data); + owns = true; + pushed.type_tag = pushed.class_tag = NO_OBJECT; + parent = 0; + } + +/************************************************* +* 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 = 0; + } + +/************************************************* +* Request for an object to decode itself * +*************************************************/ +BER_Decoder& BER_Decoder::decode(ASN1_Object& obj) + { + 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(u32bit& out) + { + return decode(out, INTEGER, UNIVERSAL); + } + +/************************************************* +* Decode a BER encoded INTEGER * +*************************************************/ +BER_Decoder& BER_Decoder::decode(BigInt& out) + { + return decode(out, INTEGER, UNIVERSAL); + } + +/************************************************* +* 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(u32bit& out, + ASN1_Tag type_tag, ASN1_Tag class_tag) + { + BigInt integer; + decode(integer, type_tag, class_tag); + out = integer.to_u32bit(); + return (*this); + } + +/************************************************* +* 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.is_empty()) + out = 0; + else + { + const bool negative = (obj.value[0] & 0x80) ? true : false; + + if(negative) + { + for(u32bit j = obj.value.size(); j > 0; --j) + if(obj.value[j-1]--) + break; + for(u32bit j = 0; j != obj.value.size(); ++j) + obj.value[j] = ~obj.value[j]; + } + + out = BigInt(obj.value, obj.value.size()); + + if(negative) + out.flip_sign(); + } + + return (*this); + } + +/************************************************* +* BER decode a BIT STRING or OCTET STRING * +*************************************************/ +BER_Decoder& BER_Decoder::decode(MemoryRegion<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(MemoryRegion<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.set(obj.value + 1, obj.value.size() - 1); + } + return (*this); + } + +/************************************************* +* Decode an OPTIONAL string type * +*************************************************/ +BER_Decoder& BER_Decoder::decode_optional_string(MemoryRegion<byte>& out, + ASN1_Tag real_type, + u16bit type_no) + { + BER_Object obj = get_next_object(); + + ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no); + + out.clear(); + push_back(obj); + + if(obj.type_tag == type_tag && obj.class_tag == CONTEXT_SPECIFIC) + decode(out, real_type, type_tag, CONTEXT_SPECIFIC); + + return (*this); + } + +} diff --git a/src/asn1/ber_dec.h b/src/asn1/ber_dec.h new file mode 100644 index 000000000..9fec75832 --- /dev/null +++ b/src/asn1/ber_dec.h @@ -0,0 +1,130 @@ +/************************************************* +* BER Decoder Header File * +* (C) 1999-2008 Jack Lloyd * +*************************************************/ + +#ifndef BOTAN_BER_DECODER_H__ +#define BOTAN_BER_DECODER_H__ + +#include <botan/asn1_oid.h> +#include <botan/data_src.h> +#include <botan/enums.h> + +namespace Botan { + +/************************************************* +* BER Decoding Object * +*************************************************/ +class BOTAN_DLL BER_Decoder + { + public: + BER_Object get_next_object(); + void push_back(const BER_Object&); + + bool more_items() const; + BER_Decoder& verify_end(); + BER_Decoder& discard_remaining(); + + BER_Decoder start_cons(ASN1_Tag, ASN1_Tag = UNIVERSAL); + BER_Decoder& end_cons(); + + BER_Decoder& raw_bytes(MemoryRegion<byte>&); + + BER_Decoder& decode_null(); + BER_Decoder& decode(bool&); + BER_Decoder& decode(u32bit&); + BER_Decoder& decode(class BigInt&); + BER_Decoder& decode(MemoryRegion<byte>&, ASN1_Tag); + + BER_Decoder& decode(bool&, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + BER_Decoder& decode(u32bit&, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + BER_Decoder& decode(class BigInt&, + ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + BER_Decoder& decode(MemoryRegion<byte>&, ASN1_Tag, + ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + + BER_Decoder& decode(class ASN1_Object&); + + template<typename T> + BER_Decoder& decode_optional(T&, ASN1_Tag, ASN1_Tag, const T& = T()); + + template<typename T> + BER_Decoder& decode_list(std::vector<T>&, bool = true); + + BER_Decoder& decode_optional_string(MemoryRegion<byte>&, + ASN1_Tag, u16bit); + + BER_Decoder(DataSource&); + BER_Decoder(const byte[], u32bit); + BER_Decoder(const MemoryRegion<byte>&); + BER_Decoder(const BER_Decoder&); + ~BER_Decoder(); + private: + BER_Decoder& operator=(const BER_Decoder&) { return (*this); } + + 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) + 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 a list of homogenously typed values * +*************************************************/ +template<typename T> +BER_Decoder& BER_Decoder::decode_list(std::vector<T>& vec, bool clear_it) + { + if(clear_it) + vec.clear(); + + while(more_items()) + { + T value; + decode(value); + vec.push_back(value); + } + return (*this); + } + +/************************************************* +* BER Decoding Functions * +*************************************************/ +namespace BER { + +void BOTAN_DLL decode(BER_Decoder&, Key_Constraints&); + +} + +} + +#endif diff --git a/src/asn1/der_enc.cpp b/src/asn1/der_enc.cpp new file mode 100644 index 000000000..1ab5ddd4e --- /dev/null +++ b/src/asn1/der_enc.cpp @@ -0,0 +1,391 @@ +/************************************************* +* DER Encoder Source File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#include <botan/der_enc.h> +#include <botan/asn1_int.h> +#include <botan/bigint.h> +#include <botan/loadstor.h> +#include <botan/bit_ops.h> +#include <botan/parsing.h> +#include <algorithm> + +namespace Botan { + +namespace { + +/************************************************* +* DER encode an ASN.1 type tag * +*************************************************/ +SecureVector<byte> encode_tag(ASN1_Tag type_tag, ASN1_Tag class_tag) + { + if((class_tag | 0xE0) != 0xE0) + throw Encoding_Error("DER_Encoder: Invalid class tag " + + to_string(class_tag)); + + SecureVector<byte> encoded_tag; + if(type_tag <= 30) + encoded_tag.append(static_cast<byte>(type_tag | class_tag)); + else + { + u32bit blocks = high_bit(type_tag) + 6; + blocks = (blocks - (blocks % 7)) / 7; + + encoded_tag.append(class_tag | 0x1F); + for(u32bit k = 0; k != blocks - 1; ++k) + encoded_tag.append(0x80 | ((type_tag >> 7*(blocks-k-1)) & 0x7F)); + encoded_tag.append(type_tag & 0x7F); + } + + return encoded_tag; + } + +/************************************************* +* DER encode an ASN.1 length field * +*************************************************/ +SecureVector<byte> encode_length(u32bit length) + { + SecureVector<byte> encoded_length; + if(length <= 127) + encoded_length.append(static_cast<byte>(length)); + else + { + const u32bit top_byte = significant_bytes(length); + encoded_length.append(static_cast<byte>(0x80 | top_byte)); + for(u32bit j = 4-top_byte; j != 4; ++j) + encoded_length.append(get_byte(j, length)); + } + return encoded_length; + } + +} + +/************************************************* +* Return the encoded SEQUENCE/SET * +*************************************************/ +SecureVector<byte> DER_Encoder::DER_Sequence::get_contents() + { + const ASN1_Tag real_class_tag = ASN1_Tag(class_tag | CONSTRUCTED); + + SecureVector<byte> encoded_tag = encode_tag(type_tag, real_class_tag); + + if(type_tag == SET) + { + std::sort(set_contents.begin(), set_contents.end()); + for(u32bit j = 0; j != set_contents.size(); ++j) + contents.append(set_contents[j]); + set_contents.clear(); + } + + SecureVector<byte> encoded_length = encode_length(contents.size()); + + SecureVector<byte> retval; + retval.append(encoded_tag); + retval.append(encoded_length); + retval.append(contents); + contents.destroy(); + return retval; + } + +/************************************************* +* Add an encoded value to the SEQUENCE/SET * +*************************************************/ +void DER_Encoder::DER_Sequence::add_bytes(const byte data[], u32bit length) + { + if(type_tag == SET) + set_contents.push_back(SecureVector<byte>(data, length)); + else + contents.append(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 * +*************************************************/ +SecureVector<byte> DER_Encoder::get_contents() + { + if(subsequences.size() != 0) + throw Invalid_State("DER_Encoder: Sequence hasn't been marked done"); + + SecureVector<byte> retval; + retval = contents; + contents.destroy(); + return retval; + } + +/************************************************* +* 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"); + + SecureVector<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 MemoryRegion<byte>& val) + { + return raw_bytes(val.begin(), val.size()); + } + +/************************************************* +* Write raw bytes into the stream * +*************************************************/ +DER_Encoder& DER_Encoder::raw_bytes(const byte bytes[], u32bit length) + { + if(subsequences.size()) + subsequences[subsequences.size()-1].add_bytes(bytes, length); + else + contents.append(bytes, length); + + return (*this); + } + +/************************************************* +* Encode a NULL object * +*************************************************/ +DER_Encoder& DER_Encoder::encode_null() + { + return add_object(NULL_TAG, UNIVERSAL, 0, 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(u32bit 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 MemoryRegion<byte>& bytes, + ASN1_Tag real_type) + { + return encode(bytes.begin(), bytes.size(), + real_type, real_type, UNIVERSAL); + } + +/************************************************* +* Encode this object * +*************************************************/ +DER_Encoder& DER_Encoder::encode(const byte bytes[], u32bit 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(u32bit 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); + SecureVector<byte> contents(extra_zero + n.bytes()); + BigInt::encode(contents.begin() + extra_zero, n); + if(n < 0) + { + for(u32bit j = 0; j != contents.size(); ++j) + contents[j] = ~contents[j]; + for(u32bit j = contents.size(); j > 0; --j) + if(++contents[j-1]) + break; + } + + return add_object(type_tag, class_tag, contents); + } + +/************************************************* +* DER encode an OCTET STRING or BIT STRING * +*************************************************/ +DER_Encoder& DER_Encoder::encode(const MemoryRegion<byte>& bytes, + ASN1_Tag real_type, + ASN1_Tag type_tag, ASN1_Tag class_tag) + { + return encode(bytes.begin(), bytes.size(), + real_type, type_tag, class_tag); + } + +/************************************************* +* DER encode an OCTET STRING or BIT STRING * +*************************************************/ +DER_Encoder& DER_Encoder::encode(const byte bytes[], u32bit 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) + { + SecureVector<byte> encoded; + encoded.append(0); + encoded.append(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); + } + +/************************************************* +* 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[], u32bit length) + { + SecureVector<byte> encoded_tag = encode_tag(type_tag, class_tag); + SecureVector<byte> encoded_length = encode_length(length); + + SecureVector<byte> buffer; + buffer.append(encoded_tag); + buffer.append(encoded_length); + buffer.append(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 MemoryRegion<byte>& rep_buf) + { + const byte* rep = rep_buf.begin(); + const u32bit rep_len = rep_buf.size(); + return add_object(type_tag, class_tag, rep, rep_len); + } + +/************************************************* +* 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 u32bit 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/asn1/der_enc.h b/src/asn1/der_enc.h new file mode 100644 index 000000000..5b3c11489 --- /dev/null +++ b/src/asn1/der_enc.h @@ -0,0 +1,89 @@ +/************************************************* +* DER Encoder Header File * +* (C) 1999-2007 Jack Lloyd * +*************************************************/ + +#ifndef BOTAN_DER_ENCODER_H__ +#define BOTAN_DER_ENCODER_H__ + +#include <botan/asn1_int.h> +#include <vector> + +namespace Botan { + +/************************************************* +* General DER Encoding Object * +*************************************************/ +class BOTAN_DLL DER_Encoder + { + public: + SecureVector<byte> get_contents(); + + DER_Encoder& start_cons(ASN1_Tag, ASN1_Tag = UNIVERSAL); + DER_Encoder& end_cons(); + + DER_Encoder& start_explicit(u16bit); + DER_Encoder& end_explicit(); + + DER_Encoder& raw_bytes(const byte[], u32bit); + DER_Encoder& raw_bytes(const MemoryRegion<byte>&); + + DER_Encoder& encode_null(); + DER_Encoder& encode(bool); + DER_Encoder& encode(u32bit); + DER_Encoder& encode(const class BigInt&); + DER_Encoder& encode(const MemoryRegion<byte>&, ASN1_Tag); + DER_Encoder& encode(const byte[], u32bit, ASN1_Tag); + + DER_Encoder& encode(bool, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + DER_Encoder& encode(u32bit, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + DER_Encoder& encode(const class BigInt&, ASN1_Tag, + ASN1_Tag = CONTEXT_SPECIFIC); + DER_Encoder& encode(const MemoryRegion<byte>&, ASN1_Tag, + ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + DER_Encoder& encode(const byte[], u32bit, ASN1_Tag, + ASN1_Tag, ASN1_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(u32bit j = 0; j != values.size(); ++j) + encode(values[j]); + return (*this); + } + + DER_Encoder& encode(const class ASN1_Object&); + DER_Encoder& encode_if(bool, DER_Encoder&); + + DER_Encoder& add_object(ASN1_Tag, ASN1_Tag, const byte[], u32bit); + DER_Encoder& add_object(ASN1_Tag, ASN1_Tag, const MemoryRegion<byte>&); + DER_Encoder& add_object(ASN1_Tag, ASN1_Tag, const std::string&); + DER_Encoder& add_object(ASN1_Tag, ASN1_Tag, byte); + private: + class DER_Sequence + { + public: + ASN1_Tag tag_of() const; + SecureVector<byte> get_contents(); + void add_bytes(const byte[], u32bit); + DER_Sequence(ASN1_Tag, ASN1_Tag); + private: + ASN1_Tag type_tag, class_tag; + SecureVector<byte> contents; + std::vector< SecureVector<byte> > set_contents; + }; + SecureVector<byte> contents; + std::vector<DER_Sequence> subsequences; + }; + +} + +#endif diff --git a/src/asn1/modinfo.txt b/src/asn1/modinfo.txt new file mode 100644 index 000000000..03284f48b --- /dev/null +++ b/src/asn1/modinfo.txt @@ -0,0 +1,23 @@ +realname "ASN.1/BER/DER module" + +define ASN1 + +<add> +alg_id.cpp +asn1_alt.cpp +asn1_att.cpp +asn1_dn.cpp +asn1_int.cpp +asn1_ku.cpp +asn1_oid.cpp +asn1_str.cpp +asn1_tm.cpp +ber_dec.cpp +der_enc.cpp +alg_id.h +asn1_int.h +asn1_obj.h +asn1_oid.h +ber_dec.h +der_enc.h +</add> |