Initial commit.
[BoarSSL] / Asn1 / AsnElt.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.IO;
28 using System.Text;
29
30 namespace Asn1 {
31
32 /*
33 * An AsnElt instance represents a decoded ASN.1 DER object. It is
34 * immutable.
35 */
36
37 public class AsnElt {
38
39 /*
40 * Universal tag values.
41 */
42 public const int BOOLEAN = 1;
43 public const int INTEGER = 2;
44 public const int BIT_STRING = 3;
45 public const int OCTET_STRING = 4;
46 public const int NULL = 5;
47 public const int OBJECT_IDENTIFIER = 6;
48 public const int Object_Descriptor = 7;
49 public const int EXTERNAL = 8;
50 public const int REAL = 9;
51 public const int ENUMERATED = 10;
52 public const int EMBEDDED_PDV = 11;
53 public const int UTF8String = 12;
54 public const int RELATIVE_OID = 13;
55 public const int SEQUENCE = 16;
56 public const int SET = 17;
57 public const int NumericString = 18;
58 public const int PrintableString = 19;
59 public const int T61String = 20;
60 public const int TeletexString = 20;
61 public const int VideotexString = 21;
62 public const int IA5String = 22;
63 public const int UTCTime = 23;
64 public const int GeneralizedTime = 24;
65 public const int GraphicString = 25;
66 public const int VisibleString = 26;
67 public const int GeneralString = 27;
68 public const int UniversalString = 28;
69 public const int CHARACTER_STRING = 29;
70 public const int BMPString = 30;
71
72 /*
73 * Tag classes.
74 */
75 public const int UNIVERSAL = 0;
76 public const int APPLICATION = 1;
77 public const int CONTEXT = 2;
78 public const int PRIVATE = 3;
79
80 /*
81 * Internal rules
82 * ==============
83 *
84 * Instances are immutable. They reference an internal buffer
85 * that they never modify. The buffer is never shown to the
86 * outside; when decoding and creating, copies are performed
87 * where necessary.
88 *
89 * If the instance was created by decoding, then:
90 * objBuf points to the array containing the complete object
91 * objOff start offset for the object header
92 * objLen complete object length
93 * valOff offset for the first value byte
94 * valLen value length (excluding the null-tag, if applicable)
95 * hasEncodedHeader is true
96 *
97 * If the instance was created from an explicit value or from
98 * sub-elements, then:
99 * objBuf contains the value, or is null
100 * objOff is 0
101 * objLen is -1, or contains the computed object length
102 * valOff is 0
103 * valLen is -1, or contains the computed value length
104 * hasEncodedHeader is false
105 *
106 * If objBuf is null, then the object is necessarily constructed
107 * (Sub is not null). If objBuf is not null, then the encoded
108 * value is known (the object may be constructed or primitive),
109 * and valOff/valLen identify the actual value within objBuf.
110 *
111 * Tag class and value, and sub-elements, are referenced from
112 * specific properties.
113 */
114
115 byte[] objBuf;
116 int objOff;
117 int objLen;
118 int valOff;
119 int valLen;
120 bool hasEncodedHeader;
121
122 AsnElt()
123 {
124 }
125
126 /*
127 * The tag class for this element.
128 */
129 int tagClass_;
130 public int TagClass {
131 get {
132 return tagClass_;
133 }
134 private set {
135 tagClass_ = value;
136 }
137 }
138
139 /*
140 * The tag value for this element.
141 */
142 int tagValue_;
143 public int TagValue {
144 get {
145 return tagValue_;
146 }
147 private set {
148 tagValue_ = value;
149 }
150 }
151
152 /*
153 * The sub-elements. This is null if this element is primitive.
154 * DO NOT MODIFY this array.
155 */
156 AsnElt[] sub_;
157 public AsnElt[] Sub {
158 get {
159 return sub_;
160 }
161 private set {
162 sub_ = value;
163 }
164 }
165
166 /*
167 * The "constructed" flag: true for an elements with sub-elements,
168 * false for a primitive element.
169 */
170 public bool Constructed {
171 get {
172 return Sub != null;
173 }
174 }
175
176 /*
177 * The value length. When the object is BER-encoded with an
178 * indefinite length, the value length includes all the sub-objects
179 * but NOT the formal null-tag marker.
180 */
181 public int ValueLength {
182 get {
183 if (valLen < 0) {
184 if (Constructed) {
185 int vlen = 0;
186 foreach (AsnElt a in Sub) {
187 vlen += a.EncodedLength;
188 }
189 valLen = vlen;
190 } else {
191 valLen = objBuf.Length;
192 }
193 }
194 return valLen;
195 }
196 }
197
198 /*
199 * The encoded object length (complete with header).
200 */
201 public int EncodedLength {
202 get {
203 if (objLen < 0) {
204 int vlen = ValueLength;
205 objLen = TagLength(TagValue)
206 + LengthLength(vlen) + vlen;
207 }
208 return objLen;
209 }
210 }
211
212 /*
213 * Check that this element is constructed. An exception is thrown
214 * if this is not the case.
215 */
216 public void CheckConstructed()
217 {
218 if (!Constructed) {
219 throw new AsnException("not constructed");
220 }
221 }
222
223 /*
224 * Check that this element is primitive. An exception is thrown
225 * if this is not the case.
226 */
227 public void CheckPrimitive()
228 {
229 if (Constructed) {
230 throw new AsnException("not primitive");
231 }
232 }
233
234 /*
235 * Get a sub-element. This method throws appropriate exceptions
236 * if this element is not constructed, or the requested index
237 * is out of range.
238 */
239 public AsnElt GetSub(int n)
240 {
241 CheckConstructed();
242 if (n < 0 || n >= Sub.Length) {
243 throw new AsnException("no such sub-object: n=" + n);
244 }
245 return Sub[n];
246 }
247
248 /*
249 * Check that the tag is UNIVERSAL with the provided value.
250 */
251 public void CheckTag(int tv)
252 {
253 CheckTag(UNIVERSAL, tv);
254 }
255
256 /*
257 * Check that the tag has the specified class and value.
258 */
259 public void CheckTag(int tc, int tv)
260 {
261 if (TagClass != tc || TagValue != tv) {
262 throw new AsnException("unexpected tag: " + TagString);
263 }
264 }
265
266 /*
267 * Check that this element is constructed and contains exactly
268 * 'n' sub-elements.
269 */
270 public void CheckNumSub(int n)
271 {
272 CheckConstructed();
273 if (Sub.Length != n) {
274 throw new AsnException("wrong number of sub-elements: "
275 + Sub.Length + " (expected: " + n + ")");
276 }
277 }
278
279 /*
280 * Check that this element is constructed and contains at least
281 * 'n' sub-elements.
282 */
283 public void CheckNumSubMin(int n)
284 {
285 CheckConstructed();
286 if (Sub.Length < n) {
287 throw new AsnException("not enough sub-elements: "
288 + Sub.Length + " (minimum: " + n + ")");
289 }
290 }
291
292 /*
293 * Check that this element is constructed and contains no more
294 * than 'n' sub-elements.
295 */
296 public void CheckNumSubMax(int n)
297 {
298 CheckConstructed();
299 if (Sub.Length > n) {
300 throw new AsnException("too many sub-elements: "
301 + Sub.Length + " (maximum: " + n + ")");
302 }
303 }
304
305 /*
306 * Get a string representation of the tag class and value.
307 */
308 public string TagString {
309 get {
310 return TagToString(TagClass, TagValue);
311 }
312 }
313
314 static string TagToString(int tc, int tv)
315 {
316 switch (tc) {
317 case UNIVERSAL:
318 break;
319 case APPLICATION:
320 return "APPLICATION:" + tv;
321 case CONTEXT:
322 return "CONTEXT:" + tv;
323 case PRIVATE:
324 return "PRIVATE:" + tv;
325 default:
326 return String.Format("INVALID:{0}/{1}", tc, tv);
327 }
328
329 switch (tv) {
330 case BOOLEAN: return "BOOLEAN";
331 case INTEGER: return "INTEGER";
332 case BIT_STRING: return "BIT_STRING";
333 case OCTET_STRING: return "OCTET_STRING";
334 case NULL: return "NULL";
335 case OBJECT_IDENTIFIER: return "OBJECT_IDENTIFIER";
336 case Object_Descriptor: return "Object_Descriptor";
337 case EXTERNAL: return "EXTERNAL";
338 case REAL: return "REAL";
339 case ENUMERATED: return "ENUMERATED";
340 case EMBEDDED_PDV: return "EMBEDDED_PDV";
341 case UTF8String: return "UTF8String";
342 case RELATIVE_OID: return "RELATIVE_OID";
343 case SEQUENCE: return "SEQUENCE";
344 case SET: return "SET";
345 case NumericString: return "NumericString";
346 case PrintableString: return "PrintableString";
347 case TeletexString: return "TeletexString";
348 case VideotexString: return "VideotexString";
349 case IA5String: return "IA5String";
350 case UTCTime: return "UTCTime";
351 case GeneralizedTime: return "GeneralizedTime";
352 case GraphicString: return "GraphicString";
353 case VisibleString: return "VisibleString";
354 case GeneralString: return "GeneralString";
355 case UniversalString: return "UniversalString";
356 case CHARACTER_STRING: return "CHARACTER_STRING";
357 case BMPString: return "BMPString";
358 default:
359 return String.Format("UNIVERSAL:" + tv);
360 }
361 }
362
363 /*
364 * Get the encoded length for a tag.
365 */
366 static int TagLength(int tv)
367 {
368 if (tv <= 0x1F) {
369 return 1;
370 }
371 int z = 1;
372 while (tv > 0) {
373 z ++;
374 tv >>= 7;
375 }
376 return z;
377 }
378
379 /*
380 * Get the encoded length for a length.
381 */
382 static int LengthLength(int len)
383 {
384 if (len < 0x80) {
385 return 1;
386 }
387 int z = 1;
388 while (len > 0) {
389 z ++;
390 len >>= 8;
391 }
392 return z;
393 }
394
395 /*
396 * Decode an ASN.1 object. The provided buffer is internally
397 * copied. Trailing garbage is not tolerated.
398 */
399 public static AsnElt Decode(byte[] buf)
400 {
401 return Decode(buf, 0, buf.Length, true);
402 }
403
404 /*
405 * Decode an ASN.1 object. The provided buffer is internally
406 * copied. Trailing garbage is not tolerated.
407 */
408 public static AsnElt Decode(byte[] buf, int off, int len)
409 {
410 return Decode(buf, off, len, true);
411 }
412
413 /*
414 * Decode an ASN.1 object. The provided buffer is internally
415 * copied. If 'exactLength' is true, then trailing garbage is
416 * not tolerated (it triggers an exception).
417 */
418 public static AsnElt Decode(byte[] buf, bool exactLength)
419 {
420 return Decode(buf, 0, buf.Length, exactLength);
421 }
422
423 /*
424 * Decode an ASN.1 object. The provided buffer is internally
425 * copied. If 'exactLength' is true, then trailing garbage is
426 * not tolerated (it triggers an exception).
427 */
428 public static AsnElt Decode(byte[] buf, int off, int len,
429 bool exactLength)
430 {
431 int tc, tv, valOff, valLen, objLen;
432 bool cons;
433 objLen = Decode(buf, off, len,
434 out tc, out tv, out cons,
435 out valOff, out valLen);
436 if (exactLength && objLen != len) {
437 throw new AsnException("trailing garbage");
438 }
439 byte[] nbuf = new byte[objLen];
440 Array.Copy(buf, off, nbuf, 0, objLen);
441 return DecodeNoCopy(nbuf, 0, objLen);
442 }
443
444 /*
445 * Internal recursive decoder. The provided array is NOT copied.
446 * Trailing garbage is ignored (caller should use the 'objLen'
447 * field to learn the total object length).
448 */
449 static AsnElt DecodeNoCopy(byte[] buf, int off, int len)
450 {
451 int tc, tv, valOff, valLen, objLen;
452 bool cons;
453 objLen = Decode(buf, off, len,
454 out tc, out tv, out cons,
455 out valOff, out valLen);
456 AsnElt a = new AsnElt();
457 a.TagClass = tc;
458 a.TagValue = tv;
459 a.objBuf = buf;
460 a.objOff = off;
461 a.objLen = objLen;
462 a.valOff = valOff;
463 a.valLen = valLen;
464 a.hasEncodedHeader = true;
465 if (cons) {
466 List<AsnElt> subs = new List<AsnElt>();
467 off = valOff;
468 int lim = valOff + valLen;
469 while (off < lim) {
470 AsnElt b = DecodeNoCopy(buf, off, lim - off);
471 off += b.objLen;
472 subs.Add(b);
473 }
474 a.Sub = subs.ToArray();
475 } else {
476 a.Sub = null;
477 }
478 return a;
479 }
480
481 /*
482 * Decode the tag and length, and get the value offset and length.
483 * Returned value if the total object length.
484 * Note: when an object has indefinite length, the terminated
485 * "null tag" will NOT be considered part of the "value length".
486 */
487 static int Decode(byte[] buf, int off, int maxLen,
488 out int tc, out int tv, out bool cons,
489 out int valOff, out int valLen)
490 {
491 int lim = off + maxLen;
492 int orig = off;
493
494 /*
495 * Decode tag.
496 */
497 CheckOff(off, lim);
498 tv = buf[off ++];
499 cons = (tv & 0x20) != 0;
500 tc = tv >> 6;
501 tv &= 0x1F;
502 if (tv == 0x1F) {
503 tv = 0;
504 for (;;) {
505 CheckOff(off, lim);
506 int c = buf[off ++];
507 if (tv > 0xFFFFFF) {
508 throw new AsnException(
509 "tag value overflow");
510 }
511 tv = (tv << 7) | (c & 0x7F);
512 if ((c & 0x80) == 0) {
513 break;
514 }
515 }
516 }
517
518 /*
519 * Decode length.
520 */
521 CheckOff(off, lim);
522 int vlen = buf[off ++];
523 if (vlen == 0x80) {
524 /*
525 * Indefinite length. This is not strict DER, but
526 * we allow it nonetheless; we must check that
527 * the value was tagged as constructed, though.
528 */
529 vlen = -1;
530 if (!cons) {
531 throw new AsnException("indefinite length"
532 + " but not constructed");
533 }
534 } else if (vlen > 0x80) {
535 int lenlen = vlen - 0x80;
536 CheckOff(off + lenlen - 1, lim);
537 vlen = 0;
538 while (lenlen -- > 0) {
539 if (vlen > 0x7FFFFF) {
540 throw new AsnException(
541 "length overflow");
542 }
543 vlen = (vlen << 8) + buf[off ++];
544 }
545 }
546
547 /*
548 * Length was decoded, so the value starts here.
549 */
550 valOff = off;
551
552 /*
553 * If length is indefinite then we must explore sub-objects
554 * to get the value length.
555 */
556 if (vlen < 0) {
557 for (;;) {
558 int tc2, tv2, valOff2, valLen2;
559 bool cons2;
560 int slen;
561
562 slen = Decode(buf, off, lim - off,
563 out tc2, out tv2, out cons2,
564 out valOff2, out valLen2);
565 if (tc2 == 0 && tv2 == 0) {
566 if (cons2 || valLen2 != 0) {
567 throw new AsnException(
568 "invalid null tag");
569 }
570 valLen = off - valOff;
571 off += slen;
572 break;
573 } else {
574 off += slen;
575 }
576 }
577 } else {
578 if (vlen > (lim - off)) {
579 throw new AsnException("value overflow");
580 }
581 off += vlen;
582 valLen = off - valOff;
583 }
584
585 return off - orig;
586 }
587
588 static void CheckOff(int off, int lim)
589 {
590 if (off >= lim) {
591 throw new AsnException("offset overflow");
592 }
593 }
594
595 /*
596 * Get a specific byte from the value. This provided offset is
597 * relative to the value start (first value byte has offset 0).
598 */
599 public int ValueByte(int off)
600 {
601 if (off < 0) {
602 throw new AsnException("invalid value offset: " + off);
603 }
604 if (objBuf == null) {
605 int k = 0;
606 foreach (AsnElt a in Sub) {
607 int slen = a.EncodedLength;
608 if ((k + slen) > off) {
609 return a.ValueByte(off - k);
610 }
611 }
612 } else {
613 if (off < valLen) {
614 return objBuf[valOff + off];
615 }
616 }
617 throw new AsnException(String.Format(
618 "invalid value offset {0} (length = {1})",
619 off, ValueLength));
620 }
621
622 /*
623 * Encode this object into a newly allocated array.
624 */
625 public byte[] Encode()
626 {
627 byte[] r = new byte[EncodedLength];
628 Encode(r, 0);
629 return r;
630 }
631
632 /*
633 * Encode this object into the provided array. Encoded object
634 * length is returned.
635 */
636 public int Encode(byte[] dst, int off)
637 {
638 return Encode(0, Int32.MaxValue, dst, off);
639 }
640
641 /*
642 * Encode this object into the provided array. Only bytes
643 * at offset between 'start' (inclusive) and 'end' (exclusive)
644 * are actually written. The number of written bytes is returned.
645 * Offsets are relative to the object start (first tag byte).
646 */
647 int Encode(int start, int end, byte[] dst, int dstOff)
648 {
649 /*
650 * If the encoded value is already known, then we just
651 * dump it.
652 */
653 if (hasEncodedHeader) {
654 int from = objOff + Math.Max(0, start);
655 int to = objOff + Math.Min(objLen, end);
656 int len = to - from;
657 if (len > 0) {
658 Array.Copy(objBuf, from, dst, dstOff, len);
659 return len;
660 } else {
661 return 0;
662 }
663 }
664
665 int off = 0;
666
667 /*
668 * Encode tag.
669 */
670 int fb = (TagClass << 6) + (Constructed ? 0x20 : 0x00);
671 if (TagValue < 0x1F) {
672 fb |= (TagValue & 0x1F);
673 if (start <= off && off < end) {
674 dst[dstOff ++] = (byte)fb;
675 }
676 off ++;
677 } else {
678 fb |= 0x1F;
679 if (start <= off && off < end) {
680 dst[dstOff ++] = (byte)fb;
681 }
682 off ++;
683 int k = 0;
684 for (int v = TagValue; v > 0; v >>= 7, k += 7);
685 while (k > 0) {
686 k -= 7;
687 int v = (TagValue >> k) & 0x7F;
688 if (k != 0) {
689 v |= 0x80;
690 }
691 if (start <= off && off < end) {
692 dst[dstOff ++] = (byte)v;
693 }
694 off ++;
695 }
696 }
697
698 /*
699 * Encode length.
700 */
701 int vlen = ValueLength;
702 if (vlen < 0x80) {
703 if (start <= off && off < end) {
704 dst[dstOff ++] = (byte)vlen;
705 }
706 off ++;
707 } else {
708 int k = 0;
709 for (int v = vlen; v > 0; v >>= 8, k += 8);
710 if (start <= off && off < end) {
711 dst[dstOff ++] = (byte)(0x80 + (k >> 3));
712 }
713 off ++;
714 while (k > 0) {
715 k -= 8;
716 if (start <= off && off < end) {
717 dst[dstOff ++] = (byte)(vlen >> k);
718 }
719 off ++;
720 }
721 }
722
723 /*
724 * Encode value. We must adjust the start/end window to
725 * make it relative to the value.
726 */
727 EncodeValue(start - off, end - off, dst, dstOff);
728 off += vlen;
729
730 /*
731 * Compute copied length.
732 */
733 return Math.Max(0, Math.Min(off, end) - Math.Max(0, start));
734 }
735
736 /*
737 * Encode the value into the provided buffer. Only value bytes
738 * at offsets between 'start' (inclusive) and 'end' (exclusive)
739 * are written. Actual number of written bytes is returned.
740 * Offsets are relative to the start of the value.
741 */
742 int EncodeValue(int start, int end, byte[] dst, int dstOff)
743 {
744 int orig = dstOff;
745 if (objBuf == null) {
746 int k = 0;
747 foreach (AsnElt a in Sub) {
748 int slen = a.EncodedLength;
749 dstOff += a.Encode(start - k, end - k,
750 dst, dstOff);
751 k += slen;
752 }
753 } else {
754 int from = Math.Max(0, start);
755 int to = Math.Min(valLen, end);
756 int len = to - from;
757 if (len > 0) {
758 Array.Copy(objBuf, valOff + from,
759 dst, dstOff, len);
760 dstOff += len;
761 }
762 }
763 return dstOff - orig;
764 }
765
766 /*
767 * Copy a value chunk. The provided offset ('off') and length ('len')
768 * define the chunk to copy; the offset is relative to the value
769 * start (first value byte has offset 0). If the requested window
770 * exceeds the value boundaries, an exception is thrown.
771 */
772 public void CopyValueChunk(int off, int len, byte[] dst, int dstOff)
773 {
774 int vlen = ValueLength;
775 if (off < 0 || len < 0 || len > (vlen - off)) {
776 throw new AsnException(String.Format(
777 "invalid value window {0}:{1}"
778 + " (value length = {2})", off, len, vlen));
779 }
780 EncodeValue(off, off + len, dst, dstOff);
781 }
782
783 /*
784 * Copy the value into the specified array. The value length is
785 * returned.
786 */
787 public int CopyValue(byte[] dst, int off)
788 {
789 return EncodeValue(0, Int32.MaxValue, dst, off);
790 }
791
792 /*
793 * Get a copy of the value as a freshly allocated array.
794 */
795 public byte[] CopyValue()
796 {
797 byte[] r = new byte[ValueLength];
798 EncodeValue(0, r.Length, r, 0);
799 return r;
800 }
801
802 /*
803 * Get the value. This may return a shared buffer, that MUST NOT
804 * be modified.
805 */
806 byte[] GetValue(out int off, out int len)
807 {
808 if (objBuf == null) {
809 /*
810 * We can modify objBuf because CopyValue()
811 * called ValueLength, thus valLen has been
812 * filled.
813 */
814 objBuf = CopyValue();
815 off = 0;
816 len = objBuf.Length;
817 } else {
818 off = valOff;
819 len = valLen;
820 }
821 return objBuf;
822 }
823
824 /*
825 * Interpret the value as a BOOLEAN.
826 */
827 public bool GetBoolean()
828 {
829 if (Constructed) {
830 throw new AsnException(
831 "invalid BOOLEAN (constructed)");
832 }
833 int vlen = ValueLength;
834 if (vlen != 1) {
835 throw new AsnException(String.Format(
836 "invalid BOOLEAN (length = {0})", vlen));
837 }
838 return ValueByte(0) != 0;
839 }
840
841 /*
842 * Interpret the value as an INTEGER. An exception is thrown if
843 * the value does not fit in a 'long'.
844 */
845 public long GetInteger()
846 {
847 if (Constructed) {
848 throw new AsnException(
849 "invalid INTEGER (constructed)");
850 }
851 int vlen = ValueLength;
852 if (vlen == 0) {
853 throw new AsnException("invalid INTEGER (length = 0)");
854 }
855 int v = ValueByte(0);
856 long x;
857 if ((v & 0x80) != 0) {
858 x = -1;
859 for (int k = 0; k < vlen; k ++) {
860 if (x < ((-1L) << 55)) {
861 throw new AsnException(
862 "integer overflow (negative)");
863 }
864 x = (x << 8) + (long)ValueByte(k);
865 }
866 } else {
867 x = 0;
868 for (int k = 0; k < vlen; k ++) {
869 if (x >= (1L << 55)) {
870 throw new AsnException(
871 "integer overflow (positive)");
872 }
873 x = (x << 8) + (long)ValueByte(k);
874 }
875 }
876 return x;
877 }
878
879 /*
880 * Interpret the value as an INTEGER. An exception is thrown if
881 * the value is outside of the provided range.
882 */
883 public long GetInteger(long min, long max)
884 {
885 long v = GetInteger();
886 if (v < min || v > max) {
887 throw new AsnException("integer out of allowed range");
888 }
889 return v;
890 }
891
892 /*
893 * Interpret the value as an INTEGER. Return its hexadecimal
894 * representation (uppercase), preceded by a '0x' or '-0x'
895 * header, depending on the integer sign. The number of
896 * hexadecimal digits is even. Leading zeroes are returned (but
897 * one may remain, to ensure an even number of digits). If the
898 * integer has value 0, then 0x00 is returned.
899 */
900 public string GetIntegerHex()
901 {
902 if (Constructed) {
903 throw new AsnException(
904 "invalid INTEGER (constructed)");
905 }
906 int vlen = ValueLength;
907 if (vlen == 0) {
908 throw new AsnException("invalid INTEGER (length = 0)");
909 }
910 StringBuilder sb = new StringBuilder();
911 byte[] tmp = CopyValue();
912 if (tmp[0] >= 0x80) {
913 sb.Append('-');
914 int cc = 1;
915 for (int i = tmp.Length - 1; i >= 0; i --) {
916 int v = ((~tmp[i]) & 0xFF) + cc;
917 tmp[i] = (byte)v;
918 cc = v >> 8;
919 }
920 }
921 int k = 0;
922 while (k < tmp.Length && tmp[k] == 0) {
923 k ++;
924 }
925 if (k == tmp.Length) {
926 return "0x00";
927 }
928 sb.Append("0x");
929 while (k < tmp.Length) {
930 sb.AppendFormat("{0:X2}", tmp[k ++]);
931 }
932 return sb.ToString();
933 }
934
935 /*
936 * Interpret the value as an OCTET STRING. The value bytes are
937 * returned. This method supports constructed values and performs
938 * the reassembly.
939 */
940 public byte[] GetOctetString()
941 {
942 int len = GetOctetString(null, 0);
943 byte[] r = new byte[len];
944 GetOctetString(r, 0);
945 return r;
946 }
947
948 /*
949 * Interpret the value as an OCTET STRING. The value bytes are
950 * written in dst[], starting at offset 'off', and the total value
951 * length is returned. If 'dst' is null, then no byte is written
952 * anywhere, but the total length is still returned. This method
953 * supports constructed values and performs the reassembly.
954 */
955 public int GetOctetString(byte[] dst, int off)
956 {
957 if (Constructed) {
958 int orig = off;
959 foreach (AsnElt ae in Sub) {
960 ae.CheckTag(AsnElt.OCTET_STRING);
961 off += ae.GetOctetString(dst, off);
962 }
963 return off - orig;
964 }
965 if (dst != null) {
966 return CopyValue(dst, off);
967 } else {
968 return ValueLength;
969 }
970 }
971
972 /*
973 * Interpret the value as a BIT STRING. The bits are returned,
974 * with the "ignored bits" cleared.
975 */
976 public byte[] GetBitString()
977 {
978 int bitLength;
979 return GetBitString(out bitLength);
980 }
981
982 /*
983 * Interpret the value as a BIT STRING. The bits are returned,
984 * with the "ignored bits" cleared. The actual bit length is
985 * written in 'bitLength'.
986 */
987 public byte[] GetBitString(out int bitLength)
988 {
989 if (Constructed) {
990 /*
991 * TODO: support constructed BIT STRING values.
992 */
993 throw new AsnException(
994 "invalid BIT STRING (constructed)");
995 }
996 int vlen = ValueLength;
997 if (vlen == 0) {
998 throw new AsnException(
999 "invalid BIT STRING (length = 0)");
1000 }
1001 int fb = ValueByte(0);
1002 if (fb > 7 || (vlen == 1 && fb != 0)) {
1003 throw new AsnException(String.Format(
1004 "invalid BIT STRING (start = 0x{0:X2})", fb));
1005 }
1006 byte[] r = new byte[vlen - 1];
1007 CopyValueChunk(1, vlen - 1, r, 0);
1008 if (vlen > 1) {
1009 r[r.Length - 1] &= (byte)(0xFF << fb);
1010 }
1011 bitLength = (r.Length << 3) - fb;
1012 return r;
1013 }
1014
1015 /*
1016 * Interpret the value as a NULL.
1017 */
1018 public void CheckNull()
1019 {
1020 if (Constructed) {
1021 throw new AsnException(
1022 "invalid NULL (constructed)");
1023 }
1024 if (ValueLength != 0) {
1025 throw new AsnException(String.Format(
1026 "invalid NULL (length = {0})", ValueLength));
1027 }
1028 }
1029
1030 /*
1031 * Interpret the value as an OBJECT IDENTIFIER, and return it
1032 * (in decimal-dotted string format).
1033 */
1034 public string GetOID()
1035 {
1036 CheckPrimitive();
1037 if (valLen == 0) {
1038 throw new AsnException("zero-length OID");
1039 }
1040 int v = objBuf[valOff];
1041 if (v >= 120) {
1042 throw new AsnException(
1043 "invalid OID: first byte = " + v);
1044 }
1045 StringBuilder sb = new StringBuilder();
1046 sb.Append(v / 40);
1047 sb.Append('.');
1048 sb.Append(v % 40);
1049 long acc = 0;
1050 bool uv = false;
1051 for (int i = 1; i < valLen; i ++) {
1052 v = objBuf[valOff + i];
1053 if ((acc >> 56) != 0) {
1054 throw new AsnException(
1055 "invalid OID: integer overflow");
1056 }
1057 acc = (acc << 7) + (long)(v & 0x7F);
1058 if ((v & 0x80) == 0) {
1059 sb.Append('.');
1060 sb.Append(acc);
1061 acc = 0;
1062 uv = false;
1063 } else {
1064 uv = true;
1065 }
1066 }
1067 if (uv) {
1068 throw new AsnException(
1069 "invalid OID: truncated");
1070 }
1071 return sb.ToString();
1072 }
1073
1074 /*
1075 * Get the object value as a string. The string type is inferred
1076 * from the tag.
1077 */
1078 public string GetString()
1079 {
1080 if (TagClass != UNIVERSAL) {
1081 throw new AsnException(String.Format(
1082 "cannot infer string type: {0}:{1}",
1083 TagClass, TagValue));
1084 }
1085 return GetString(TagValue);
1086 }
1087
1088 /*
1089 * Get the object value as a string. The string type is provided
1090 * (universal tag value). Supported string types include
1091 * NumericString, PrintableString, IA5String, TeletexString
1092 * (interpreted as ISO-8859-1), UTF8String, BMPString and
1093 * UniversalString; the "time types" (UTCTime and GeneralizedTime)
1094 * are also supported, though, in their case, the internal
1095 * contents are not checked (they are decoded as PrintableString).
1096 */
1097 public string GetString(int type)
1098 {
1099 if (Constructed) {
1100 throw new AsnException(
1101 "invalid string (constructed)");
1102 }
1103 switch (type) {
1104 case NumericString:
1105 case PrintableString:
1106 case IA5String:
1107 case TeletexString:
1108 case UTCTime:
1109 case GeneralizedTime:
1110 return DecodeMono(objBuf, valOff, valLen, type);
1111 case UTF8String:
1112 return DecodeUTF8(objBuf, valOff, valLen);
1113 case BMPString:
1114 return DecodeUTF16(objBuf, valOff, valLen);
1115 case UniversalString:
1116 return DecodeUTF32(objBuf, valOff, valLen);
1117 default:
1118 throw new AsnException(
1119 "unsupported string type: " + type);
1120 }
1121 }
1122
1123 static string DecodeMono(byte[] buf, int off, int len, int type)
1124 {
1125 char[] tc = new char[len];
1126 for (int i = 0; i < len; i ++) {
1127 tc[i] = (char)buf[off + i];
1128 }
1129 VerifyChars(tc, type);
1130 return new string(tc);
1131 }
1132
1133 static string DecodeUTF8(byte[] buf, int off, int len)
1134 {
1135 /*
1136 * Skip BOM.
1137 */
1138 if (len >= 3 && buf[off] == 0xEF
1139 && buf[off + 1] == 0xBB && buf[off + 2] == 0xBF)
1140 {
1141 off += 3;
1142 len -= 3;
1143 }
1144 char[] tc = null;
1145 for (int k = 0; k < 2; k ++) {
1146 int tcOff = 0;
1147 for (int i = 0; i < len; i ++) {
1148 int c = buf[off + i];
1149 int e;
1150 if (c < 0x80) {
1151 e = 0;
1152 } else if (c < 0xC0) {
1153 throw BadByte(c, UTF8String);
1154 } else if (c < 0xE0) {
1155 c &= 0x1F;
1156 e = 1;
1157 } else if (c < 0xF0) {
1158 c &= 0x0F;
1159 e = 2;
1160 } else if (c < 0xF8) {
1161 c &= 0x07;
1162 e = 3;
1163 } else {
1164 throw BadByte(c, UTF8String);
1165 }
1166 while (e -- > 0) {
1167 if (++ i >= len) {
1168 throw new AsnException(
1169 "invalid UTF-8 string");
1170 }
1171 int d = buf[off + i];
1172 if (d < 0x80 || d > 0xBF) {
1173 throw BadByte(d, UTF8String);
1174 }
1175 c = (c << 6) + (d & 0x3F);
1176 }
1177 if (c > 0x10FFFF) {
1178 throw BadChar(c, UTF8String);
1179 }
1180 if (c > 0xFFFF) {
1181 c -= 0x10000;
1182 int hi = 0xD800 + (c >> 10);
1183 int lo = 0xDC00 + (c & 0x3FF);
1184 if (tc != null) {
1185 tc[tcOff] = (char)hi;
1186 tc[tcOff + 1] = (char)lo;
1187 }
1188 tcOff += 2;
1189 } else {
1190 if (tc != null) {
1191 tc[tcOff] = (char)c;
1192 }
1193 tcOff ++;
1194 }
1195 }
1196 if (tc == null) {
1197 tc = new char[tcOff];
1198 }
1199 }
1200 VerifyChars(tc, UTF8String);
1201 return new string(tc);
1202 }
1203
1204 static string DecodeUTF16(byte[] buf, int off, int len)
1205 {
1206 if ((len & 1) != 0) {
1207 throw new AsnException(
1208 "invalid UTF-16 string: length = " + len);
1209 }
1210 len >>= 1;
1211 if (len == 0) {
1212 return "";
1213 }
1214 bool be = true;
1215 int hi = buf[off];
1216 int lo = buf[off + 1];
1217 if (hi == 0xFE && lo == 0xFF) {
1218 off += 2;
1219 len --;
1220 } else if (hi == 0xFF && lo == 0xFE) {
1221 off += 2;
1222 len --;
1223 be = false;
1224 }
1225 char[] tc = new char[len];
1226 for (int i = 0; i < len; i ++) {
1227 int b0 = buf[off ++];
1228 int b1 = buf[off ++];
1229 if (be) {
1230 tc[i] = (char)((b0 << 8) + b1);
1231 } else {
1232 tc[i] = (char)((b1 << 8) + b0);
1233 }
1234 }
1235 VerifyChars(tc, BMPString);
1236 return new string(tc);
1237 }
1238
1239 static string DecodeUTF32(byte[] buf, int off, int len)
1240 {
1241 if ((len & 3) != 0) {
1242 throw new AsnException(
1243 "invalid UTF-32 string: length = " + len);
1244 }
1245 len >>= 2;
1246 if (len == 0) {
1247 return "";
1248 }
1249 bool be = true;
1250 if (buf[off] == 0x00
1251 && buf[off + 1] == 0x00
1252 && buf[off + 2] == 0xFE
1253 && buf[off + 3] == 0xFF)
1254 {
1255 off += 4;
1256 len --;
1257 } else if (buf[off] == 0xFF
1258 && buf[off + 1] == 0xFE
1259 && buf[off + 2] == 0x00
1260 && buf[off + 3] == 0x00)
1261 {
1262 off += 4;
1263 len --;
1264 be = false;
1265 }
1266
1267 char[] tc = null;
1268 for (int k = 0; k < 2; k ++) {
1269 int tcOff = 0;
1270 for (int i = 0; i < len; i ++) {
1271 uint b0 = buf[off + 0];
1272 uint b1 = buf[off + 1];
1273 uint b2 = buf[off + 2];
1274 uint b3 = buf[off + 3];
1275 uint c;
1276 if (be) {
1277 c = (b0 << 24) | (b1 << 16)
1278 | (b2 << 8) | b3;
1279 } else {
1280 c = (b3 << 24) | (b2 << 16)
1281 | (b1 << 8) | b0;
1282 }
1283 if (c > 0x10FFFF) {
1284 throw BadChar((int)c, UniversalString);
1285 }
1286 if (c > 0xFFFF) {
1287 c -= 0x10000;
1288 int hi = 0xD800 + (int)(c >> 10);
1289 int lo = 0xDC00 + (int)(c & 0x3FF);
1290 if (tc != null) {
1291 tc[tcOff] = (char)hi;
1292 tc[tcOff + 1] = (char)lo;
1293 }
1294 tcOff += 2;
1295 } else {
1296 if (tc != null) {
1297 tc[tcOff] = (char)c;
1298 }
1299 tcOff ++;
1300 }
1301 }
1302 if (tc == null) {
1303 tc = new char[tcOff];
1304 }
1305 }
1306 VerifyChars(tc, UniversalString);
1307 return new string(tc);
1308 }
1309
1310 static void VerifyChars(char[] tc, int type)
1311 {
1312 switch (type) {
1313 case NumericString:
1314 foreach (char c in tc) {
1315 if (!IsNum(c)) {
1316 throw BadChar(c, type);
1317 }
1318 }
1319 return;
1320 case PrintableString:
1321 case UTCTime:
1322 case GeneralizedTime:
1323 foreach (char c in tc) {
1324 if (!IsPrintable(c)) {
1325 throw BadChar(c, type);
1326 }
1327 }
1328 return;
1329 case IA5String:
1330 foreach (char c in tc) {
1331 if (!IsIA5(c)) {
1332 throw BadChar(c, type);
1333 }
1334 }
1335 return;
1336 case TeletexString:
1337 foreach (char c in tc) {
1338 if (!IsLatin1(c)) {
1339 throw BadChar(c, type);
1340 }
1341 }
1342 return;
1343 }
1344
1345 /*
1346 * For Unicode string types (UTF-8, BMP...).
1347 */
1348 for (int i = 0; i < tc.Length; i ++) {
1349 int c = tc[i];
1350 if (c >= 0xFDD0 && c <= 0xFDEF) {
1351 throw BadChar(c, type);
1352 }
1353 if (c == 0xFFFE || c == 0xFFFF) {
1354 throw BadChar(c, type);
1355 }
1356 if (c < 0xD800 || c > 0xDFFF) {
1357 continue;
1358 }
1359 if (c > 0xDBFF) {
1360 throw BadChar(c, type);
1361 }
1362 int hi = c & 0x3FF;
1363 if (++ i >= tc.Length) {
1364 throw BadChar(c, type);
1365 }
1366 c = tc[i];
1367 if (c < 0xDC00 || c > 0xDFFF) {
1368 throw BadChar(c, type);
1369 }
1370 int lo = c & 0x3FF;
1371 c = 0x10000 + lo + (hi << 10);
1372 if ((c & 0xFFFE) == 0xFFFE) {
1373 throw BadChar(c, type);
1374 }
1375 }
1376 }
1377
1378 static bool IsNum(int c)
1379 {
1380 return c == ' ' || (c >= '0' && c <= '9');
1381 }
1382
1383 internal static bool IsPrintable(int c)
1384 {
1385 if (c >= 'A' && c <= 'Z') {
1386 return true;
1387 }
1388 if (c >= 'a' && c <= 'z') {
1389 return true;
1390 }
1391 if (c >= '0' && c <= '9') {
1392 return true;
1393 }
1394 switch (c) {
1395 case ' ': case '(': case ')': case '+':
1396 case ',': case '-': case '.': case '/':
1397 case ':': case '=': case '?': case '\'':
1398 return true;
1399 default:
1400 return false;
1401 }
1402 }
1403
1404 static bool IsIA5(int c)
1405 {
1406 return c < 128;
1407 }
1408
1409 static bool IsLatin1(int c)
1410 {
1411 return c < 256;
1412 }
1413
1414 static AsnException BadByte(int c, int type)
1415 {
1416 return new AsnException(String.Format(
1417 "unexpected byte 0x{0:X2} in string of type {1}",
1418 c, type));
1419 }
1420
1421 static AsnException BadChar(int c, int type)
1422 {
1423 return new AsnException(String.Format(
1424 "unexpected character U+{0:X4} in string of type {1}",
1425 c, type));
1426 }
1427
1428 /*
1429 * Decode the value as a date/time. Returned object is in UTC.
1430 * Type of date is inferred from the tag value.
1431 */
1432 public DateTime GetTime()
1433 {
1434 if (TagClass != UNIVERSAL) {
1435 throw new AsnException(String.Format(
1436 "cannot infer date type: {0}:{1}",
1437 TagClass, TagValue));
1438 }
1439 return GetTime(TagValue);
1440 }
1441
1442 /*
1443 * Decode the value as a date/time. Returned object is in UTC.
1444 * The time string type is provided as parameter (UTCTime or
1445 * GeneralizedTime).
1446 */
1447 public DateTime GetTime(int type)
1448 {
1449 bool isGen = false;
1450 switch (type) {
1451 case UTCTime:
1452 break;
1453 case GeneralizedTime:
1454 isGen = true;
1455 break;
1456 default:
1457 throw new AsnException(
1458 "unsupported date type: " + type);
1459 }
1460 string s = GetString(type);
1461 string orig = s;
1462
1463 /*
1464 * UTCTime has format:
1465 * YYMMDDhhmm[ss](Z|(+|-)hhmm)
1466 *
1467 * GeneralizedTime has format:
1468 * YYYYMMDDhhmmss[.uu*][Z|(+|-)hhmm]
1469 *
1470 * Differences between the two types:
1471 * -- UTCTime encodes year over two digits; GeneralizedTime
1472 * uses four digits. UTCTime years map to 1950..2049 (00 is
1473 * 2000).
1474 * -- Seconds are optional with UTCTime, mandatory with
1475 * GeneralizedTime.
1476 * -- GeneralizedTime can have fractional seconds (optional).
1477 * -- Time zone is optional for GeneralizedTime. However,
1478 * a missing time zone means "local time" which depends on
1479 * the locality, so this is discouraged.
1480 *
1481 * Some other notes:
1482 * -- If there is a fractional second, then it must include
1483 * at least one digit. This implementation processes the
1484 * first three digits, and ignores the rest (if present).
1485 * -- Time zone offset ranges from -23:59 to +23:59.
1486 * -- The calendar computations are delegated to .NET's
1487 * DateTime (and DateTimeOffset) so this implements a
1488 * Gregorian calendar, even for dates before 1589. Year 0
1489 * is not supported.
1490 */
1491
1492 /*
1493 * Check characters.
1494 */
1495 foreach (char c in s) {
1496 if (c >= '0' && c <= '9') {
1497 continue;
1498 }
1499 if (c == '.' || c == '+' || c == '-' || c == 'Z') {
1500 continue;
1501 }
1502 throw BadTime(type, orig);
1503 }
1504
1505 bool good = true;
1506
1507 /*
1508 * Parse the time zone.
1509 */
1510 int tzHours = 0;
1511 int tzMinutes = 0;
1512 bool negZ = false;
1513 bool noTZ = false;
1514 if (s.EndsWith("Z")) {
1515 s = s.Substring(0, s.Length - 1);
1516 } else {
1517 int j = s.IndexOf('+');
1518 if (j < 0) {
1519 j = s.IndexOf('-');
1520 negZ = true;
1521 }
1522 if (j < 0) {
1523 noTZ = true;
1524 } else {
1525 string t = s.Substring(j + 1);
1526 s = s.Substring(0, j);
1527 if (t.Length != 4) {
1528 throw BadTime(type, orig);
1529 }
1530 tzHours = Dec2(t, 0, ref good);
1531 tzMinutes = Dec2(t, 2, ref good);
1532 if (tzHours < 0 || tzHours > 23
1533 || tzMinutes < 0 || tzMinutes > 59)
1534 {
1535 throw BadTime(type, orig);
1536 }
1537 }
1538 }
1539
1540 /*
1541 * Lack of time zone is allowed only for GeneralizedTime.
1542 */
1543 if (noTZ && !isGen) {
1544 throw BadTime(type, orig);
1545 }
1546
1547 /*
1548 * Parse the date elements.
1549 */
1550 if (s.Length < 4) {
1551 throw BadTime(type, orig);
1552 }
1553 int year = Dec2(s, 0, ref good);
1554 if (isGen) {
1555 year = year * 100 + Dec2(s, 2, ref good);
1556 s = s.Substring(4);
1557 } else {
1558 if (year < 50) {
1559 year += 100;
1560 }
1561 year += 1900;
1562 s = s.Substring(2);
1563 }
1564 int month = Dec2(s, 0, ref good);
1565 int day = Dec2(s, 2, ref good);
1566 int hour = Dec2(s, 4, ref good);
1567 int minute = Dec2(s, 6, ref good);
1568 int second = 0;
1569 int millisecond = 0;
1570 if (isGen) {
1571 second = Dec2(s, 8, ref good);
1572 if (s.Length >= 12 && s[10] == '.') {
1573 s = s.Substring(11);
1574 foreach (char c in s) {
1575 if (c < '0' || c > '9') {
1576 good = false;
1577 break;
1578 }
1579 }
1580 s += "0000";
1581 millisecond = 10 * Dec2(s, 0, ref good)
1582 + Dec2(s, 2, ref good) / 10;
1583 } else if (s.Length != 10) {
1584 good = false;
1585 }
1586 } else {
1587 switch (s.Length) {
1588 case 8:
1589 break;
1590 case 10:
1591 second = Dec2(s, 8, ref good);
1592 break;
1593 default:
1594 throw BadTime(type, orig);
1595 }
1596 }
1597
1598 /*
1599 * Parsing is finished; if any error occurred, then
1600 * the 'good' flag has been cleared.
1601 */
1602 if (!good) {
1603 throw BadTime(type, orig);
1604 }
1605
1606 /*
1607 * Leap seconds are not supported by .NET, so we claim
1608 * they do not occur.
1609 */
1610 if (second == 60) {
1611 second = 59;
1612 }
1613
1614 /*
1615 * .NET implementation performs all the checks (including
1616 * checks on month length depending on year, as per the
1617 * proleptic Gregorian calendar).
1618 */
1619 try {
1620 if (noTZ) {
1621 DateTime dt = new DateTime(year, month, day,
1622 hour, minute, second, millisecond,
1623 DateTimeKind.Local);
1624 return dt.ToUniversalTime();
1625 }
1626 TimeSpan tzOff = new TimeSpan(tzHours, tzMinutes, 0);
1627 if (negZ) {
1628 tzOff = tzOff.Negate();
1629 }
1630 DateTimeOffset dto = new DateTimeOffset(
1631 year, month, day, hour, minute, second,
1632 millisecond, tzOff);
1633 return dto.UtcDateTime;
1634 } catch (Exception e) {
1635 throw BadTime(type, orig, e);
1636 }
1637 }
1638
1639 static int Dec2(string s, int off, ref bool good)
1640 {
1641 if (off < 0 || off >= (s.Length - 1)) {
1642 good = false;
1643 return -1;
1644 }
1645 char c1 = s[off];
1646 char c2 = s[off + 1];
1647 if (c1 < '0' || c1 > '9' || c2 < '0' || c2 > '9') {
1648 good = false;
1649 return -1;
1650 }
1651 return 10 * (c1 - '0') + (c2 - '0');
1652 }
1653
1654 static AsnException BadTime(int type, string s)
1655 {
1656 return BadTime(type, s, null);
1657 }
1658
1659 static AsnException BadTime(int type, string s, Exception e)
1660 {
1661 string tt = (type == UTCTime) ? "UTCTime" : "GeneralizedTime";
1662 string msg = String.Format("invalid {0} string: '{1}'", tt, s);
1663 if (e == null) {
1664 return new AsnException(msg);
1665 } else {
1666 return new AsnException(msg, e);
1667 }
1668 }
1669
1670 /* =============================================================== */
1671
1672 /*
1673 * Create a new element for a primitive value. The provided buffer
1674 * is internally copied.
1675 */
1676 public static AsnElt MakePrimitive(int tagValue, byte[] val)
1677 {
1678 return MakePrimitive(UNIVERSAL, tagValue, val, 0, val.Length);
1679 }
1680
1681 /*
1682 * Create a new element for a primitive value. The provided buffer
1683 * is internally copied.
1684 */
1685 public static AsnElt MakePrimitive(int tagValue,
1686 byte[] val, int off, int len)
1687 {
1688 return MakePrimitive(UNIVERSAL, tagValue, val, off, len);
1689 }
1690
1691 /*
1692 * Create a new element for a primitive value. The provided buffer
1693 * is internally copied.
1694 */
1695 public static AsnElt MakePrimitive(
1696 int tagClass, int tagValue, byte[] val)
1697 {
1698 return MakePrimitive(tagClass, tagValue, val, 0, val.Length);
1699 }
1700
1701 /*
1702 * Create a new element for a primitive value. The provided buffer
1703 * is internally copied.
1704 */
1705 public static AsnElt MakePrimitive(int tagClass, int tagValue,
1706 byte[] val, int off, int len)
1707 {
1708 byte[] nval = new byte[len];
1709 Array.Copy(val, off, nval, 0, len);
1710 return MakePrimitiveInner(tagClass, tagValue, nval, 0, len);
1711 }
1712
1713 /*
1714 * Like MakePrimitive(), but the provided array is NOT copied.
1715 * This is for other factory methods that already allocate a
1716 * new array.
1717 */
1718 static AsnElt MakePrimitiveInner(int tagValue, byte[] val)
1719 {
1720 return MakePrimitiveInner(UNIVERSAL, tagValue,
1721 val, 0, val.Length);
1722 }
1723
1724 static AsnElt MakePrimitiveInner(int tagValue,
1725 byte[] val, int off, int len)
1726 {
1727 return MakePrimitiveInner(UNIVERSAL, tagValue, val, off, len);
1728 }
1729
1730 static AsnElt MakePrimitiveInner(int tagClass, int tagValue, byte[] val)
1731 {
1732 return MakePrimitiveInner(tagClass, tagValue,
1733 val, 0, val.Length);
1734 }
1735
1736 static AsnElt MakePrimitiveInner(int tagClass, int tagValue,
1737 byte[] val, int off, int len)
1738 {
1739 AsnElt a = new AsnElt();
1740 a.objBuf = new byte[len];
1741 Array.Copy(val, off, a.objBuf, 0, len);
1742 a.objOff = 0;
1743 a.objLen = -1;
1744 a.valOff = 0;
1745 a.valLen = len;
1746 a.hasEncodedHeader = false;
1747 if (tagClass < 0 || tagClass > 3) {
1748 throw new AsnException(
1749 "invalid tag class: " + tagClass);
1750 }
1751 if (tagValue < 0) {
1752 throw new AsnException(
1753 "invalid tag value: " + tagValue);
1754 }
1755 a.TagClass = tagClass;
1756 a.TagValue = tagValue;
1757 a.Sub = null;
1758 return a;
1759 }
1760
1761 /*
1762 * Create a new INTEGER value for the provided integer.
1763 */
1764 public static AsnElt MakeInteger(long x)
1765 {
1766 if (x >= 0) {
1767 return MakeInteger((ulong)x);
1768 }
1769 int k = 1;
1770 for (long w = x; w <= -(long)0x80; w >>= 8) {
1771 k ++;
1772 }
1773 byte[] v = new byte[k];
1774 for (long w = x; k > 0; w >>= 8) {
1775 v[-- k] = (byte)w;
1776 }
1777 return MakePrimitiveInner(INTEGER, v);
1778 }
1779
1780 /*
1781 * Create a new INTEGER value for the provided integer.
1782 */
1783 public static AsnElt MakeInteger(ulong x)
1784 {
1785 int k = 1;
1786 for (ulong w = x; w >= 0x80; w >>= 8) {
1787 k ++;
1788 }
1789 byte[] v = new byte[k];
1790 for (ulong w = x; k > 0; w >>= 8) {
1791 v[-- k] = (byte)w;
1792 }
1793 return MakePrimitiveInner(INTEGER, v);
1794 }
1795
1796 /*
1797 * Create a new INTEGER value for the provided integer. The x[]
1798 * array uses _unsigned_ big-endian encoding.
1799 */
1800 public static AsnElt MakeInteger(byte[] x)
1801 {
1802 int xLen = x.Length;
1803 int j = 0;
1804 while (j < xLen && x[j] == 0x00) {
1805 j ++;
1806 }
1807 if (j == xLen) {
1808 return MakePrimitiveInner(INTEGER, new byte[] { 0x00 });
1809 }
1810 byte[] v;
1811 if (x[j] < 0x80) {
1812 v = new byte[xLen - j];
1813 Array.Copy(x, j, v, 0, v.Length);
1814 } else {
1815 v = new byte[1 + xLen - j];
1816 Array.Copy(x, j, v, 1, v.Length - 1);
1817 }
1818 return MakePrimitiveInner(INTEGER, v);
1819 }
1820
1821 /*
1822 * Create a new INTEGER value for the provided integer. The x[]
1823 * array uses _signed_ big-endian encoding.
1824 */
1825 public static AsnElt MakeIntegerSigned(byte[] x)
1826 {
1827 int xLen = x.Length;
1828 if (xLen == 0) {
1829 throw new AsnException(
1830 "Invalid signed integer (empty)");
1831 }
1832 int j = 0;
1833 if (x[0] >= 0x80) {
1834 while (j < (xLen - 1)
1835 && x[j] == 0xFF
1836 && x[j + 1] >= 0x80)
1837 {
1838 j ++;
1839 }
1840 } else {
1841 while (j < (xLen - 1)
1842 && x[j] == 0x00
1843 && x[j + 1] < 0x80)
1844 {
1845 j ++;
1846 }
1847 }
1848 byte[] v = new byte[xLen - j];
1849 Array.Copy(x, j, v, 0, v.Length);
1850 return MakePrimitiveInner(INTEGER, v);
1851 }
1852
1853 /*
1854 * Create a BIT STRING from the provided value. The number of
1855 * "unused bits" is set to 0.
1856 */
1857 public static AsnElt MakeBitString(byte[] buf)
1858 {
1859 return MakeBitString(buf, 0, buf.Length);
1860 }
1861
1862 public static AsnElt MakeBitString(byte[] buf, int off, int len)
1863 {
1864 byte[] tmp = new byte[len + 1];
1865 Array.Copy(buf, off, tmp, 1, len);
1866 return MakePrimitiveInner(BIT_STRING, tmp);
1867 }
1868
1869 /*
1870 * Create a BIT STRING from the provided value. The number of
1871 * "unused bits" is specified.
1872 */
1873 public static AsnElt MakeBitString(int unusedBits, byte[] buf)
1874 {
1875 return MakeBitString(unusedBits, buf, 0, buf.Length);
1876 }
1877
1878 public static AsnElt MakeBitString(int unusedBits,
1879 byte[] buf, int off, int len)
1880 {
1881 if (unusedBits < 0 || unusedBits > 7
1882 || (unusedBits != 0 && len == 0))
1883 {
1884 throw new AsnException(
1885 "Invalid number of unused bits in BIT STRING: "
1886 + unusedBits);
1887 }
1888 byte[] tmp = new byte[len + 1];
1889 tmp[0] = (byte)unusedBits;
1890 Array.Copy(buf, off, tmp, 1, len);
1891 if (len > 0) {
1892 tmp[len - 1] &= (byte)(0xFF << unusedBits);
1893 }
1894 return MakePrimitiveInner(BIT_STRING, tmp);
1895 }
1896
1897 /*
1898 * Create an OCTET STRING from the provided value.
1899 */
1900 public static AsnElt MakeBlob(byte[] buf)
1901 {
1902 return MakeBlob(buf, 0, buf.Length);
1903 }
1904
1905 public static AsnElt MakeBlob(byte[] buf, int off, int len)
1906 {
1907 return MakePrimitive(OCTET_STRING, buf, off, len);
1908 }
1909
1910 /*
1911 * Create a new constructed elements, by providing the relevant
1912 * sub-elements.
1913 */
1914 public static AsnElt Make(int tagValue, params AsnElt[] subs)
1915 {
1916 return Make(UNIVERSAL, tagValue, subs);
1917 }
1918
1919 /*
1920 * Create a new constructed elements, by providing the relevant
1921 * sub-elements.
1922 */
1923 public static AsnElt Make(int tagClass, int tagValue,
1924 params AsnElt[] subs)
1925 {
1926 AsnElt a = new AsnElt();
1927 a.objBuf = null;
1928 a.objOff = 0;
1929 a.objLen = -1;
1930 a.valOff = 0;
1931 a.valLen = -1;
1932 a.hasEncodedHeader = false;
1933 if (tagClass < 0 || tagClass > 3) {
1934 throw new AsnException(
1935 "invalid tag class: " + tagClass);
1936 }
1937 if (tagValue < 0) {
1938 throw new AsnException(
1939 "invalid tag value: " + tagValue);
1940 }
1941 a.TagClass = tagClass;
1942 a.TagValue = tagValue;
1943 if (subs == null) {
1944 a.Sub = new AsnElt[0];
1945 } else {
1946 a.Sub = new AsnElt[subs.Length];
1947 Array.Copy(subs, 0, a.Sub, 0, subs.Length);
1948 }
1949 return a;
1950 }
1951
1952 /*
1953 * Create a SET OF: sub-elements are automatically sorted by
1954 * lexicographic order of their DER encodings. Identical elements
1955 * are merged.
1956 */
1957 public static AsnElt MakeSetOf(params AsnElt[] subs)
1958 {
1959 AsnElt a = new AsnElt();
1960 a.objBuf = null;
1961 a.objOff = 0;
1962 a.objLen = -1;
1963 a.valOff = 0;
1964 a.valLen = -1;
1965 a.hasEncodedHeader = false;
1966 a.TagClass = UNIVERSAL;
1967 a.TagValue = SET;
1968 if (subs == null) {
1969 a.Sub = new AsnElt[0];
1970 } else {
1971 SortedDictionary<byte[], AsnElt> d =
1972 new SortedDictionary<byte[], AsnElt>(
1973 COMPARER_LEXICOGRAPHIC);
1974 foreach (AsnElt ax in subs) {
1975 d[ax.Encode()] = ax;
1976 }
1977 AsnElt[] tmp = new AsnElt[d.Count];
1978 int j = 0;
1979 foreach (AsnElt ax in d.Values) {
1980 tmp[j ++] = ax;
1981 }
1982 a.Sub = tmp;
1983 }
1984 return a;
1985 }
1986
1987 static IComparer<byte[]> COMPARER_LEXICOGRAPHIC =
1988 new ComparerLexicographic();
1989
1990 class ComparerLexicographic : IComparer<byte[]> {
1991
1992 public int Compare(byte[] x, byte[] y)
1993 {
1994 int xLen = x.Length;
1995 int yLen = y.Length;
1996 int cLen = Math.Min(xLen, yLen);
1997 for (int i = 0; i < cLen; i ++) {
1998 if (x[i] != y[i]) {
1999 return (int)x[i] - (int)y[i];
2000 }
2001 }
2002 return xLen - yLen;
2003 }
2004 }
2005
2006 /*
2007 * Wrap an element into an explicit tag.
2008 */
2009 public static AsnElt MakeExplicit(int tagClass, int tagValue, AsnElt x)
2010 {
2011 return Make(tagClass, tagValue, x);
2012 }
2013
2014 /*
2015 * Wrap an element into an explicit CONTEXT tag.
2016 */
2017 public static AsnElt MakeExplicit(int tagValue, AsnElt x)
2018 {
2019 return Make(CONTEXT, tagValue, x);
2020 }
2021
2022 /*
2023 * Apply an implicit tag to a value. The source AsnElt object
2024 * is unmodified; a new object is returned.
2025 */
2026 public static AsnElt MakeImplicit(int tagClass, int tagValue, AsnElt x)
2027 {
2028 if (x.Constructed) {
2029 return Make(tagClass, tagValue, x.Sub);
2030 }
2031 AsnElt a = new AsnElt();
2032 a.objBuf = x.GetValue(out a.valOff, out a.valLen);
2033 a.objOff = 0;
2034 a.objLen = -1;
2035 a.hasEncodedHeader = false;
2036 a.TagClass = tagClass;
2037 a.TagValue = tagValue;
2038 a.Sub = null;
2039 return a;
2040 }
2041
2042 public static AsnElt NULL_V = AsnElt.MakePrimitive(
2043 NULL, new byte[0]);
2044
2045 public static AsnElt BOOL_TRUE = AsnElt.MakePrimitive(
2046 BOOLEAN, new byte[] { 0xFF });
2047 public static AsnElt BOOL_FALSE = AsnElt.MakePrimitive(
2048 BOOLEAN, new byte[] { 0x00 });
2049
2050 /*
2051 * Create an OBJECT IDENTIFIER from its string representation.
2052 * This function tolerates extra leading zeros.
2053 */
2054 public static AsnElt MakeOID(string str)
2055 {
2056 List<long> r = new List<long>();
2057 int n = str.Length;
2058 long x = -1;
2059 for (int i = 0; i < n; i ++) {
2060 int c = str[i];
2061 if (c == '.') {
2062 if (x < 0) {
2063 throw new AsnException(
2064 "invalid OID (empty element)");
2065 }
2066 r.Add(x);
2067 x = -1;
2068 continue;
2069 }
2070 if (c < '0' || c > '9') {
2071 throw new AsnException(String.Format(
2072 "invalid character U+{0:X4} in OID",
2073 c));
2074 }
2075 if (x < 0) {
2076 x = 0;
2077 } else if (x > ((Int64.MaxValue - 9) / 10)) {
2078 throw new AsnException("OID element overflow");
2079 }
2080 x = x * (long)10 + (long)(c - '0');
2081 }
2082 if (x < 0) {
2083 throw new AsnException(
2084 "invalid OID (empty element)");
2085 }
2086 r.Add(x);
2087 if (r.Count < 2) {
2088 throw new AsnException(
2089 "invalid OID (not enough elements)");
2090 }
2091 if (r[0] > 2 || r[1] > 40) {
2092 throw new AsnException(
2093 "invalid OID (first elements out of range)");
2094 }
2095
2096 MemoryStream ms = new MemoryStream();
2097 ms.WriteByte((byte)(40 * (int)r[0] + (int)r[1]));
2098 for (int i = 2; i < r.Count; i ++) {
2099 long v = r[i];
2100 if (v < 0x80) {
2101 ms.WriteByte((byte)v);
2102 continue;
2103 }
2104 int k = -7;
2105 for (long w = v; w != 0; w >>= 7, k += 7);
2106 ms.WriteByte((byte)(0x80 + (int)(v >> k)));
2107 for (k -= 7; k >= 0; k -= 7) {
2108 int z = (int)(v >> k) & 0x7F;
2109 if (k > 0) {
2110 z |= 0x80;
2111 }
2112 ms.WriteByte((byte)z);
2113 }
2114 }
2115 byte[] buf = ms.ToArray();
2116 return MakePrimitiveInner(OBJECT_IDENTIFIER,
2117 buf, 0, buf.Length);
2118 }
2119
2120 /*
2121 * Create a string of the provided type and contents. The string
2122 * type is a universal tag value for one of the string or time
2123 * types.
2124 */
2125 public static AsnElt MakeString(int type, string str)
2126 {
2127 VerifyChars(str.ToCharArray(), type);
2128 byte[] buf;
2129 switch (type) {
2130 case NumericString:
2131 case PrintableString:
2132 case UTCTime:
2133 case GeneralizedTime:
2134 case IA5String:
2135 case TeletexString:
2136 buf = EncodeMono(str);
2137 break;
2138 case UTF8String:
2139 buf = EncodeUTF8(str);
2140 break;
2141 case BMPString:
2142 buf = EncodeUTF16(str);
2143 break;
2144 case UniversalString:
2145 buf = EncodeUTF32(str);
2146 break;
2147 default:
2148 throw new AsnException(
2149 "unsupported string type: " + type);
2150 }
2151 return MakePrimitiveInner(type, buf);
2152 }
2153
2154 static byte[] EncodeMono(string str)
2155 {
2156 byte[] r = new byte[str.Length];
2157 int k = 0;
2158 foreach (char c in str) {
2159 r[k ++] = (byte)c;
2160 }
2161 return r;
2162 }
2163
2164 /*
2165 * Get the code point at offset 'off' in the string. Either one
2166 * or two 'char' slots are used; 'off' is updated accordingly.
2167 */
2168 static int CodePoint(string str, ref int off)
2169 {
2170 int c = str[off ++];
2171 if (c >= 0xD800 && c < 0xDC00 && off < str.Length) {
2172 int d = str[off];
2173 if (d >= 0xDC00 && d < 0xE000) {
2174 c = ((c & 0x3FF) << 10)
2175 + (d & 0x3FF) + 0x10000;
2176 off ++;
2177 }
2178 }
2179 return c;
2180 }
2181
2182 static byte[] EncodeUTF8(string str)
2183 {
2184 int k = 0;
2185 int n = str.Length;
2186 MemoryStream ms = new MemoryStream();
2187 while (k < n) {
2188 int cp = CodePoint(str, ref k);
2189 if (cp < 0x80) {
2190 ms.WriteByte((byte)cp);
2191 } else if (cp < 0x800) {
2192 ms.WriteByte((byte)(0xC0 + (cp >> 6)));
2193 ms.WriteByte((byte)(0x80 + (cp & 63)));
2194 } else if (cp < 0x10000) {
2195 ms.WriteByte((byte)(0xE0 + (cp >> 12)));
2196 ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63)));
2197 ms.WriteByte((byte)(0x80 + (cp & 63)));
2198 } else {
2199 ms.WriteByte((byte)(0xF0 + (cp >> 18)));
2200 ms.WriteByte((byte)(0x80 + ((cp >> 12) & 63)));
2201 ms.WriteByte((byte)(0x80 + ((cp >> 6) & 63)));
2202 ms.WriteByte((byte)(0x80 + (cp & 63)));
2203 }
2204 }
2205 return ms.ToArray();
2206 }
2207
2208 static byte[] EncodeUTF16(string str)
2209 {
2210 byte[] buf = new byte[str.Length << 1];
2211 int k = 0;
2212 foreach (char c in str) {
2213 buf[k ++] = (byte)(c >> 8);
2214 buf[k ++] = (byte)c;
2215 }
2216 return buf;
2217 }
2218
2219 static byte[] EncodeUTF32(string str)
2220 {
2221 int k = 0;
2222 int n = str.Length;
2223 MemoryStream ms = new MemoryStream();
2224 while (k < n) {
2225 int cp = CodePoint(str, ref k);
2226 ms.WriteByte((byte)(cp >> 24));
2227 ms.WriteByte((byte)(cp >> 16));
2228 ms.WriteByte((byte)(cp >> 8));
2229 ms.WriteByte((byte)cp);
2230 }
2231 return ms.ToArray();
2232 }
2233
2234 /*
2235 * Create a time value of the specified type (UTCTime or
2236 * GeneralizedTime).
2237 */
2238 public static AsnElt MakeTime(int type, DateTime dt)
2239 {
2240 dt = dt.ToUniversalTime();
2241 string str;
2242 switch (type) {
2243 case UTCTime:
2244 int year = dt.Year;
2245 if (year < 1950 || year >= 2050) {
2246 throw new AsnException(String.Format(
2247 "cannot encode year {0} as UTCTime",
2248 year));
2249 }
2250 year = year % 100;
2251 str = String.Format(
2252 "{0:d2}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}Z",
2253 year, dt.Month, dt.Day,
2254 dt.Hour, dt.Minute, dt.Second);
2255 break;
2256 case GeneralizedTime:
2257 str = String.Format(
2258 "{0:d4}{1:d2}{2:d2}{3:d2}{4:d2}{5:d2}",
2259 dt.Year, dt.Month, dt.Day,
2260 dt.Hour, dt.Minute, dt.Second);
2261 int millis = dt.Millisecond;
2262 if (millis != 0) {
2263 if (millis % 100 == 0) {
2264 str = String.Format("{0}.{1:d1}",
2265 str, millis / 100);
2266 } else if (millis % 10 == 0) {
2267 str = String.Format("{0}.{1:d2}",
2268 str, millis / 10);
2269 } else {
2270 str = String.Format("{0}.{1:d3}",
2271 str, millis);
2272 }
2273 }
2274 str = str + "Z";
2275 break;
2276 default:
2277 throw new AsnException(
2278 "unsupported time type: " + type);
2279 }
2280 return MakeString(type, str);
2281 }
2282
2283 /*
2284 * Create a time value of the specified type (UTCTime or
2285 * GeneralizedTime).
2286 */
2287 public static AsnElt MakeTime(int type, DateTimeOffset dto)
2288 {
2289 return MakeTime(type, dto.UtcDateTime);
2290 }
2291
2292 /*
2293 * Create a time value with an automatic type selection
2294 * (UTCTime if year is in the 1950..2049 range, GeneralizedTime
2295 * otherwise).
2296 */
2297 public static AsnElt MakeTimeAuto(DateTime dt)
2298 {
2299 dt = dt.ToUniversalTime();
2300 return MakeTime((dt.Year >= 1950 && dt.Year <= 2049)
2301 ? UTCTime : GeneralizedTime, dt);
2302 }
2303
2304 /*
2305 * Create a time value with an automatic type selection
2306 * (UTCTime if year is in the 1950..2049 range, GeneralizedTime
2307 * otherwise).
2308 */
2309 public static AsnElt MakeTimeAuto(DateTimeOffset dto)
2310 {
2311 return MakeTimeAuto(dto.UtcDateTime);
2312 }
2313 }
2314
2315 }