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 }