Added CCM and CCM_8 cipher suites.
[BoarSSL] / X500 / X500Name.cs
1 /*
2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 using System;
26 using System.Collections.Generic;
27 using System.Text;
28
29 using Asn1;
30
31 namespace X500 {
32
33 /*
34 * An X.500 name is a "distinguished name", which is an ordered sequence
35 * of RDN (relative distinguished names). Each RDN is an unordered set
36 * of name elements. A name element is an arbitrary value with an
37 * identifying OID; some (most) name element values are character
38 * strings.
39 *
40 * X.500 names are used primarily to identify certificate owner entities
41 * (subject and issuer name in a certificate), and to serve as
42 * hierarchical indexing key for values in LDAP.
43 *
44 * An X.500 name is encoded and decoded using ASN.1. They can also be
45 * converted to a string representation. The string representation does
46 * not conserve all encoding details, so encoding+parsing will not
47 * necessarily restore the exact same binary DN.
48 */
49
50 public class X500Name {
51
52 /*
53 * Get the individual name elements, in a "flattened" structure
54 * (if a SET in a RDN contains multiple values, then they are
55 * stored consecutively in that array).
56 *
57 * The returned array MUST NOT be modified.
58 */
59 public DNPart[] Parts {
60 get {
61 return Parts_;
62 }
63 }
64 DNPart[] Parts_;
65
66 /*
67 * Get the individual name elements. Each internal array contains
68 * the name elements found in the SET for a specific RDN.
69 */
70 public DNPart[][] PartsGen {
71 get {
72 return PartsGen_;
73 }
74 }
75 DNPart[][] PartsGen_;
76
77 /*
78 * Check whether this DN is empty.
79 */
80 public bool IsEmpty {
81 get {
82 return Parts_.Length == 0;
83 }
84 }
85
86 int hashCode;
87
88 /*
89 * Constructor for parsing.
90 */
91 X500Name(List<List<DNPart>> dn)
92 {
93 Init(dn);
94 }
95
96 void Init(List<List<DNPart>> dn)
97 {
98 int n = dn.Count;
99 List<DNPart> r = new List<DNPart>();
100 PartsGen_ = new DNPart[n][];
101 for (int i = 0; i < n; i ++) {
102 IDictionary<string, DNPart> dd =
103 new SortedDictionary<string, DNPart>(
104 StringComparer.Ordinal);
105 foreach (DNPart dnp in dn[i]) {
106 string nt = dnp.OID;
107 if (dd.ContainsKey(nt)) {
108 throw new AsnException(string.Format(
109 "multiple values of type {0}"
110 + " in RDN", nt));
111 }
112 dd[nt] = dnp;
113 }
114 PartsGen_[i] = new DNPart[dd.Count];
115 int j = 0;
116 foreach (DNPart p in dd.Values) {
117 PartsGen_[i][j ++] = p;
118 r.Add(p);
119 }
120 }
121 Parts_ = r.ToArray();
122
123 uint hc = 0;
124 foreach (DNPart dnp in r) {
125 hc = ((hc << 7) | (hc >> 25)) + (uint)dnp.GetHashCode();
126 }
127 hashCode = (int)hc;
128 }
129
130 /*
131 * Simplified parsing: this constructor checks that every SET
132 * in the sequence of RDN has size exactly 1, and decodes each
133 * name element as a "generic string".
134 *
135 * On decoding error, an AsnException is thrown.
136 */
137 public X500Name(AsnElt aDN) : this(aDN, true)
138 {
139 }
140
141 /*
142 * Generic parsing. If 'strictStrings' is true, then the following
143 * rules are enforced:
144 * -- Every SET in the sequence of RDN must have size 1.
145 * -- Every name element is decoded as a string (by tag).
146 *
147 * If 'strictStrings' is false, then multiple elements may appear
148 * in each SET, and values needs not be decodable as string (values
149 * with a known OID must still be decodable).
150 *
151 * This constructor checks that within a single RDN, no two
152 * attributes may have the same type.
153 *
154 * On decoding error, an AsnException is thrown.
155 */
156 public X500Name(AsnElt aDN, bool strictStrings)
157 {
158 /*
159 * Note: the SEQUENCE tag MUST be present, since the
160 * ASN.1 definition of Name starts with a CHOICE; thus,
161 * any tag override would have to be explicit, not
162 * implicit.
163 */
164 aDN.CheckConstructed();
165 aDN.CheckTag(AsnElt.SEQUENCE);
166 List<List<DNPart>> r = new List<List<DNPart>>();
167 foreach (AsnElt aRDN in aDN.Sub) {
168 aRDN.CheckConstructed();
169 aRDN.CheckTag(AsnElt.SET);
170 aRDN.CheckNumSubMin(1);
171 int n = aRDN.Sub.Length;
172 if (n != 1 && strictStrings) {
173 throw new AsnException(String.Format(
174 "several ({0}) values in RDN", n));
175 }
176 List<DNPart> r2 = new List<DNPart>();
177 r.Add(r2);
178 for (int i = 0; i < n; i ++) {
179 AsnElt aTV = aRDN.Sub[i];
180 aTV.CheckConstructed();
181 aTV.CheckTag(AsnElt.SEQUENCE);
182 aTV.CheckNumSub(2);
183 AsnElt aOID = aTV.GetSub(0);
184 aOID.CheckTag(AsnElt.OBJECT_IDENTIFIER);
185 AsnElt aVal = aTV.GetSub(1);
186 string nt = aOID.GetOID();
187 DNPart dnp = new DNPart(nt, aVal);
188 if (strictStrings && !dnp.IsString) {
189 throw new AsnException(
190 "RDN is not a string");
191 }
192 r2.Add(dnp);
193 }
194 }
195 Init(r);
196 }
197
198 /*
199 * Encode this DN into a string as specified in RFC 4514.
200 */
201 public override string ToString()
202 {
203 StringBuilder sb = new StringBuilder();
204 for (int i = PartsGen_.Length - 1; i >= 0; i --) {
205 DNPart[] dd = PartsGen_[i];
206 for (int j = 0; j < dd.Length; j ++) {
207 if (j > 0) {
208 sb.Append("+");
209 } else if (sb.Length > 0) {
210 sb.Append(",");
211 }
212 sb.Append(dd[j].ToString());
213 }
214 }
215 return sb.ToString();
216 }
217
218 /*
219 * Encode back this DN into an ASN.1 structure.
220 */
221 public AsnElt ToAsn1()
222 {
223 AsnElt[] t1 = new AsnElt[PartsGen_.Length];
224 for (int i = 0; i < PartsGen_.Length; i ++) {
225 DNPart[] dp = PartsGen_[i];
226 AsnElt[] t2 = new AsnElt[dp.Length];
227 for (int j = 0; j < dp.Length; j ++) {
228 t2[j] = AsnElt.Make(AsnElt.SEQUENCE,
229 AsnElt.MakeOID(dp[j].OID),
230 dp[j].AsnValue);
231 }
232 t1[i] = AsnElt.MakeSetOf(t2);
233 }
234 return AsnElt.Make(AsnElt.SEQUENCE, t1);
235 }
236
237 /*
238 * Parse a string into a DN. The input is expected to use
239 * RFC 4514 format. Name elements that are provided as
240 * character strings will be mapped to ASN.1 PrintableString
241 * values (if they are compatible with that string type)
242 * or UTF8String values (otherwise).
243 *
244 * On parse error, an AsnException is thrown.
245 */
246 public static X500Name Parse(string str)
247 {
248 int n = str.Length;
249 int p = 0;
250 bool acc = false;
251 List<List<DNPart>> dn = new List<List<DNPart>>();
252 while (p < n) {
253 /*
254 * Find the next unescaped '+' or ',' sign.
255 */
256 bool lcwb = false;
257 int q;
258 for (q = p; q < n; q ++) {
259 if (lcwb) {
260 lcwb = false;
261 continue;
262 }
263 switch (str[q]) {
264 case ',':
265 case '+':
266 goto found;
267 case '\\':
268 lcwb = true;
269 break;
270 }
271 }
272 found:
273
274 /*
275 * Parse DN element.
276 */
277 DNPart dnp = DNPart.Parse(str.Substring(p, q - p));
278 if (acc) {
279 dn[dn.Count - 1].Add(dnp);
280 } else {
281 List<DNPart> r = new List<DNPart>();
282 r.Add(dnp);
283 dn.Add(r);
284 }
285
286 p = q + 1;
287 acc = q < n && str[q] == '+';
288 }
289
290 dn.Reverse();
291 return new X500Name(dn);
292 }
293
294 /*
295 * Compare two DN for equality. "null" is equal to "null" but
296 * to nothing else.
297 */
298 public static bool Equals(X500Name dn1, X500Name dn2)
299 {
300 if (dn1 == null) {
301 return dn2 == null;
302 } else {
303 return dn1.Equals(dn2);
304 }
305 }
306
307 public override bool Equals(object obj)
308 {
309 return Equals(obj as X500Name);
310 }
311
312 public bool Equals(X500Name dn)
313 {
314 if (dn == null) {
315 return false;
316 }
317 int n = PartsGen.Length;
318 if (dn.PartsGen.Length != n) {
319 return false;
320 }
321 for (int i = 0; i < n; i ++) {
322 DNPart[] p1 = PartsGen[i];
323 DNPart[] p2 = dn.PartsGen[i];
324 int k = p1.Length;
325 if (k != p2.Length) {
326 return false;
327 }
328 for (int j = 0; j < k; j ++) {
329 if (!p1[j].Equals(p2[j])) {
330 return false;
331 }
332 }
333 }
334 return true;
335 }
336
337 public override int GetHashCode()
338 {
339 return hashCode;
340 }
341 }
342
343 }