1 /** 2 * Common ASN.1 Objects 3 * 4 * Copyright: 5 * (C) 1999-2007 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 2007 Yves Jerschow 8 * 9 * License: 10 * Botan is released under the Simplified BSD License (see LICENSE.md) 11 */ 12 module botan.asn1.asn1_alt_name; 13 14 import botan.constants; 15 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO): 16 17 import botan.asn1.asn1_obj; 18 import botan.asn1.asn1_str; 19 import botan.asn1.asn1_oid; 20 import botan.asn1.asn1_alt_name; 21 import botan.asn1.der_enc; 22 import botan.asn1.ber_dec; 23 import botan.asn1.oids; 24 import memutils.dictionarylist; 25 import botan.utils.charset; 26 import botan.utils.parsing; 27 import botan.utils.loadstor; 28 import botan.utils.types; 29 import memutils.hashmap; 30 31 alias AlternativeName = RefCounted!AlternativeNameImpl; 32 33 /** 34 * Alternative Name 35 */ 36 final class AlternativeNameImpl : ASN1Object 37 { 38 public: 39 /* 40 * DER encode an AlternativeName extension 41 */ 42 override void encodeInto(ref DEREncoder der) const 43 { 44 der.startCons(ASN1Tag.SEQUENCE); 45 46 encodeEntries(der, m_alt_info, "RFC822", (cast(ASN1Tag)1)); 47 encodeEntries(der, m_alt_info, "DNS", (cast(ASN1Tag)2)); 48 encodeEntries(der, m_alt_info, "URI", (cast(ASN1Tag)6)); 49 encodeEntries(der, m_alt_info, "IP", (cast(ASN1Tag)7)); 50 51 foreach(const ref OID oid, const ref ASN1String asn1_str; m_othernames) 52 { 53 der.startExplicit(0) 54 .encode(oid) 55 .startExplicit(0) 56 .encode(asn1_str) 57 .endExplicit() 58 .endExplicit(); 59 } 60 61 der.endCons(); 62 } 63 64 /* 65 * Decode a BER encoded AlternativeName 66 */ 67 override void decodeFrom(ref BERDecoder source) 68 { 69 BERDecoder names = source.startCons(ASN1Tag.SEQUENCE); 70 71 while (names.moreItems()) 72 { 73 BERObject obj = names.getNextObject(); 74 if ((obj.class_tag != ASN1Tag.CONTEXT_SPECIFIC) && 75 (obj.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED))) 76 continue; 77 78 const ASN1Tag tag = obj.type_tag; 79 80 if (tag == 0) 81 { 82 auto othername = BERDecoder(obj.value); 83 84 OID oid = OID(); 85 othername.decode(oid); 86 if (othername.moreItems()) 87 { 88 BERObject othername_value_outer = othername.getNextObject(); 89 othername.verifyEnd(); 90 91 if (othername_value_outer.type_tag != (cast(ASN1Tag) 0) || 92 othername_value_outer.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED)) 93 throw new DecodingError("Invalid tags on otherName value"); 94 95 auto othername_value_inner = BERDecoder(othername_value_outer.value); 96 97 BERObject value = othername_value_inner.getNextObject(); 98 othername_value_inner.verifyEnd(); 99 100 const ASN1Tag value_type = value.type_tag; 101 102 if (isStringType(value_type) && value.class_tag == ASN1Tag.UNIVERSAL) 103 addOthername(oid, value.toString(), value_type); 104 } 105 } 106 else if (tag == 1 || tag == 2 || tag == 6) 107 { 108 const string value = transcode(obj.toString(), 109 LATIN1_CHARSET, 110 LOCAL_CHARSET); 111 112 if (tag == 1) addAttribute("RFC822", value); 113 if (tag == 2) addAttribute("DNS", value); 114 if (tag == 6) addAttribute("URI", value); 115 } 116 else if (tag == 7) 117 { 118 if (obj.value.length == 4) 119 { 120 const uint ip = loadBigEndian!uint(obj.value.ptr, 0); 121 addAttribute("IP", ipv4ToString(ip)); 122 } 123 } 124 125 } 126 } 127 128 /* 129 * Return all of the alternative names 130 */ 131 DictionaryListRef!(string, string) contents() const 132 { 133 DictionaryListRef!(string, string) names; 134 135 foreach(const ref string k, const ref string v; m_alt_info) { 136 names.insert(k, v); 137 } 138 139 foreach(const ref OID oid, const ref ASN1String asn1_str; m_othernames) { 140 names.insert(OIDS.lookup(oid), asn1_str.value()); 141 } 142 143 return names; 144 } 145 146 /* 147 * Add an attribute to an alternative name 148 */ 149 void addAttribute(in string type, in string str) 150 { 151 if (type == "" || str == "") 152 return; 153 154 bool exists; 155 void adder(in string val) { 156 if (val == str) 157 exists = true; 158 } 159 m_alt_info.getValuesAt(type, &adder); 160 161 if (!exists) 162 m_alt_info.insert(type, str); 163 } 164 165 /* 166 * Get the attributes of this alternative name 167 */ 168 const(DictionaryListRef!(string, string)) getAttributes() const 169 { 170 return m_alt_info; 171 } 172 173 /* 174 * Add an OtherName field 175 */ 176 void addOthername(in OID oid, in string value, ASN1Tag type) 177 { 178 if (value == "") 179 return; 180 m_othernames.insert(oid, ASN1String(value, type)); 181 } 182 183 /* 184 * Get the otherNames 185 */ 186 const(DictionaryListRef!(OID, ASN1String)) getOthernames() const 187 { 188 return m_othernames; 189 } 190 191 /* 192 * Return if this object has anything useful 193 */ 194 bool hasItems() const 195 { 196 return (m_alt_info.length > 0 || m_othernames.length > 0); 197 } 198 199 /* 200 * Create an AlternativeName 201 */ 202 this(in string email_addr = "", 203 in string uri = "", 204 in string dns = "", 205 in string ip = "") 206 { 207 addAttribute("RFC822", email_addr); 208 addAttribute("DNS", dns); 209 addAttribute("URI", uri); 210 addAttribute("IP", ip); 211 } 212 213 private: 214 DictionaryListRef!(string, string) m_alt_info; 215 DictionaryListRef!(OID, ASN1String) m_othernames; 216 } 217 218 219 220 /* 221 * Check if type is a known ASN.1 string type 222 */ 223 bool isStringType(ASN1Tag tag) 224 { 225 return (tag == ASN1Tag.NUMERIC_STRING || 226 tag == ASN1Tag.PRINTABLE_STRING || 227 tag == ASN1Tag.VISIBLE_STRING || 228 tag == ASN1Tag.T61_STRING || 229 tag == ASN1Tag.IA5_STRING || 230 tag == ASN1Tag.UTF8_STRING || 231 tag == ASN1Tag.BMP_STRING); 232 } 233 234 235 /* 236 * DER encode an AlternativeName entry 237 */ 238 void encodeEntries(ref DEREncoder encoder, 239 in DictionaryListRef!(string, string) attr, 240 string type, ASN1Tag tagging) 241 { 242 void checker(in string alt_name) { 243 244 if (type == "RFC822" || type == "DNS" || type == "URI") 245 { 246 ASN1String asn1_string = ASN1String(alt_name, ASN1Tag.IA5_STRING); 247 encoder.addObject(tagging, ASN1Tag.CONTEXT_SPECIFIC, asn1_string.iso8859()); 248 } 249 else if (type == "IP") 250 { 251 const uint ip = stringToIpv4(alt_name); 252 ubyte[4] ip_buf; 253 storeBigEndian(ip, &ip_buf); 254 encoder.addObject(tagging, ASN1Tag.CONTEXT_SPECIFIC, ip_buf.ptr, 4); 255 } 256 } 257 attr.getValuesAt(type, &checker); 258 }