1 /**
2     Canonical Encoding Rules (CER) is a standard for encoding ASN.1 data.
3     CER is often used for cryptgraphically-signed data, such as X.509
4     certificates, because CER's defining feature is that there is only one way
5     to encode each data type, which means that two encodings of the same data
6     could not have different cryptographic signatures. For this reason, CER
7     is generally regarded as the most secure encoding standard for ASN.1.
8     Like Basic Encoding Rules (BER), Canonical Encoding Rules (CER), and
9     Packed Encoding Rules (PER), Canonical Encoding Rules (CER) is a
10     specification created by the
11     $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union),
12     and specified in $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules)
13 
14     Authors:
15     $(UL
16         $(LI $(PERSON Jonathan M. Wilbur, jonathan@wilbur.space, http://jonathan.wilbur.space))
17     )
18     Copyright: Copyright (C) Jonathan M. Wilbur
19     License: $(LINK https://mit-license.org/, MIT License)
20     Standards:
21     $(UL
22         $(LI $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1)))
23         $(LI $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules))
24     )
25     See_Also:
26     $(UL
27         $(LI $(LINK https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One, The Wikipedia Page on ASN.1))
28         $(LI $(LINK https://en.wikipedia.org/wiki/X.690, The Wikipedia Page on X.690))
29         $(LI $(LINK https://www.strozhevsky.com/free_docs/asn1_in_simple_words.pdf, ASN.1 By Simple Words))
30         $(LI $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems))
31     )
32 */
33 module asn1.codecs.cer;
34 public import asn1.codec;
35 public import asn1.interfaces : Byteable;
36 public import asn1.types.identification;
37 
38 ///
39 public alias cerOID = canonicalEncodingRulesObjectIdentifier;
40 ///
41 public alias cerObjectID = canonicalEncodingRulesObjectIdentifier;
42 ///
43 public alias cerObjectIdentifier = canonicalEncodingRulesObjectIdentifier;
44 /**
45     The object identifier assigned to the Canonical Encoding Rules (CER), per the
46     $(LINK https://www.itu.int/en/pages/default.aspx,
47         International Telecommunications Union)'s,
48     $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules)
49 
50     $(MONO {joint-iso-itu-t asn1(1) ber-derived(2) canonical-encoding(0)} )
51 */
52 public immutable OID canonicalEncodingRulesObjectIdentifier = cast(immutable(OID)) new OID(2, 1, 2, 0);
53 
54 ///
55 public alias CERElement = CanonicalEncodingRulesElement;
56 /**
57     The unit of encoding and decoding for Canonical Encoding Rules (CER).
58 
59     There are three parts to an element encoded according to the Canonical
60     Encoding Rules (CER):
61 
62     $(UL
63         $(LI A Type Tag, which specifies what data type is encoded)
64         $(LI A Length Tag, which specifies how many subsequent bytes encode the data)
65         $(LI The Encoded Value)
66     )
67 
68     They appear in the binary encoding in that order, and as such, the encoding
69     scheme is sometimes described as "TLV," which stands for Type-Length-Value.
70 
71     This class provides a properties for getting and setting bit fields of
72     the type tag, but most of it is functionality for encoding data per
73     the specification.
74 
75     As an example, this is what encoding a simple INTEGER looks like:
76 
77     ---
78     CERElement cv = new CERElement();
79     cv.tagNumber = 0x02u; // "2" means this is an INTEGER
80     cv.integer = 1433; // Now the data is encoded.
81     transmit(cast(ubyte[]) cv); // transmit() is a made-up function.
82     ---
83 
84     And this is what decoding looks like:
85 
86     ---
87     ubyte[] data = receive(); // receive() is a made-up function.
88     CERElement cv2 = new CERElement(data);
89 
90     long x;
91     if (cv.tagNumber == 0x02u) // it is an INTEGER
92     {
93         x = cv.integer;
94     }
95     // Now x is 1433!
96     ---
97 */
98 /* FIXME:
99     This class should be "final," but a bug in the DMD compiler produces
100     unlinkable objects if a final class inherits an alias to an internal
101     member of a parent class.
102 
103     I have reported this to the D Language Foundation's Bugzilla site on
104     17 October, 2017, and this bug can be viewed here:
105     https://issues.dlang.org/show_bug.cgi?id=17909
106 */
107 public
108 class CanonicalEncodingRulesElement : ASN1Element!CERElement, Byteable
109 {
110     @system
111     unittest
112     {
113         writeln("Running unit tests for codec: " ~ typeof(this).stringof);
114     }
115 
116     /**
117         "Decodes" an $(MONO END OF CONTENT), by which I mean: returns nothing, but
118         throws exceptions if the element is not correct.
119 
120         Throws:
121         $(UL
122             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
123             $(LI $(D ASN1ValueSizeException) if there are any content octets)
124         )
125     */
126     override public @property @safe
127     void endOfContent() const
128     {
129         if (this.construction != ASN1Construction.primitive)
130             throw new ASN1ConstructionException
131             (this.construction, "decode an END OF CONTENT");
132 
133         if (this.value.length != 0u)
134             throw new ASN1ValueSizeException
135             (0u, 0u, this.value.length, "decode an END OF CONTENT");
136     }
137 
138     /**
139         Decodes a $(D bool).
140 
141         A $(D 0xFF) byte will be interpreted as $(MONO TRUE). A zero byte
142         ($(D 0x00)) will be interpreted as $(MONO FALSE). Any other value,
143         or any length other than 1 byte will throw an exception.
144 
145         Throws:
146         $(UL
147             $(LI $(D ASN1ConstructionException)
148                 if the encoded value is not primitively-constructed)
149             $(LI $(D ASN1ValueSizeException)
150                 if the encoded value is not exactly 1 byte in size)
151             $(LI $(D ASN1ValueException)
152                 if the encoded value is not either 0xFF or 0x00)
153         )
154     */
155     override public @property @safe
156     bool boolean() const
157     {
158         if (this.construction != ASN1Construction.primitive)
159             throw new ASN1ConstructionException
160             (this.construction, "decode a BOOLEAN");
161 
162         if (this.value.length != 1u)
163             throw new ASN1ValueSizeException
164             (1u, 1u, this.value.length, "decode a BOOLEAN");
165 
166         if (this.value[0] == 0xFFu)
167         {
168             return true;
169         }
170         else if (this.value[0] == 0x00u)
171         {
172             return false;
173         }
174         else
175         {
176             throw new ASN1ValueException
177             (
178                 "This exception was thrown because you attempted to decode a BOOLEAN " ~
179                 "that was encoded on a byte that was not 0xFF or 0x00 using the CER " ~
180                 "codec. Any encoding of a boolean other than 0xFF (true) or 0x00 " ~
181                 "(false) is restricted by the CER codec. " ~ notWhatYouMeantText ~
182                 forMoreInformationText ~ debugInformationText ~ reportBugsText
183             );
184         }
185     }
186 
187     /// Encodes a $(D bool)
188     override public @property @safe nothrow
189     void boolean(in bool value)
190     out
191     {
192         assert(this.value.length > 0u);
193     }
194     do
195     {
196         scope(success) this.construction = ASN1Construction.primitive;
197         this.value = [(value ? 0xFFu : 0x00u)];
198     }
199 
200     @safe
201     unittest
202     {
203         CERElement cv = new CERElement();
204         cv.value = [ 0xFFu ];
205         assert(cv.boolean == true);
206         cv.value = [ 0x00u ];
207         assert(cv.boolean == false);
208         cv.value = [ 0x01u, 0x00u ];
209         assertThrown!ASN1ValueSizeException(cv.boolean);
210         cv.value = [];
211         assertThrown!ASN1ValueSizeException(cv.boolean);
212         cv.value = [ 0x01u ];
213         assertThrown!ASN1ValueException(cv.boolean);
214     }
215 
216     /**
217         Decodes a signed integer.
218 
219         Throws:
220         $(UL
221             $(LI $(D ASN1ConstructionException)
222                 if the encoded value is not primitively-constructed)
223             $(LI $(D ASN1ValueSizeException)
224                 if the value is too big to decode to a signed integral type,
225                 or if the value is zero bytes)
226             $(LI $(D ASN1ValuePaddingException)
227                 if there are padding bytes)
228         )
229     */
230     public @property @system
231     T integer(T)() const
232     if ((isIntegral!T && isSigned!T) || is(T == BigInt))
233     {
234         if (this.construction != ASN1Construction.primitive)
235             throw new ASN1ConstructionException
236             (this.construction, "decode an INTEGER");
237 
238         if (this.value.length == 1u)
239         {
240             static if (is(T == BigInt))
241                 return BigInt(cast(byte) this.value[0]);
242             else
243                 return cast(T) cast(byte) this.value[0];
244         }
245 
246         if (this.value.length == 0u)
247             throw new ASN1ValueSizeException
248             (1u, long.sizeof, this.value.length, "decode an INTEGER");
249 
250         static if (!is(T == BigInt))
251         {
252             if (this.value.length > T.sizeof)
253                 throw new ASN1ValueSizeException
254                 (1u, long.sizeof, this.value.length, "decode an INTEGER");
255         }
256 
257         if
258         (
259             (this.value[0] == 0x00u && (!(this.value[1] & 0x80u))) || // Unnecessary positive leading bytes
260             (this.value[0] == 0xFFu && (this.value[1] & 0x80u)) // Unnecessary negative leading bytes
261         )
262             throw new ASN1ValuePaddingException
263             (
264                 "This exception was thrown because you attempted to decode " ~
265                 "an INTEGER that was encoded on more than the minimum " ~
266                 "necessary bytes. " ~
267                 notWhatYouMeantText ~ forMoreInformationText ~
268                 debugInformationText ~ reportBugsText
269             );
270 
271         static if (is(T == BigInt))
272         {
273             /*
274                 The most significant bit of the most significant byte
275                 determines the sign of the resulting INTEGER. So we have to
276                 treat the first byte specially. After that, all remaining
277                 bytes are just added after the BigInt has been shifted by
278                 a byte.
279             */
280             BigInt ret = BigInt(0);
281             ret += cast(byte) (this.value[0] & 0x80u);
282             ret += (this.value[0] & cast(ubyte) 0x7Fu);
283             foreach (immutable ubyte b; this.value[1 .. $])
284             {
285                 ret <<= 8;
286                 ret += b;
287             }
288             return ret;
289         }
290         else
291         {
292             /* NOTE:
293                 Because the INTEGER is stored in two's complement form, you
294                 can't just apppend 0x00u to the big end of it until it is as long
295                 as T in bytes, then cast to T. Instead, you have to first determine
296                 if the encoded integer is negative or positive. If it is negative,
297                 then you actually want to append 0xFFu to the big end until it is
298                 as big as T, so you get the two's complement form of whatever T
299                 you choose.
300 
301                 The line immediately below this determines whether the padding byte
302                 should be 0xFF or 0x00 based on the most significant bit of the
303                 most significant byte (which, since CER encodes big-endian, will
304                 always be the first byte). If set (1), the number is negative, and
305                 hence, the padding byte should be 0xFF. If not, it is positive,
306                 and the padding byte should be 0x00.
307             */
308             immutable ubyte paddingByte = ((this.value[0] & 0x80u) ? 0xFFu : 0x00u);
309             ubyte[] value = this.value.dup; // Duplication is necessary to prevent modifying the source bytes
310             while (value.length < T.sizeof)
311                 value = (paddingByte ~ value);
312             version (LittleEndian) reverse(value);
313             version (unittest) assert(value.length == T.sizeof);
314             return *cast(T *) value.ptr;
315         }
316     }
317 
318     /// Encodes a signed integral type
319     public @property @system // nothrow (Can't be nothrow because of BigInt.opCast)
320     void integer(T)(in T value)
321     if ((isIntegral!T && isSigned!T) || is(T == BigInt))
322     out
323     {
324         assert(this.value.length > 0u);
325     }
326     do
327     {
328         scope(success) this.construction = ASN1Construction.primitive;
329         if (value <= byte.max && value >= byte.min)
330         {
331             this.value = [ cast(ubyte) cast(byte) value ];
332             return;
333         }
334 
335         ubyte[] ub;
336         static if (is(T == BigInt))
337         {
338             if (value.uintLength > (size_t.max >> 5u))
339                 throw new ASN1ValueSizeException
340                 (1u, (size_t.max >> 2u), (value.uintLength << 2u), "encode an INTEGER from a BigInt");
341 
342             // "+ 1u" because we are going to set the last byte to 00 or FF to indicate sign
343             ub.length = ((value.uintLength * uint.sizeof) + 1u);
344 
345             /*
346                 Since there is no easy way to obtain the bytes of the BigInt,
347                 we have to incrementally shift, mask, and cast to bytes, one
348                 uint at a time. This loop shifts the supplied BigInt by 32 more
349                 bits with each iteration, then masks with FFFFFFFF. The resulting
350                 value can then be cast to a uint without overflow, which can then
351                 be written to the appropriate "slot" in the pre-allocated array.
352             */
353             for (size_t i = 0u; i < value.uintLength; i++)
354             {
355                 *cast(uint *) &ub[i << 2u] = cast(uint) ((value >> (i << 5u)) & uint.max);
356             }
357 
358             /*
359                 When the BigInt is converted to bytes, unlike a signed integer,
360                 it might not have the most significant bit set to correctly
361                 indicate its sign, because the bytes of the BigInt are obtained
362                 from the internal array of *uints* in the BigInt. So here, we
363                 append a 00 or FF byte to correct this.
364             */
365             if (value >= 0)
366                 ub[$-1] = 0x00u;
367             else
368                 ub[$-1] = 0xFFu;
369         }
370         else // it is a native integral type
371         {
372             ub.length = T.sizeof;
373             *cast(T *)&ub[0] = value;
374         }
375         version (LittleEndian) reverse(ub);
376 
377         /*
378             An INTEGER must be encoded on the fewest number of bytes than can
379             encode it. The loops below identify how many bytes can be
380             truncated from the start of the INTEGER, with one loop for positive
381             and another loop for negative numbers.
382 
383             From X.690, Section 8.3.2:
384 
385             If the contents octets of an integer value encoding consist of more
386             than one octet, then the bits of the first octet and bit 8 of the
387             second octet:
388                 a) shall not all be ones; and
389                 b) shall not all be zero.
390                 NOTE – These rules ensure that an integer value is always
391                 encoded in the smallest possible number of octets.
392         */
393         size_t startOfNonPadding = 0u;
394         static if (T.sizeof > 1u)
395         {
396             if (value >= 0)
397             {
398                 for (size_t i = 0u; i < ub.length-1; i++)
399                 {
400                     if (ub[i] != 0x00u) break;
401                     if (!(ub[i+1] & 0x80u)) startOfNonPadding++;
402                 }
403             }
404             else
405             {
406                 for (size_t i = 0u; i < ub.length-1; i++)
407                 {
408                     if (ub[i] != 0xFFu) break;
409                     if (ub[i+1] & 0x80u) startOfNonPadding++;
410                 }
411             }
412         }
413         this.value = ub[startOfNonPadding .. $];
414     }
415 
416     @system
417     unittest
418     {
419         CERElement el = new CERElement();
420         BigInt b1 = BigInt("18446744073709551619"); // ulong.max + 4
421         BigInt b2 = BigInt(uint.max);
422         BigInt b3 = BigInt(0);
423         BigInt b4 = BigInt(3);
424         BigInt b5 = BigInt(-3);
425 
426         el.integer = b1;
427         assert(el.value == [
428             0x01u, 0x00u, 0x00u, 0x00u,
429             0x00u, 0x00u, 0x00u, 0x00u,
430             0x03u
431         ]);
432 
433         el.integer = b2;
434         assert(el.value == [
435             0x00u, 0xFFu, 0xFFu, 0xFFu,
436             0xFFu
437         ]);
438 
439         el.integer = b3;
440         assert(el.value == [
441             0x00u
442         ]);
443 
444         el.integer = b4;
445         assert(el.value == [
446             0x03u
447         ]);
448 
449         el.integer = b5;
450         assert(el.value == [
451             0xFDu
452         ]);
453     }
454 
455     // Ensure that INTEGER 0 gets encoded on a single null byte.
456     @system
457     unittest
458     {
459         CERElement el = new CERElement();
460 
461         el.integer!byte = cast(byte) 0x00;
462         assert(el.value == [ 0x00u ]);
463 
464         el.integer!short = cast(short) 0x0000;
465         assert(el.value == [ 0x00u ]);
466 
467         el.integer!int = cast(int) 0;
468         assert(el.value == [ 0x00u ]);
469 
470         el.integer!long = cast(long) 0;
471         assert(el.value == [ 0x00u ]);
472 
473         el.value = [];
474         assertThrown!ASN1ValueSizeException(el.integer!byte);
475         assertThrown!ASN1ValueSizeException(el.integer!short);
476         assertThrown!ASN1ValueSizeException(el.integer!int);
477         assertThrown!ASN1ValueSizeException(el.integer!long);
478     }
479 
480     // Test encoding -0 for the sake of CVE-2016-2108
481     @system
482     unittest
483     {
484         CERElement el = new CERElement();
485 
486         el.integer!byte = -0;
487         assertNotThrown!RangeError(el.integer!byte);
488         assertNotThrown!ASN1Exception(el.integer!byte);
489 
490         el.integer!short = -0;
491         assertNotThrown!RangeError(el.integer!short);
492         assertNotThrown!ASN1Exception(el.integer!short);
493 
494         el.integer!int = -0;
495         assertNotThrown!RangeError(el.integer!int);
496         assertNotThrown!ASN1Exception(el.integer!int);
497 
498         el.integer!long = -0;
499         assertNotThrown!RangeError(el.integer!long);
500         assertNotThrown!ASN1Exception(el.integer!long);
501     }
502 
503     /**
504         Decodes an array of $(D bool)s representing a string of bits.
505 
506         Returns: an array of $(D bool)s, where each $(D bool) represents a bit
507             in the encoded bit string
508 
509         Throws:
510         $(UL
511             $(LI $(D ASN1ValueSizeException)
512                 if the any primitive contains 0 bytes)
513             $(LI $(D ASN1ValueException)
514                 if the first byte has a value greater
515                 than seven, or if the first byte indicates the presence of
516                 padding bits when no subsequent bytes exist, or if any primitive
517                 but the last in a constructed BIT STRING uses padding bits, or
518                 if any of the padding bits are set)
519             $(LI $(D ASN1RecursionException)
520                 if using constructed form and the element
521                 is constructed of too many nested constructed elements)
522             $(LI $(D ASN1TagClassException)
523                 if any nested primitives do not share the
524                 same tag class as their outer constructed element)
525             $(LI $(D ASN1TagNumberException)
526                 if any nested primitives do not share the
527                 same tag number as their outer constructed element)
528         )
529     */
530     override public @property @system
531     bool[] bitString() const
532     in
533     {
534         // If the blank constructor ever stops producing an EOC,
535         // this method must change.
536         CERElement test = new CERElement();
537         assert(test.tagNumber == 0x00u);
538         assert(test.length == 0u);
539     }
540     do
541     {
542         if (this.construction == ASN1Construction.primitive)
543         {
544             if (this.value.length == 0u || this.value.length > 1000u)
545                 throw new ASN1ValueSizeException
546                 (1u, 1000u, this.value.length, "decode a primitively-encoded BIT STRING");
547 
548             if (this.value[0] > 0x07u)
549                 throw new ASN1ValueException
550                 (
551                     "In Canonical Encoding Rules (CER), the first byte of the encoded " ~
552                     "binary value (after the type and length bytes, of course) " ~
553                     "is used to indicate how many unused bits there are at the " ~
554                     "end of the BIT STRING. Since everything is encoded in bytes " ~
555                     "in Canonical Encoding Rules (CER), but a BIT STRING may not " ~
556                     "necessarily encode a number of bits, divisible by eight " ~
557                     "there may be bits at the end of the BIT STRING that will " ~
558                     "need to be identified as padding instead of meaningful data." ~
559                     "Since a byte is eight bits, the largest number that the " ~
560                     "first byte should encode is 7, since, if you have eight " ~
561                     "unused bits or more, you may as well truncate an entire " ~
562                     "byte from the encoded data. This exception was thrown because " ~
563                     "you attempted to decode a BIT STRING whose first byte " ~
564                     "had a value greater than seven. The value was: " ~
565                     text(this.value[0]) ~ ". " ~ notWhatYouMeantText ~
566                     forMoreInformationText ~ debugInformationText ~ reportBugsText
567                 );
568 
569             if (this.value[0] > 0x00u && this.value.length <= 1u)
570                 throw new ASN1ValueException
571                 (
572                     "This exception was thrown because you attempted to decode a " ~
573                     "BIT STRING that had a misleading first byte, which indicated " ~
574                     "that there were more than zero padding bits, but there were " ~
575                     "no subsequent octets supplied, which contain the octet-" ~
576                     "aligned bits and padding. This may have been a mistake on " ~
577                     "the part of the encoder, but this looks really suspicious: " ~
578                     "it is likely that an attempt was made to hack your systems " ~
579                     "by inducing an out-of-bounds read from an array. " ~
580                     notWhatYouMeantText ~ forMoreInformationText ~
581                     debugInformationText ~ reportBugsText
582                 );
583 
584             bool[] ret;
585             ret.length = ((this.value.length - 1u) << 3u);
586             const ubyte[] allButTheFirstByte = this.value[1 .. $];
587             foreach (immutable size_t i, immutable ubyte b; allButTheFirstByte)
588             {
589                 ret[((i << 3u) + 0u)] = cast(bool) (b & 0b10000000u);
590                 ret[((i << 3u) + 1u)] = cast(bool) (b & 0b01000000u);
591                 ret[((i << 3u) + 2u)] = cast(bool) (b & 0b00100000u);
592                 ret[((i << 3u) + 3u)] = cast(bool) (b & 0b00010000u);
593                 ret[((i << 3u) + 4u)] = cast(bool) (b & 0b00001000u);
594                 ret[((i << 3u) + 5u)] = cast(bool) (b & 0b00000100u);
595                 ret[((i << 3u) + 6u)] = cast(bool) (b & 0b00000010u);
596                 ret[((i << 3u) + 7u)] = cast(bool) (b & 0b00000001u);
597             }
598 
599             foreach (immutable bit; ret[$-this.value[0] .. $])
600             {
601                 if (bit == true)
602                     throw new ASN1ValueException
603                     (
604                         "This exception was thrown because you attempted to decode " ~
605                         "a BIT STRING whose padding bits were not entirely zeroes. " ~
606                         "If you were using the Basic Encoding Rules (BER), this " ~
607                         "would not be a problem, but under Canonical Encoding " ~
608                         "Rules (CER), padding the BIT STRING with anything other " ~
609                         "zeroes is forbidden. " ~
610                         notWhatYouMeantText ~ forMoreInformationText ~
611                         debugInformationText ~ reportBugsText
612                     );
613             }
614 
615             ret.length -= this.value[0];
616             return ret;
617         }
618         else
619         {
620             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
621                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a BIT STRING");
622             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
623 
624             CERElement[] substrings = this.sequence;
625             if (substrings.length < 2u)
626                 throw new ASN1ValueException
627                 (
628                     "This exception was thrown because you attempted to decode " ~
629                     "a constructed BIT STRING that was constituted by fewer " ~
630                     "than two nested elements. When using the Canonical Encoding " ~
631                     "Rules (CER), a legitimate constructed BIT STRING must " ~
632                     "contain at least two nested elements, and each but the " ~
633                     "last must contain exactly 1000 content octets. The last " ~
634                     "nested element may have between 1 and 1000 content " ~
635                     "octets inclusively. This element should have been encoded " ~
636                     "primitively using definite-length encoding."
637                 );
638 
639             foreach (substring; substrings[0 .. $-1])
640             {
641                 if (substring.length != 1000u)
642                     throw new ASN1ValueSizeException
643                     (1000u, 1000u, substring.length, "decode a substring of a constructed BIT STRING");
644 
645                 if
646                 (
647                     substring.construction == ASN1Construction.primitive &&
648                     substring.length > 0u &&
649                     substring.value[0] != 0x00u
650                 )
651                     throw new ASN1ValueException
652                     (
653                         "This exception was thrown because you attempted to " ~
654                         "decode a constructed BIT STRING that contained a " ~
655                         "substring whose first byte indicated a non-zero " ~
656                         "number of padding bits, despite not being the " ~
657                         "last substring of the constructed BIT STRING. " ~
658                         "Only the last substring may have padding bits. "
659                     );
660             }
661 
662             if (substrings[$-1].length == 0u)
663                 throw new ASN1ValueSizeException
664                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed OCTET STRING");
665 
666             Appender!(bool[]) appendy = appender!(bool[])();
667             foreach (substring; substrings)
668             {
669                 if (substring.tagClass != this.tagClass)
670                     throw new ASN1TagClassException
671                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed BIT STRING");
672 
673                 if (substring.construction != ASN1Construction.primitive)
674                     throw new ASN1ConstructionException
675                     (substring.construction, "decode a substring of a constructed OCTET STRING");
676 
677                 if (substring.tagNumber != this.tagNumber)
678                     throw new ASN1TagNumberException
679                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed BIT STRING");
680 
681                 appendy.put(substring.bitString);
682             }
683             return appendy.data;
684         }
685     }
686 
687     /// Encodes an array of $(D bool)s representing a string of bits.
688     override public @property @system
689     void bitString(in bool[] value)
690     out
691     {
692         assert(this.value.length > 0u);
693     }
694     do
695     {
696         ubyte[] ub;
697         ub.length = ((value.length / 8u) + (value.length % 8u ? 1u : 0u));
698         foreach (immutable size_t i, immutable bool b; value)
699         {
700             if (!b) continue;
701             ub[(i >> 3u)] |= (0b10000000u >> (i % 8u));
702         }
703 
704         if (ub.length <= 1000u)
705         {
706             this.value = [ cast(ubyte) (8u - (value.length % 8u)) ] ~ ub;
707             if (this.value[0] == 0x08u) this.value[0] = 0x00u;
708         }
709         else
710         {
711             CERElement[] primitives;
712             size_t i = 0u;
713             while ((i + 999u) < value.length)
714             {
715                 CERElement x = new CERElement();
716                 x.tagNumber = this.tagNumber;
717                 x.construction = ASN1Construction.primitive;
718                 x.value.length = 1000u;
719                 x.value[1u .. 999u] = ub[i .. (i + 999u)];
720                 primitives ~= x;
721                 i += 999u;
722             }
723 
724             CERElement y = new CERElement();
725             y.tagNumber = this.tagNumber;
726             y.construction = ASN1Construction.primitive;
727             y.value = ([ cast(ubyte) 0x00u ] ~ ub[i .. $]);
728             primitives ~= y;
729 
730             CERElement z = new CERElement();
731             primitives ~= z;
732 
733             this.sequence = primitives;
734             this.construction = ASN1Construction.constructed;
735         }
736     }
737 
738     @system
739     unittest
740     {
741         bool[] data;
742         data.length = 289u;
743         for (size_t i = 0u; i < data.length; i++)
744         {
745             data[i] = cast(bool) (i % 3);
746         }
747         CERElement el = new CERElement();
748         el.bitString = data;
749         assert(el.bitString == data);
750     }
751 
752     @system
753     unittest
754     {
755         void test(size_t length)
756         {
757             bool[] data;
758             data.length = length;
759             for (size_t i = 0u; i < data.length; i++)
760             {
761                 data[i] = cast(bool) (i % 3);
762             }
763             CERElement el = new CERElement();
764             el.bitString = data;
765             assert(el.bitString == data);
766         }
767         test(0u);
768         test(1u);
769         test(8u);
770         test(127u);
771         test(128u);
772         test(129u);
773         test(192u);
774         test(999u);
775         test(1000u);
776         test(1001u);
777         test(2017u);
778     }
779 
780     // Ensure that 1s in the padding get PUNISHED with an exception
781     @system
782     unittest
783     {
784         CERElement el = new CERElement();
785         el.value = [ 0x07u, 0b11000000u ];
786         assertThrown!ASN1ValueException(el.bitString);
787 
788         el.value = [ 0x01u, 0b11111111u ];
789         assertThrown!ASN1ValueException(el.bitString);
790 
791         el.value = [ 0x00u, 0b11111111u ];
792         assertNotThrown!ASN1ValueException(el.bitString);
793     }
794 
795     // Test a BIT STRING with a deceptive first byte.
796     @system
797     unittest
798     {
799         CERElement el = new CERElement();
800         el.value = [ 0x01u ];
801         assertThrown!ASN1ValueException(el.bitString);
802     }
803 
804     /**
805         Decodes an $(MONO OCTET STRING) into an unsigned byte array.
806 
807         Throws:
808         $(UL
809             $(LI $(D ASN1RecursionException)
810                 if using constructed form and the element
811                 is constructed of too many nested constructed elements)
812             $(LI $(D ASN1TagClassException)
813                 if any nested primitives do not share the
814                 same tag class as their outer constructed element)
815             $(LI $(D ASN1TagNumberException)
816                 if any nested primitives do not share the
817                 same tag number as their outer constructed element)
818         )
819     */
820     override public @property @system
821     ubyte[] octetString() const
822     {
823         if (this.construction == ASN1Construction.primitive)
824         {
825             if (this.value.length > 1000u)
826                 throw new ASN1ValueSizeException
827                 (0u, 1000u, this.value.length, "decode a primitively-encoded OCTET STRING");
828 
829             return this.value.dup;
830         }
831         else
832         {
833             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
834                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse an OCTET STRING");
835             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
836 
837             CERElement[] substrings = this.sequence;
838             if (substrings.length < 2u)
839                 throw new ASN1ValueException
840                 (
841                     "This exception was thrown because you attempted to decode " ~
842                     "a constructed OCTET STRING that was constituted by fewer " ~
843                     "than two nested elements. When using the Canonical Encoding " ~
844                     "Rules (CER), a legitimate constructed OCTET STRING must " ~
845                     "contain at least two nested elements, and each but the " ~
846                     "last must contain exactly 1000 content octets. The last " ~
847                     "nested element may have between 1 and 1000 content " ~
848                     "octets inclusively. This element should have been encoded " ~
849                     "primitively using definite-length encoding."
850                 );
851 
852             foreach (substring; substrings[0 .. $-1])
853             {
854                 if (substring.length != 1000u)
855                     throw new ASN1ValueSizeException
856                     (1000u, 1000u, substring.length, "decode a substring of a constructed OCTET STRING");
857             }
858 
859             if (substrings[$-1].length == 0u)
860                 throw new ASN1ValueSizeException
861                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed OCTET STRING");
862 
863             Appender!(ubyte[]) appendy = appender!(ubyte[])();
864             foreach (substring; substrings)
865             {
866                 if (substring.tagClass != this.tagClass)
867                     throw new ASN1TagClassException
868                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed OCTET STRING");
869 
870                 if (substring.construction != ASN1Construction.primitive)
871                     throw new ASN1ConstructionException
872                     (substring.construction, "decode a substring of a constructed OCTET STRING");
873 
874                 if (substring.tagNumber != this.tagNumber)
875                     throw new ASN1TagNumberException
876                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed OCTET STRING");
877 
878                 appendy.put(substring.octetString);
879             }
880             return appendy.data;
881         }
882     }
883 
884     /// Encodes an $(MONO OCTET STRING) from an unsigned byte ($(D ubyte)) array.
885     override public @property @system
886     void octetString(in ubyte[] value)
887     in
888     {
889         // If the blank constructor ever stops producing an EOC,
890         // this method must change.
891         CERElement test = new CERElement();
892         assert(test.tagNumber == 0x00u);
893         assert(test.length == 0u);
894     }
895     do
896     {
897         if (value.length <= 1000u)
898         {
899             this.value = value.dup;
900         }
901         else
902         {
903             CERElement[] primitives;
904             size_t i = 0u;
905             while (i+1000u < value.length)
906             {
907                 CERElement x = new CERElement();
908                 x.tagNumber = this.tagNumber;
909                 x.construction = ASN1Construction.primitive;
910                 x.value = value[i .. i+1000u].dup;
911                 primitives ~= x;
912                 i += 1000u;
913             }
914 
915             CERElement y = new CERElement();
916             y.tagNumber = this.tagNumber;
917             y.construction = ASN1Construction.primitive;
918             y.value = value[i .. $].dup;
919             primitives ~= y;
920 
921             this.sequence = primitives;
922             this.construction = ASN1Construction.constructed;
923         }
924     }
925 
926     @system
927     unittest
928     {
929         void test(size_t length)
930         {
931             ubyte[] data;
932             data.length = length;
933             for (size_t i = 0u; i < data.length; i++)
934             {
935                 data[i] = (i % 9u);
936             }
937             CERElement el = new CERElement();
938             el.octetString = data;
939             assert(el.octetString == data);
940         }
941         test(0u);
942         test(1u);
943         test(8u);
944         test(127u);
945         test(128u);
946         test(129u);
947         test(192u);
948         test(999u);
949         test(1000u);
950         test(1001u);
951         test(2017u);
952     }
953 
954     /**
955         "Decodes" a $(D null), by which I mean: returns nothing, but
956         throws exceptions if the element is not correct.
957 
958         Note:
959             I had to name this method $(D nill), because $(D null) is a keyword in D.
960 
961         Throws:
962         $(UL
963             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
964             $(LI $(D ASN1ValueSizeException) if there are any content octets)
965         )
966     */
967     override public @property @safe
968     void nill() const
969     {
970         if (this.construction != ASN1Construction.primitive)
971             throw new ASN1ConstructionException
972             (this.construction, "decode a NULL");
973 
974         if (this.value.length != 0u)
975             throw new ASN1ValueSizeException
976             (0u, 0u, this.value.length, "decode a NULL");
977     }
978 
979     /**
980         Decodes an $(MONO OBJECT IDENTIFIER).
981         See $(MONO source/types/universal/objectidentifier.d) for information about
982         the $(D ObjectIdentifier) class (aliased as $(D OID)).
983 
984         Throws:
985         $(UL
986             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
987             $(LI $(D ASN1ValueSizeException) if there are no value bytes)
988             $(LI $(D ASN1ValuePaddingException) if a single OID number is encoded with
989                 "leading zero bytes" ($(D 0x80u)))
990             $(LI $(D ASN1ValueOverflowException) if a single OID number is too big to
991                 decode to a $(D size_t))
992             $(LI $(D ASN1TruncationException) if a single OID number is "cut off")
993         )
994 
995         Standards:
996         $(UL
997             $(LI $(LINK http://www.itu.int/rec/T-REC-X.660-201107-I/en, X.660))
998         )
999     */
1000     override public @property @system
1001     OID objectIdentifier() const
1002     out (value)
1003     {
1004         assert(value.length >= 2u);
1005     }
1006     do
1007     {
1008         if (this.construction != ASN1Construction.primitive)
1009             throw new ASN1ConstructionException
1010             (this.construction, "decode an OBJECT IDENTIFIER");
1011 
1012         if (this.value.length == 0u)
1013             throw new ASN1ValueSizeException
1014             (1u, size_t.max, 0u, "decode an OBJECT IDENTIFIER");
1015 
1016         size_t[] numbers = [ 0u, 0u ];
1017         if (this.value[0] >= 0x50u)
1018         {
1019             numbers[0] = 2u;
1020             numbers[1] = (this.value[0] - 0x50u);
1021         }
1022         else if (this.value[0] >= 0x28u)
1023         {
1024             numbers[0] = 1u;
1025             numbers[1] = (this.value[0] - 0x28u);
1026         }
1027         else
1028         {
1029             numbers[0] = 0u;
1030             numbers[1] = this.value[0];
1031         }
1032 
1033         if (this.value.length == 1u)
1034             return new OID(numbers);
1035 
1036         if ((this.value[$-1] & 0x80u) == 0x80u)
1037             throw new ASN1TruncationException
1038             (size_t.max, this.value.length, "decode an OBJECT IDENTIFIER");
1039 
1040         size_t components = 2u;
1041         const ubyte[] allButTheFirstByte = this.value[1 .. $];
1042         foreach (immutable size_t i, immutable ubyte b; allButTheFirstByte)
1043         {
1044             if (!(b & 0x80u)) components++;
1045         }
1046         numbers.length = components;
1047 
1048         size_t currentNumber = 2u;
1049         ubyte bytesUsedInCurrentNumber = 0u;
1050         foreach (immutable ubyte b; allButTheFirstByte)
1051         {
1052             if (bytesUsedInCurrentNumber == 0u && b == 0x80u)
1053                 throw new ASN1ValuePaddingException
1054                 (
1055                     "This exception was thrown because you attempted to decode " ~
1056                     "an OBJECT IDENTIFIER that contained a number that was " ~
1057                     "encoded on more than the minimum necessary octets. This " ~
1058                     "is indicated by an occurrence of the octet 0x80, which " ~
1059                     "is the encoded equivalent of a leading zero. " ~
1060                     notWhatYouMeantText ~ forMoreInformationText ~
1061                     debugInformationText ~ reportBugsText
1062                 );
1063 
1064             if (numbers[currentNumber] > (size_t.max >> 7))
1065                 throw new ASN1ValueOverflowException
1066                 (
1067                     "This exception was thrown because you attempted to decode " ~
1068                     "an OBJECT IDENTIFIER that encoded a number that is too big to " ~
1069                     "decode to a native integral type. " ~
1070                     notWhatYouMeantText ~ forMoreInformationText ~
1071                     debugInformationText ~ reportBugsText
1072                 );
1073 
1074             numbers[currentNumber] <<= 7;
1075             numbers[currentNumber] |= cast(size_t) (b & 0x7Fu);
1076 
1077             if (!(b & 0x80u))
1078             {
1079                 currentNumber++;
1080                 bytesUsedInCurrentNumber = 0u;
1081             }
1082             else
1083             {
1084                 bytesUsedInCurrentNumber++;
1085             }
1086         }
1087 
1088         return new OID(numbers);
1089     }
1090 
1091     /**
1092         Encodes an $(MONO OBJECT IDENTIFIER).
1093         See $(MONO source/types/universal/objectidentifier.d) for information about
1094         the $(D ObjectIdentifier) class (aliased as $(OID)).
1095 
1096         Standards:
1097         $(UL
1098             $(LI $(LINK http://www.itu.int/rec/T-REC-X.660-201107-I/en, X.660))
1099         )
1100     */
1101     override public @property @system
1102     void objectIdentifier(in OID value)
1103     in
1104     {
1105         assert(value.length >= 2u);
1106         assert(value.numericArray[0] <= 2u);
1107         if (value.numericArray[0] == 2u)
1108             assert(value.numericArray[1] <= 175u);
1109         else
1110             assert(value.numericArray[1] <= 39u);
1111     }
1112     out
1113     {
1114         assert(this.value.length > 0u);
1115     }
1116     do
1117     {
1118         scope(success) this.construction = ASN1Construction.primitive;
1119         size_t[] numbers = value.numericArray();
1120         this.value = [ cast(ubyte) (numbers[0] * 40u + numbers[1]) ];
1121         if (numbers.length > 2u)
1122         {
1123             foreach (number; numbers[2 .. $])
1124             {
1125                 if (number < 128u)
1126                 {
1127                     this.value ~= cast(ubyte) number;
1128                     continue;
1129                 }
1130 
1131                 ubyte[] encodedOIDNode;
1132                 while (number != 0u)
1133                 {
1134                     ubyte[] numberBytes;
1135                     numberBytes.length = size_t.sizeof;
1136                     *cast(size_t *) numberBytes.ptr = number;
1137                     if ((numberBytes[0] & 0x80u) == 0u) numberBytes[0] |= 0x80u;
1138                     encodedOIDNode = numberBytes[0] ~ encodedOIDNode;
1139                     number >>= 7u;
1140                 }
1141 
1142                 encodedOIDNode[$-1] &= 0x7Fu;
1143                 this.value ~= encodedOIDNode;
1144             }
1145         }
1146     }
1147 
1148     @system
1149     unittest
1150     {
1151         CERElement element = new CERElement();
1152 
1153         // All values of octet[0] should pass.
1154         for (ubyte i = 0x00u; i < 0xFFu; i++)
1155         {
1156             element.value = [ i ];
1157             assertNotThrown!Exception(element.objectIdentifier);
1158         }
1159 
1160         // All values of octet[0] should pass.
1161         for (ubyte i = 0x00u; i < 0xFFu; i++)
1162         {
1163             element.value = [ i, 0x14u ];
1164             assertNotThrown!Exception(element.objectIdentifier);
1165         }
1166     }
1167 
1168     @system
1169     unittest
1170     {
1171         CERElement element = new CERElement();
1172 
1173         // Tests for the "leading zero byte," 0x80
1174         element.value = [ 0x29u, 0x80u, 0x14u ];
1175         assertThrown!ASN1ValuePaddingException(element.objectIdentifier);
1176 
1177         // This one is fine, because the null byte is in the middle of a number.
1178         element.value = [ 0x29u, 0x84u, 0x80u, 0x01u ];
1179         assertNotThrown!ASN1CodecException(element.objectIdentifier);
1180 
1181         // Test for non-terminating components
1182         element.value = [ 0x29u, 0x80u, 0x80u ];
1183         assertThrown!ASN1TruncationException(element.objectIdentifier);
1184         element.value = [ 0x80u, 0x80u, 0x80u ];
1185         assertThrown!ASN1TruncationException(element.objectIdentifier);
1186         element.value = [ 0x29u, 0x81u ];
1187         assertThrown!ASN1TruncationException(element.objectIdentifier);
1188         element.value = [ 0x29u, 0x80u ];
1189         assertThrown!ASN1TruncationException(element.objectIdentifier);
1190         element.value = [ 0x29u, 0x14u, 0x81u ];
1191         assertThrown!ASN1TruncationException(element.objectIdentifier);
1192         element.value = [ 0x29u, 0x14u, 0x80u ];
1193         assertThrown!ASN1TruncationException(element.objectIdentifier);
1194 
1195         // This one should not fail. 0x80u is valid for the first octet.
1196         element.value = [ 0x80u, 0x14u, 0x14u ];
1197         assertNotThrown!ASN1ValuePaddingException(element.objectIdentifier);
1198     }
1199 
1200     /**
1201         Decodes an $(D ObjectDescriptor), which is a string consisting of only
1202         graphical characters. In fact, $(D ObjectDescriptor) is actually implicitly
1203         just a $(MONO GraphicString)! The formal specification for an $(D ObjectDescriptor)
1204         is:
1205 
1206         $(MONO ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1207 
1208         $(MONO GraphicString) is just a string containing only characters between
1209         and including $(D 0x20) and $(D 0x7E), therefore ObjectDescriptor is just
1210         $(D 0x20) and $(D 0x7E).
1211 
1212         Throws:
1213         $(UL
1214             $(LI $(D ASN1ValueCharactersException)
1215                 if the encoded value contains any character outside of
1216                 $(D 0x20) to $(D 0x7E), which means any control characters or $(MONO DELETE))
1217             $(LI $(D ASN1RecursionException)
1218                 if using constructed form and the element
1219                 is constructed of too many nested constructed elements)
1220             $(LI $(D ASN1TagClassException)
1221                 if any nested primitives do not share the
1222                 same tag class as their outer constructed element)
1223             $(LI $(D ASN1TagNumberException)
1224                 if any nested primitives do not share the
1225                 same tag number as their outer constructed element)
1226         )
1227 
1228         Citations:
1229         $(UL
1230             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
1231                 $(I ASN.1: Communication between Heterogeneous Systems),
1232                 Morgan Kaufmann, 2001, pp. 175-178.)
1233             $(LI $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022))
1234             $(LI $(LINK https://www.iso.org/standard/22747.html, ISO 2022))
1235         )
1236     */
1237     override public @property @system
1238     string objectDescriptor() const
1239     {
1240         if (this.construction == ASN1Construction.primitive)
1241         {
1242             if (this.value.length > 1000u)
1243                 throw new ASN1ValueSizeException
1244                 (0u, 1000u, this.value.length, "decode a primitively-encoded ObjectDescriptor");
1245 
1246             foreach (immutable character; this.value)
1247             {
1248                 if ((!character.isGraphical) && (character != ' '))
1249                     throw new ASN1ValueCharactersException
1250                     ("all characters within the range 0x20 to 0x7E", character, "ObjectDescriptor");
1251             }
1252             return cast(string) this.value;
1253         }
1254         else
1255         {
1256             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
1257                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse an ObjectDescriptor");
1258             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
1259 
1260             CERElement[] substrings = this.sequence;
1261             if (substrings.length < 2u)
1262                 throw new ASN1ValueException
1263                 (
1264                     "This exception was thrown because you attempted to decode " ~
1265                     "a constructed ObjectDescriptor that was constituted by fewer " ~
1266                     "than two nested elements. When using the Canonical Encoding " ~
1267                     "Rules (CER), a legitimate constructed ObjectDescriptor must " ~
1268                     "contain at least two nested elements, and each but the " ~
1269                     "last must contain exactly 1000 content octets. The last " ~
1270                     "nested element may have between 1 and 1000 content " ~
1271                     "octets inclusively. This element should have been encoded " ~
1272                     "primitively using definite-length encoding."
1273                 );
1274 
1275             foreach (substring; substrings[0 .. $-1])
1276             {
1277                 if (substring.length != 1000u)
1278                     throw new ASN1ValueSizeException
1279                     (1000u, 1000u, substring.length, "decode a substring of a constructed ObjectDescriptor");
1280             }
1281 
1282             if (substrings[$-1].length == 0u)
1283                 throw new ASN1ValueSizeException
1284                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed ObjectDescriptor");
1285 
1286             Appender!string appendy = appender!string();
1287             foreach (substring; substrings)
1288             {
1289                 if (substring.tagClass != this.tagClass)
1290                     throw new ASN1TagClassException
1291                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed ObjectDescriptor");
1292 
1293                 if (substring.construction != ASN1Construction.primitive)
1294                     throw new ASN1ConstructionException
1295                     (substring.construction, "decode a substring of a constructed ObjectDescriptor");
1296 
1297                 if (substring.tagNumber != this.tagNumber)
1298                     throw new ASN1TagNumberException
1299                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed ObjectDescriptor");
1300 
1301                 appendy.put(substring.octetString);
1302             }
1303             return appendy.data;
1304         }
1305     }
1306 
1307     /**
1308         Encodes an $(D ObjectDescriptor), which is a string consisting of only
1309         graphical characters. In fact, $(D ObjectDescriptor) is actually implicitly
1310         just a $(MONO GraphicString)! The formal specification for an $(D ObjectDescriptor)
1311         is:
1312 
1313         $(MONO ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1314 
1315         $(MONO GraphicString) is just a string containing only characters between
1316         and including $(D 0x20) and $(D 0x7E), therefore ObjectDescriptor is just
1317         $(D 0x20) and $(D 0x7E).
1318 
1319         Throws:
1320         $(UL
1321             $(LI $(D ASN1ValueCharactersException)
1322                 if the string value contains any character outside of
1323                 $(D 0x20) to $(D 0x7E), which means any control characters or $(MONO DELETE))
1324         )
1325 
1326         Citations:
1327         $(UL
1328             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
1329                 $(I ASN.1: Communication between Heterogeneous Systems),
1330                 Morgan Kaufmann, 2001, pp. 175-178.)
1331             $(LI $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022))
1332             $(LI $(LINK https://www.iso.org/standard/22747.html, ISO 2022))
1333         )
1334     */
1335     override public @property @system
1336     void objectDescriptor(in string value)
1337     {
1338         foreach (immutable character; value)
1339         {
1340             if ((!character.isGraphical) && (character != ' '))
1341                 throw new ASN1ValueCharactersException
1342                 ("all characters within the range 0x20 to 0x7E", character, "ObjectDescriptor");
1343         }
1344 
1345         if (value.length <= 1000u)
1346         {
1347             this.value = cast(ubyte[]) value;
1348         }
1349         else
1350         {
1351             CERElement[] primitives;
1352             size_t i = 0u;
1353             while (i+1000u < value.length)
1354             {
1355                 CERElement x = new CERElement();
1356                 x.tagNumber = this.tagNumber;
1357                 x.construction = ASN1Construction.primitive;
1358                 x.value = cast(ubyte[]) value[i .. i+1000u];
1359                 primitives ~= x;
1360                 i += 1000u;
1361             }
1362 
1363             CERElement y = new CERElement();
1364             y.tagNumber = this.tagNumber;
1365             y.construction = ASN1Construction.primitive;
1366             y.value = cast(ubyte[]) value[i .. $];
1367             primitives ~= y;
1368 
1369             this.sequence = primitives;
1370             this.construction = ASN1Construction.constructed;
1371         }
1372     }
1373 
1374     @system
1375     unittest
1376     {
1377         void test(size_t length)
1378         {
1379             char[] data;
1380             data.length = length;
1381             for (size_t i = 0u; i < data.length; i++)
1382             {
1383                 data[i] = ((i % 0x20u) + 0x41u);
1384             }
1385             CERElement el = new CERElement();
1386             el.objectDescriptor = cast(string) data;
1387             assert(el.objectDescriptor == cast(string) data);
1388         }
1389         test(0u);
1390         test(1u);
1391         test(8u);
1392         test(127u);
1393         test(128u);
1394         test(129u);
1395         test(192u);
1396         test(999u);
1397         test(1000u);
1398         test(1001u);
1399         test(2017u);
1400     }
1401 
1402     /**
1403         Decodes an $(MONO EXTERNAL).
1404 
1405         According to the
1406         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
1407         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1)),
1408         the abstract definition for an $(MONO EXTERNAL), after removing the comments in the
1409         specification, is as follows:
1410 
1411         $(PRE
1412             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1413                 identification CHOICE {
1414                     syntaxes SEQUENCE {
1415                         abstract OBJECT IDENTIFIER,
1416                         transfer OBJECT IDENTIFIER },
1417                     syntax OBJECT IDENTIFIER,
1418                     presentation-context-id INTEGER,
1419                     context-negotiation SEQUENCE {
1420                         presentation-context-id INTEGER,
1421                         transfer-syntax OBJECT IDENTIFIER },
1422                     transfer-syntax OBJECT IDENTIFIER,
1423                     fixed NULL },
1424                 data-value-descriptor ObjectDescriptor OPTIONAL,
1425                 data-value OCTET STRING }
1426                     ( WITH COMPONENTS {
1427                         ... ,
1428                         identification ( WITH COMPONENTS {
1429                             ... ,
1430                             syntaxes ABSENT,
1431                             transfer-syntax ABSENT,
1432                             fixed ABSENT } ) } )
1433         )
1434 
1435         Note that the abstract syntax resembles that of $(MONO EmbeddedPDV) and
1436         $(MONO CharacterString), except with a $(MONO WITH COMPONENTS) constraint that removes some
1437         of our choices of $(MONO identification).
1438         As can be seen on page 303 of Olivier Dubuisson's
1439         $(I $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF,
1440             ASN.1: Communication Between Heterogeneous Systems)),
1441         after applying the $(MONO WITH COMPONENTS) constraint, our reduced syntax becomes:
1442 
1443         $(PRE
1444             EXTERNAL ::= [UNIVERSAL 8] IMPLICIT SEQUENCE {
1445                 identification CHOICE {
1446                     syntax OBJECT IDENTIFIER,
1447                     presentation-context-id INTEGER,
1448                     context-negotiation SEQUENCE {
1449                         presentation-context-id INTEGER,
1450                         transfer-syntax OBJECT IDENTIFIER } },
1451                 data-value-descriptor ObjectDescriptor OPTIONAL,
1452                 data-value OCTET STRING }
1453         )
1454 
1455         But, according to the
1456         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
1457         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules),
1458         section 8.18, when encoded using Basic Encoding Rules (BER), is encoded as
1459         follows, for compatibility reasons:
1460 
1461         $(PRE
1462             EXTERNAL ::= [UNIVERSAL 8] IMPLICIT SEQUENCE {
1463                 direct-reference  OBJECT IDENTIFIER OPTIONAL,
1464                 indirect-reference  INTEGER OPTIONAL,
1465                 data-value-descriptor  ObjectDescriptor  OPTIONAL,
1466                 encoding  CHOICE {
1467                     single-ASN1-type  [0] ANY,
1468                     octet-aligned     [1] IMPLICIT OCTET STRING,
1469                     arbitrary         [2] IMPLICIT BIT STRING } }
1470         )
1471 
1472         The definition above is the pre-1994 definition of $(MONO EXTERNAL). The $(MONO syntax)
1473         field of the post-1994 definition maps to the $(MONO direct-reference) field of
1474         the pre-1994 definition. The $(MONO presentation-context-id) field of the post-1994
1475         definition maps to the $(MONO indirect-reference) field of the pre-1994 definition.
1476         If $(MONO context-negotiation) is used, per the abstract syntax, then the
1477         $(MONO presentation-context-id) field of the $(MONO context-negotiation) $(MONO SEQUENCE) in the
1478         post-1994 definition maps to the $(MONO indirect-reference) field of the pre-1994
1479         definition, and the $(MONO transfer-syntax) field of the $(MONO context-negotiation)
1480         $(MONO SEQUENCE) maps to the $(MONO direct-reference) field of the pre-1994 definition.
1481 
1482         The following additional constraints are applied to the abstract syntax
1483         when using Canonical Encoding Rules or Distinguished Encoding Rules,
1484         which are also defined in the
1485         $(LINK https://www.itu.int/en/pages/default.aspx,
1486         International Telecommunications Union)'s
1487         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules):
1488 
1489         $(PRE
1490             EXTERNAL ( WITH COMPONENTS {
1491                 ... ,
1492                 identification ( WITH COMPONENTS {
1493                     ... ,
1494                     presentation-context-id ABSENT,
1495                     context-negotiation ABSENT } ) } )
1496         )
1497 
1498         The stated purpose of the constraints shown above is to restrict the use of
1499         the $(MONO presentation-context-id), either by itself or within the
1500         $(MONO context-negotiation), which makes the following the effective abstract
1501         syntax of $(MONO EXTERNAL) when using Canonical Encoding Rules or
1502         Distinguished Encoding Rules:
1503 
1504         $(PRE
1505             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1506                 identification CHOICE {
1507                     syntaxes SEQUENCE {
1508                         abstract OBJECT IDENTIFIER,
1509                         transfer OBJECT IDENTIFIER },
1510                     syntax OBJECT IDENTIFIER,
1511                     presentation-context-id INTEGER,
1512                     context-negotiation SEQUENCE {
1513                         presentation-context-id INTEGER,
1514                         transfer-syntax OBJECT IDENTIFIER },
1515                     transfer-syntax OBJECT IDENTIFIER,
1516                     fixed NULL },
1517                 data-value-descriptor ObjectDescriptor OPTIONAL,
1518                 data-value OCTET STRING }
1519                     ( WITH COMPONENTS {
1520                         ... ,
1521                         identification ( WITH COMPONENTS {
1522                             ... ,
1523                             syntaxes ABSENT,
1524                             presentation-context-id ABSENT,
1525                             context-negotiation ABSENT,
1526                             transfer-syntax ABSENT,
1527                             fixed ABSENT } ) } )
1528         )
1529 
1530         With the constraints applied, the abstract syntax for $(MONO EXTERNAL)s encoded
1531         using Canonical Encoding Rules or Distinguished Encoding Rules becomes:
1532 
1533         $(PRE
1534             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1535                 identification CHOICE {
1536                     syntax OBJECT IDENTIFIER },
1537                 data-value-descriptor ObjectDescriptor OPTIONAL,
1538                 data-value OCTET STRING }
1539         )
1540 
1541         Upon removing the $(MONO CHOICE) tag (since you have no choice but to use syntax
1542         at this point), the encoding definition when using
1543         Canonical Encoding Rules or Distinguished Encoding Rules:
1544 
1545         $(PRE
1546             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1547                 syntax OBJECT IDENTIFIER,
1548                 data-value-descriptor ObjectDescriptor OPTIONAL,
1549                 data-value OCTET STRING }
1550         )
1551 
1552         Returns: an instance of $(D types.universal.external.External)
1553 
1554         Throws:
1555         $(UL
1556             $(LI $(D ASN1ValueException)
1557                 if the SEQUENCE does not contain two to four elements)
1558             $(LI $(D ASN1RecursionException)
1559                 if using constructed form and the element
1560                 is constructed of too many nested constructed elements)
1561             $(LI $(D ASN1TagClassException)
1562                 if any nested primitives do not have the correct tag class)
1563             $(LI $(D ASN1ConstructionException)
1564                 if any element has the wrong construction)
1565             $(LI $(D ASN1TagNumberException)
1566                 if any nested primitives do not have the correct tag number)
1567             $(LI $(D ASN1ValueCharactersException)
1568                 if a data-value-descriptor is supplied with invalid characters)
1569         )
1570     */
1571     deprecated override public @property @system
1572     External external() const
1573     {
1574         if (this.construction != ASN1Construction.constructed)
1575             throw new ASN1ConstructionException
1576             (this.construction, "decode an EXTERNAL");
1577 
1578         const CERElement[] components = this.sequence;
1579         External ext = External();
1580         ASN1ContextSwitchingTypeID identification = ASN1ContextSwitchingTypeID();
1581 
1582         if (components.length < 2u || components.length > 3u)
1583             throw new ASN1ValueException
1584             (
1585                 "This exception was thrown because you attempted to decode " ~
1586                 "an EXTERNAL that contained too many or too few elements. " ~
1587                 "An EXTERNAL should have either two and three elements: " ~
1588                 "a direct-reference (syntax), an optional " ~
1589                 "data-value-descriptor, and an encoding (data-value). " ~
1590                 notWhatYouMeantText ~ forMoreInformationText ~
1591                 debugInformationText ~ reportBugsText
1592             );
1593 
1594         // Every component except the last must be universal class
1595         foreach (const component; components[0 .. $-1])
1596         {
1597             if (component.tagClass != ASN1TagClass.universal)
1598                 throw new ASN1TagClassException
1599                 ([ ASN1TagClass.universal ], component.tagClass, "decode all but the last component of an EXTERNAL");
1600         }
1601 
1602         // The last tag must be context-specific class
1603         if (components[$-1].tagClass != ASN1TagClass.contextSpecific)
1604             throw new ASN1TagClassException
1605             ([ ASN1TagClass.contextSpecific ], components[$-1].tagClass, "decode the last component of an EXTERNAL");
1606 
1607         // The first component should always be primitive
1608         if (components[0].construction != ASN1Construction.primitive)
1609             throw new ASN1ConstructionException
1610             (components[0].construction, "decode the first component of an EXTERNAL");
1611 
1612         if (components[0].tagNumber != ASN1UniversalType.objectIdentifier)
1613             throw new ASN1TagNumberException
1614             ([ 2u, 6u ], components[0].tagNumber, "decode the first component of an EXTERNAL");
1615 
1616         identification.directReference = components[0].objectIdentifier;
1617         if (components.length == 3u)
1618         {
1619             if (components[1].tagNumber != ASN1UniversalType.objectDescriptor)
1620                 throw new ASN1TagNumberException
1621                 (
1622                     [ ASN1UniversalType.objectDescriptor ],
1623                     components[1].tagNumber,
1624                     "decode the second of three subcomponents of an EXTERNAL"
1625                 );
1626 
1627             ext.dataValueDescriptor = components[1].objectDescriptor;
1628         }
1629 
1630         switch (components[$-1].tagNumber)
1631         {
1632             case (0u): // single-ASN1-value
1633             {
1634                 ext.encoding = ASN1ExternalEncodingChoice.singleASN1Type;
1635                 break;
1636             }
1637             case (1u): // octet-aligned
1638             {
1639                 ext.encoding = ASN1ExternalEncodingChoice.octetAligned;
1640                 break;
1641             }
1642             case (2u): // arbitrary
1643             {
1644                 ext.encoding = ASN1ExternalEncodingChoice.arbitrary;
1645                 break;
1646             }
1647             default:
1648                 throw new ASN1TagNumberException
1649                 ([ 0u, 1u, 2u ], components[$-1].tagNumber, "decode an EXTERNAL identification");
1650         }
1651 
1652         ext.dataValue = components[$-1].value.dup;
1653         ext.identification = identification;
1654         return ext;
1655     }
1656 
1657     /**
1658         Encodes an $(MONO EXTERNAL).
1659 
1660         According to the
1661         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
1662         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1)),
1663         the abstract definition for an $(MONO EXTERNAL), after removing the comments in the
1664         specification, is as follows:
1665 
1666         $(PRE
1667             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1668                 identification CHOICE {
1669                     syntaxes SEQUENCE {
1670                         abstract OBJECT IDENTIFIER,
1671                         transfer OBJECT IDENTIFIER },
1672                     syntax OBJECT IDENTIFIER,
1673                     presentation-context-id INTEGER,
1674                     context-negotiation SEQUENCE {
1675                         presentation-context-id INTEGER,
1676                         transfer-syntax OBJECT IDENTIFIER },
1677                     transfer-syntax OBJECT IDENTIFIER,
1678                     fixed NULL },
1679                 data-value-descriptor ObjectDescriptor OPTIONAL,
1680                 data-value OCTET STRING }
1681                     ( WITH COMPONENTS {
1682                         ... ,
1683                         identification ( WITH COMPONENTS {
1684                             ... ,
1685                             syntaxes ABSENT,
1686                             transfer-syntax ABSENT,
1687                             fixed ABSENT } ) } )
1688         )
1689 
1690         Note that the abstract syntax resembles that of $(MONO EmbeddedPDV) and
1691         $(MONO CharacterString), except with a $(MONO WITH COMPONENTS) constraint that removes some
1692         of our choices of $(MONO identification).
1693         As can be seen on page 303 of Olivier Dubuisson's
1694         $(I $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF,
1695             ASN.1: Communication Between Heterogeneous Systems)),
1696         after applying the $(MONO WITH COMPONENTS) constraint, our reduced syntax becomes:
1697 
1698         $(PRE
1699             EXTERNAL ::= [UNIVERSAL 8] IMPLICIT SEQUENCE {
1700                 identification CHOICE {
1701                     syntax OBJECT IDENTIFIER,
1702                     presentation-context-id INTEGER,
1703                     context-negotiation SEQUENCE {
1704                         presentation-context-id INTEGER,
1705                         transfer-syntax OBJECT IDENTIFIER } },
1706                 data-value-descriptor ObjectDescriptor OPTIONAL,
1707                 data-value OCTET STRING }
1708         )
1709 
1710         But, according to the
1711         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
1712         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules),
1713         section 8.18, when encoded using Basic Encoding Rules (BER), is encoded as
1714         follows, for compatibility reasons:
1715 
1716         $(PRE
1717             EXTERNAL ::= [UNIVERSAL 8] IMPLICIT SEQUENCE {
1718                 direct-reference  OBJECT IDENTIFIER OPTIONAL,
1719                 indirect-reference  INTEGER OPTIONAL,
1720                 data-value-descriptor  ObjectDescriptor  OPTIONAL,
1721                 encoding  CHOICE {
1722                     single-ASN1-type  [0] ANY,
1723                     octet-aligned     [1] IMPLICIT OCTET STRING,
1724                     arbitrary         [2] IMPLICIT BIT STRING } }
1725         )
1726 
1727         The definition above is the pre-1994 definition of $(MONO EXTERNAL). The $(MONO syntax)
1728         field of the post-1994 definition maps to the $(MONO direct-reference) field of
1729         the pre-1994 definition. The $(MONO presentation-context-id) field of the post-1994
1730         definition maps to the $(MONO indirect-reference) field of the pre-1994 definition.
1731         If $(MONO context-negotiation) is used, per the abstract syntax, then the
1732         $(MONO presentation-context-id) field of the $(MONO context-negotiation) $(MONO SEQUENCE) in the
1733         post-1994 definition maps to the $(MONO indirect-reference) field of the pre-1994
1734         definition, and the $(MONO transfer-syntax) field of the $(MONO context-negotiation)
1735         $(MONO SEQUENCE) maps to the $(MONO direct-reference) field of the pre-1994 definition.
1736 
1737         The following additional constraints are applied to the abstract syntax
1738         when using Canonical Encoding Rules or Distinguished Encoding Rules,
1739         which are also defined in the
1740         $(LINK https://www.itu.int/en/pages/default.aspx,
1741         International Telecommunications Union)'s
1742         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules):
1743 
1744         $(PRE
1745             EXTERNAL ( WITH COMPONENTS {
1746                 ... ,
1747                 identification ( WITH COMPONENTS {
1748                     ... ,
1749                     presentation-context-id ABSENT,
1750                     context-negotiation ABSENT } ) } )
1751         )
1752 
1753         The stated purpose of the constraints shown above is to restrict the use of
1754         the $(MONO presentation-context-id), either by itself or within the
1755         $(MONO context-negotiation), which makes the following the effective abstract
1756         syntax of $(MONO EXTERNAL) when using Canonical Encoding Rules or
1757         Distinguished Encoding Rules:
1758 
1759         $(PRE
1760             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1761                 identification CHOICE {
1762                     syntaxes SEQUENCE {
1763                         abstract OBJECT IDENTIFIER,
1764                         transfer OBJECT IDENTIFIER },
1765                     syntax OBJECT IDENTIFIER,
1766                     presentation-context-id INTEGER,
1767                     context-negotiation SEQUENCE {
1768                         presentation-context-id INTEGER,
1769                         transfer-syntax OBJECT IDENTIFIER },
1770                     transfer-syntax OBJECT IDENTIFIER,
1771                     fixed NULL },
1772                 data-value-descriptor ObjectDescriptor OPTIONAL,
1773                 data-value OCTET STRING }
1774                     ( WITH COMPONENTS {
1775                         ... ,
1776                         identification ( WITH COMPONENTS {
1777                             ... ,
1778                             syntaxes ABSENT,
1779                             presentation-context-id ABSENT,
1780                             context-negotiation ABSENT,
1781                             transfer-syntax ABSENT,
1782                             fixed ABSENT } ) } )
1783         )
1784 
1785         With the constraints applied, the abstract syntax for $(MONO EXTERNAL)s encoded
1786         using Canonical Encoding Rules or Distinguished Encoding Rules becomes:
1787 
1788         $(PRE
1789             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1790                 identification CHOICE {
1791                     syntax OBJECT IDENTIFIER },
1792                 data-value-descriptor ObjectDescriptor OPTIONAL,
1793                 data-value OCTET STRING }
1794         )
1795 
1796         Upon removing the $(MONO CHOICE) tag (since you have no choice but to use syntax
1797         at this point), the encoding definition when using
1798         Canonical Encoding Rules or Distinguished Encoding Rules:
1799 
1800         $(PRE
1801             EXTERNAL ::= [UNIVERSAL 8] SEQUENCE {
1802                 syntax OBJECT IDENTIFIER,
1803                 data-value-descriptor ObjectDescriptor OPTIONAL,
1804                 data-value OCTET STRING }
1805         )
1806 
1807         Returns: an instance of $(D types.universal.external.External)
1808 
1809         Throws:
1810         $(UL
1811             $(LI $(D ASN1ValueException)
1812                 if something other than $(D syntax) is used for $(MONO identification))
1813             $(LI $(D ASN1ValueCharactersException)
1814                 if a data-value-descriptor is supplied with invalid characters)
1815         )
1816     */
1817     deprecated override public @property @system
1818     void external(in External value)
1819     out
1820     {
1821         assert(this.value.length > 0u);
1822     }
1823     do
1824     {
1825         scope(success) this.construction = ASN1Construction.constructed;
1826         CERElement[] components = [];
1827 
1828         if (!(value.identification.syntax.isNull))
1829         {
1830             CERElement directReference = new CERElement();
1831             directReference.tagNumber = ASN1UniversalType.objectIdentifier;
1832             directReference.objectIdentifier = value.identification.directReference;
1833             components ~= directReference;
1834         }
1835         else // it must be the presentationContextID / indirectReference INTEGER
1836             throw new ASN1ValueException
1837             (
1838                 "This exception was thrown because you attempted to encode an " ~
1839                 "EXTERNAL that used something other than syntax as the CHOICE " ~
1840                 "of identification, which is not permitted when using " ~
1841                 "Canonical Encoding Rules (CER). " ~
1842                 notWhatYouMeantText ~ forMoreInformationText ~
1843                 debugInformationText ~ reportBugsText
1844             );
1845 
1846         CERElement dataValueDescriptor = new CERElement();
1847         dataValueDescriptor.tagNumber = ASN1UniversalType.objectDescriptor;
1848         dataValueDescriptor.objectDescriptor = value.dataValueDescriptor;
1849         components ~= dataValueDescriptor;
1850 
1851         CERElement dataValue = new CERElement();
1852         dataValue.tagClass = ASN1TagClass.contextSpecific;
1853         dataValue.tagNumber = value.encoding;
1854         dataValue.value = value.dataValue.dup;
1855 
1856         components ~= dataValue;
1857         this.sequence = components;
1858     }
1859 
1860     /*
1861         Since a CER-encoded EXTERNAL can only use the syntax field for
1862         the CHOICE of identification, this unit test ensures that an
1863         exception if thrown if an alternative identification is supplied.
1864     */
1865     @system
1866     unittest
1867     {
1868         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
1869         id.presentationContextID = 27L;
1870 
1871         External input = External();
1872         input.identification = id;
1873         input.dataValueDescriptor = "external";
1874         input.dataValue = [ 0x01u, 0x02u, 0x03u, 0x04u ];
1875 
1876         CERElement el = new CERElement();
1877         assertThrown!ASN1ValueException(el.external = input);
1878     }
1879 
1880     // Inspired by CVE-2017-9023
1881     @system
1882     unittest
1883     {
1884         ubyte[] external = [ // This is valid
1885             0x08u, 0x09u, // EXTERNAL, Length 9
1886                 0x02u, 0x01u, 0x1Bu, // INTEGER 27
1887                 0x81u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u // OCTET STRING 1,2,3,4
1888         ];
1889 
1890         // Valid values for octet[2]: 02 06
1891         for (ubyte i = 0x07u; i < 0x1Eu; i++)
1892         {
1893             external[2] = i;
1894             size_t x = 0u;
1895             CERElement el = new CERElement(x, external);
1896             assertThrown!ASN1Exception(el.external);
1897         }
1898 
1899         // Valid values for octet[5]: 80 - 82 (Anything else is an invalid value)
1900         for (ubyte i = 0x82u; i < 0x9Eu; i++)
1901         {
1902             external[5] = i;
1903             size_t x = 0u;
1904             CERElement el = new CERElement(x, external);
1905             assertThrown!ASN1Exception(el.external);
1906         }
1907     }
1908 
1909     // Assert that duplicate elements throw exceptions
1910     @system
1911     unittest
1912     {
1913         ubyte[] external;
1914 
1915         external = [ // This is invalid
1916             0x08u, 0x0Cu, // EXTERNAL, Length 12
1917                 0x02u, 0x01u, 0x1Bu, // INTEGER 27
1918                 0x02u, 0x01u, 0x1Bu, // INTEGER 27
1919                 0x81, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u // OCTET STRING 1,2,3,4
1920         ];
1921         assertThrown!ASN1Exception((new CERElement(external)).external);
1922 
1923         external = [ // This is invalid
1924             0x08u, 0x0Eu, // EXTERNAL, Length 14
1925                 0x06u, 0x02u, 0x2Au, 0x03u, // OBJECT IDENTIFIER 1.2.3
1926                 0x06u, 0x02u, 0x2Au, 0x03u, // OBJECT IDENTIFIER 1.2.3
1927                 0x81, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u // OCTET STRING 1,2,3,4
1928         ];
1929         assertThrown!ASN1Exception((new CERElement(external)).external);
1930 
1931         external = [ // This is invalid
1932             0x08u, 0x12u, // EXTERNAL, Length 18
1933                 0x06u, 0x02u, 0x2Au, 0x03u, // OBJECT IDENTIFIER 1.2.3
1934                 0x07u, 0x02u, 0x45u, 0x45u, // ObjectDescriptor "EE"
1935                 0x07u, 0x02u, 0x45u, 0x45u, // ObjectDescriptor "EE"
1936                 0x81u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u // OCTET STRING 1,2,3,4
1937         ];
1938         assertThrown!ASN1Exception((new CERElement(external)).external);
1939 
1940         external = [ // This is invalid
1941             0x08u, 0x14u, // EXTERNAL, Length 20
1942                 0x06u, 0x02u, 0x2Au, 0x03u, // OBJECT IDENTIFIER 1.2.3
1943                 0x07u, 0x02u, 0x45u, 0x45u, // ObjectDescriptor "EE"
1944                 0x81u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u, // OCTET STRING 1,2,3,4
1945                 0x81u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u // OCTET STRING 1,2,3,4
1946         ];
1947         assertThrown!ASN1Exception((new CERElement(external)).external);
1948     }
1949 
1950     /**
1951         Decodes a floating-point type.
1952 
1953         Note that this method assumes that your machine uses
1954         $(LINK http://ieeexplore.ieee.org/document/4610935/, IEEE 754-2008)
1955         floating point format.
1956 
1957         Throws:
1958         $(UL
1959             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
1960             $(LI $(D ASN1TruncationException) if the value appears to be "cut off")
1961             $(LI $(D ConvException) if character-encoding cannot be converted to
1962                 the selected floating-point type, T)
1963             $(LI $(D ConvOverflowException) if the character-encoding encodes a
1964                 number that is too big for the selected floating-point
1965                 type to express)
1966             $(LI $(D ASN1ValueSizeException) if the binary-encoding contains fewer
1967                 bytes than the information byte purports, or if the
1968                 binary-encoded mantissa is too big to be expressed by an
1969                 unsigned long integer)
1970             $(LI $(D ASN1ValueException) if a complicated-form exponent or a
1971                 non-zero-byte mantissa encodes a zero or even number, of if a base-10
1972                 (character-encoded) REAL is has something wrong that is not
1973                 covered by $(D ASN1ValueCharactersException) or
1974                 $(D ASN1ValuePaddingException))
1975             $(LI $(D ASN1ValueUndefinedException) if both bits indicating the base in the
1976                 information byte of a binary-encoded $(MONO REAL)'s information byte
1977                 are set, which would indicate an invalid base, or if a special
1978                 value has been indicated that is not defined by the specification)
1979             $(LI $(D ASN1ValuePaddingException) if a base-10 (character-encoded)
1980                 REAL is encoded with leading zeroes, or trailing zeroes on the
1981                 mantissa, or if the mantissa is encoded on more than the minimal
1982                 necessary octets)
1983             $(LI $(D ASN1ValueCharactersException) if a base-10 (character-encoded)
1984                 REAL is encoded with characters that are not numeric, a decimal, E,
1985                 or plus or minus)
1986         )
1987 
1988         Citations:
1989         $(UL
1990             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
1991                 $(I ASN.1: Communication between Heterogeneous Systems),
1992                 Morgan Kaufmann, 2001, pp. 400-402.)
1993             $(LI $(LINK https://www.iso.org/standard/12285.html, ISO 6093))
1994         )
1995     */
1996     public @property @system
1997     T realNumber(T)() const
1998     if (isFloatingPoint!T)
1999     {
2000         if (this.construction != ASN1Construction.primitive)
2001             throw new ASN1ConstructionException
2002             (this.construction, "decode a REAL");
2003 
2004         if (this.value.length == 0u) return cast(T) 0.0;
2005         switch (this.value[0] & 0b11000000u)
2006         {
2007             case (0b01000000u):
2008             {
2009                 if (this.value[0] == ASN1SpecialRealValue.notANumber) return T.nan;
2010                 if (this.value[0] == ASN1SpecialRealValue.minusZero) return -0.0;
2011                 if (this.value[0] == ASN1SpecialRealValue.plusInfinity) return T.infinity;
2012                 if (this.value[0] == ASN1SpecialRealValue.minusInfinity) return -T.infinity;
2013                 throw new ASN1ValueUndefinedException
2014                 (
2015                     "This exception was thrown because you attempted to decode " ~
2016                     "a REAL whose information byte indicated a special value " ~
2017                     "not recognized by the specification. The only special " ~
2018                     "values recognized by the specification are PLUS-INFINITY, " ~
2019                     "MINUS-INFINITY, NOT-A-NUMBER, and minus zero, identified " ~
2020                     "by information bytes of 0x40, 0x41 0x42, 0x43 respectively. " ~
2021                     notWhatYouMeantText ~ forMoreInformationText ~
2022                     debugInformationText ~ reportBugsText
2023                 );
2024             }
2025             case (0b00000000u): // Character Encoding
2026             {
2027                 /* NOTE:
2028                     Specification X.690 lays out very strict standards for the
2029                     Canonical Encoding Rules (CER) and Distinguished Encoding
2030                     Rules (DER) base-10-encoded REAL.
2031 
2032                     The character encoding form must be NR3, from ISO 6093, but
2033                     with even more restrictions applied.
2034 
2035                     It must be encoded like so:
2036                     * No whitespace whatsoever
2037                     * No leading zeroes under any circumstance.
2038                     * No trailing zeroes under any circumstance.
2039                     * No plus sign unless exponent is 0.
2040 
2041                     A valid encoding looks like this: 22.E-5
2042                 */
2043                 import std.conv : to;
2044                 import std..string : indexOf;
2045 
2046                 immutable string invalidNR3RealMessage =
2047                     "This exception was thrown because you attempted to decode " ~
2048                     "a base-10 encoded REAL that was encoded with improper " ~
2049                     "format. When using Canonical Encoding Rules (CER) or " ~
2050                     "Distinguished Encoding Rules (DER), the base-10 encoded " ~
2051                     "REAL must be encoded in the NR3 format specified in " ~
2052                     "ISO 6093. Further, there may be no whitespace, no leading " ~
2053                     "zeroes, no trailing zeroes on the mantissa, before or " ~
2054                     "after the decimal point, and no plus sign should ever " ~
2055                     "appear, unless the exponent is 0, in which case, the " ~
2056                     "exponent should read '+0'. Further, there must be a " ~
2057                     "decimal point, immediately followed by a capital 'E'." ~
2058                     "Your problem, in this case, was that your encoded value ";
2059 
2060                 // Smallest possible is '#.E#'. Decimal is necessary.
2061                 if (this.value.length < 5u)
2062                     throw new ASN1ValueSizeException
2063                     (5u, size_t.max, this.value.length, "decode a base-10 encoded REAL");
2064 
2065                 if (this.value[0] != 0b00000011u)
2066                     throw new ASN1ValueException
2067                     (invalidNR3RealMessage ~ "was not NR3 format at all.");
2068 
2069                 string valueString = cast(string) this.value[1 .. $];
2070 
2071                 foreach (character; valueString)
2072                 {
2073                     import std.ascii : isWhite;
2074                     if
2075                     (
2076                         character.isWhite ||
2077                         character == ',' ||
2078                         character == '_'
2079                     )
2080                         throw new ASN1ValueCharactersException
2081                         ("1234567890+-.E", character, "decode a base-10 encoded REAL");
2082                 }
2083 
2084                 if
2085                 (
2086                     valueString[0] == '0' ||
2087                     (valueString[0] == '-' && valueString[1] == '0')
2088                 )
2089                     throw new ASN1ValuePaddingException
2090                     (invalidNR3RealMessage ~ "contained a leading zero.");
2091 
2092                 ptrdiff_t indexOfDecimalPoint = valueString.indexOf(".");
2093                 if (indexOfDecimalPoint == -1)
2094                     throw new ASN1ValueException
2095                     (invalidNR3RealMessage ~ "contained no decimal point.");
2096 
2097                 if (valueString[indexOfDecimalPoint+1] != 'E')
2098                     throw new ASN1ValueException
2099                     (invalidNR3RealMessage ~ "contained no 'E'.");
2100 
2101                 if (valueString[indexOfDecimalPoint-1] == '0')
2102                     throw new ASN1ValuePaddingException
2103                     (invalidNR3RealMessage ~ "contained a trailing zero on the mantissa.");
2104 
2105                 if (valueString[$-2 .. $] != "+0" && canFind(valueString, '+'))
2106                     throw new ASN1ValueException
2107                     (invalidNR3RealMessage ~ "contained an illegitimate plus sign.");
2108 
2109                 if (canFind(valueString, "E0") || canFind(valueString, "E-0"))
2110                     throw new ASN1ValuePaddingException
2111                     (invalidNR3RealMessage ~ "contained a leading zero on the exponent.");
2112 
2113                 return to!(T)(valueString);
2114             }
2115             case 0b10000000u, 0b11000000u: // Binary Encoding
2116             {
2117                 ulong mantissa;
2118                 short exponent;
2119                 ubyte scale;
2120                 ubyte base;
2121                 size_t startOfMantissa;
2122 
2123                 switch (this.value[0] & 0b00000011u)
2124                 {
2125                     case 0b00000000u: // Exponent on the following octet
2126                     {
2127                         if (this.value.length < 3u)
2128                             throw new ASN1TruncationException
2129                             (3u, this.value.length, "decode a REAL exponent");
2130 
2131                         exponent = cast(short) cast(byte) this.value[1];
2132                         startOfMantissa = 2u;
2133                         break;
2134                     }
2135                     case 0b00000001u: // Exponent on the following two octets
2136                     {
2137                         if (this.value.length < 4u)
2138                             throw new ASN1TruncationException
2139                             (4u, this.value.length, "decode a REAL exponent");
2140 
2141                         ubyte[] exponentBytes = this.value[1 .. 3].dup;
2142                         version (LittleEndian) exponentBytes = [ exponentBytes[1], exponentBytes[0] ];
2143                         exponent = *cast(short *) exponentBytes.ptr;
2144 
2145                         if (exponent <= byte.max && exponent >= byte.min)
2146                             throw new ASN1ValuePaddingException
2147                             (
2148                                 "This exception was thrown because you attempted " ~
2149                                 "to decode a binary-encoded REAL whose exponent " ~
2150                                 "was encoded on more bytes than necessary. This " ~
2151                                 "would not be a problem if you were using the " ~
2152                                 "Basic Encoding Rules (BER), but the Canonical " ~
2153                                 "Encoding Rules (CER) and Distinguished Encoding " ~
2154                                 "Rules (DER) require that the exponent be " ~
2155                                 "encoded on the fewest possible bytes. " ~
2156                                 notWhatYouMeantText ~ forMoreInformationText ~
2157                                 debugInformationText ~ reportBugsText
2158                             );
2159 
2160                         startOfMantissa = 3u;
2161                         break;
2162                     }
2163                     case 0b00000010u: // Exponent on the following three octets
2164                     case 0b00000011u: // Complicated
2165                     {
2166                         throw new ASN1ValueOverflowException
2167                         (
2168                             "This exception was thrown because, according to " ~
2169                             "section 11.3.1 of specification X.690, a REAL's " ~
2170                             "exponent must be encoded on the fewest possible " ~
2171                             "octets, but you attempted to decode one that was " ~
2172                             "either too big to fit in an IEEE 754 floating " ~
2173                             "point type, or would have had unnecessary leading " ~
2174                             "bytes if it could. "
2175                         );
2176                     }
2177                     default: assert(0, "Impossible binary exponent encoding on REAL type");
2178                 }
2179 
2180                 if (this.value.length - startOfMantissa > ulong.sizeof)
2181                     throw new ASN1ValueOverflowException
2182                     (
2183                         "This exception was thrown because you attempted to " ~
2184                         "decode a REAL whose mantissa was encoded on too many " ~
2185                         "bytes to decode to the largest unsigned integral data " ~
2186                         "type. "
2187                     );
2188 
2189                 ubyte[] mantissaBytes = this.value[startOfMantissa .. $].dup;
2190 
2191                 if (mantissaBytes[0] == 0x00u)
2192                     throw new ASN1ValuePaddingException
2193                     (
2194                         "This exception was thrown because you attempted to decode " ~
2195                         "a REAL mantissa that was encoded on more than the minimum " ~
2196                         "necessary bytes. " ~
2197                         notWhatYouMeantText ~ forMoreInformationText ~
2198                         debugInformationText ~ reportBugsText
2199                     );
2200 
2201                 while (mantissaBytes.length < ulong.sizeof)
2202                     mantissaBytes = (0x00u ~ mantissaBytes);
2203                 version (LittleEndian) reverse(mantissaBytes);
2204                 version (unittest) assert(mantissaBytes.length == ulong.sizeof);
2205                 mantissa = *cast(ulong *) mantissaBytes.ptr;
2206 
2207                 if (mantissa == 0u)
2208                     throw new ASN1ValueException
2209                     (
2210                         "This exception was thrown because you attempted to " ~
2211                         "decode a REAL that was encoded on more than zero " ~
2212                         "bytes, but whose mantissa encoded a zero. This " ~
2213                         "is prohibited by specification X.690. If the " ~
2214                         "abstract value encoded is a real number of zero, " ~
2215                         "the REAL must be encoded upon zero bytes. " ~
2216                         notWhatYouMeantText ~ forMoreInformationText ~
2217                         debugInformationText ~ reportBugsText
2218                     );
2219 
2220                 switch (this.value[0] & 0b00110000u)
2221                 {
2222                     case (0b00000000u):
2223                     {
2224                         /* NOTE:
2225                             Section 11.3.1 of X.690 states that, for Canonical Encoding Rules
2226                             (CER) and Distinguished Encoding Rules (DER), the mantissa must be
2227                             zero or odd.
2228                         */
2229                         if (!(mantissa & 1u))
2230                             throw new ASN1ValueException
2231                             (
2232                                 "This exception was thrown because you attempted to " ~
2233                                 "decode a base-2 encoded REAL whose mantissa was " ~
2234                                 "not zero or odd. Both Canonical Encoding Rules (CER) " ~
2235                                 "and Distinguished Encoding Rules (DER) require that " ~
2236                                 "a base-2 encoded REAL's mantissa be shifted so that " ~
2237                                 "it is either zero or odd. "
2238                             );
2239 
2240                         base = 0x02u;
2241                         break;
2242                     }
2243                     case (0b00010000u): base = 0x08u; break;
2244                     case (0b00100000u): base = 0x10u; break;
2245                     default:
2246                         throw new ASN1ValueUndefinedException
2247                         (
2248                             "This exception was thrown because you attempted to " ~
2249                             "decode a REAL that had both base bits in the " ~
2250                             "information block set, the meaning of which is " ~
2251                             "not specified. " ~
2252                             notWhatYouMeantText ~ forMoreInformationText ~
2253                             debugInformationText ~ reportBugsText
2254                         );
2255                 }
2256 
2257                 if (this.value[0] & 0b00001100u)
2258                     throw new ASN1ValueException
2259                     (
2260                         "This exception was thrown because you attempted to " ~
2261                         "decode a REAL whose scale was not zero. This would " ~
2262                         "not be a problem if you were using the Basic " ~
2263                         "Encoding Rules (BER), but specification X.690 " ~
2264                         "says that, when using the Canonical Encoding Rules " ~
2265                         "(CER) or Distinguished Encoding Rules (DER), the " ~
2266                         "scale must be zero. " ~
2267                         notWhatYouMeantText ~ forMoreInformationText ~
2268                         debugInformationText ~ reportBugsText
2269                     );
2270 
2271                 /*
2272                     For some reason that I have yet to discover, you must
2273                     cast the exponent to T. If you do not, specifically
2274                     any usage of realNumber!T() outside of this library will
2275                     produce a "floating point exception 8" message and
2276                     crash. For some reason, all of the tests pass within
2277                     this library without doing this.
2278                 */
2279                 return (
2280                     ((this.value[0] & 0b01000000u) ? -1.0 : 1.0) *
2281                     cast(T) mantissa *
2282                     2^^scale *
2283                     (cast(T) base)^^(cast(T) exponent) // base must be cast
2284                 );
2285             }
2286             default: assert(0, "Impossible information byte value appeared!");
2287         }
2288     }
2289 
2290     /**
2291         Encodes a floating-point type, using base-2 binary encoding.
2292 
2293         Note that this method assumes that your machine uses
2294         $(LINK http://ieeexplore.ieee.org/document/4610935/, IEEE 754-2008)
2295         floating point format.
2296 
2297         Citations:
2298         $(UL
2299             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
2300                 $(I ASN.1: Communication between Heterogeneous Systems),
2301                 Morgan Kaufmann, 2001, pp. 400-402.)
2302             $(LI $(LINK https://www.iso.org/standard/12285.html, ISO 6093))
2303         )
2304     */
2305     public @property @system nothrow
2306     void realNumber(T)(in T value)
2307     if (isFloatingPoint!T)
2308     {
2309         scope(success) this.construction = ASN1Construction.primitive;
2310         /* NOTE:
2311             You must use isIdentical() to compare FP types to negative zero,
2312             because the basic == operator does not distinguish between zero
2313             and negative zero.
2314 
2315             isNaN() must be used to compare NaNs, because comparison using ==
2316             does not work for that at all.
2317 
2318             Also, this cannot go in a switch statement, because FP types
2319             cannot be the switch value.
2320         */
2321         if (isIdentical(value, 0.0))
2322         {
2323             this.value = [];
2324             return;
2325         }
2326         else if (isIdentical(value, -0.0))
2327         {
2328             this.value = [ ASN1SpecialRealValue.minusZero ];
2329             return;
2330         }
2331         else if (value.isNaN)
2332         {
2333             this.value = [ ASN1SpecialRealValue.notANumber ];
2334             return;
2335         }
2336         else if (value == T.infinity)
2337         {
2338             this.value = [ ASN1SpecialRealValue.plusInfinity ];
2339             return;
2340         }
2341         else if (value == -T.infinity)
2342         {
2343             this.value = [ ASN1SpecialRealValue.minusInfinity ];
2344             return;
2345         }
2346 
2347         real realValue = cast(real) value;
2348         bool positive = true;
2349         ulong mantissa;
2350         short exponent;
2351 
2352         /*
2353             Per the IEEE specifications, the exponent of a floating-point
2354             type is stored with a bias, meaning that the exponent counts
2355             up from a negative number, the reaches zero at the bias. We
2356             subtract the bias from the raw binary exponent to get the
2357             actual exponent encoded in the IEEE floating-point number.
2358             In the case of an x86 80-bit extended-precision floating-point
2359             type, the bias is 16383. In the case of double-precision, it is
2360             1023. For single-precision, it is 127.
2361 
2362             We then subtract the number of bits in the fraction from the
2363             exponent, which is equivalent to having had multiplied the
2364             fraction enough to have made it an integer represented by the
2365             same sequence of bits.
2366         */
2367         ubyte[] realBytes;
2368         realBytes.length = real.sizeof;
2369         *cast(real *)&realBytes[0] = realValue;
2370 
2371         version (BigEndian)
2372         {
2373             static if (real.sizeof > 10u) realBytes = realBytes[real.sizeof-10 .. $];
2374             positive = ((realBytes[0] & 0x80u) ? false : true);
2375         }
2376         else version (LittleEndian)
2377         {
2378             static if (real.sizeof > 10u) realBytes.length = 10u;
2379             positive = ((realBytes[$-1] & 0x80u) ? false : true);
2380         }
2381         else assert(0, "Could not determine endianness");
2382 
2383         static if (real.mant_dig == 64) // x86 Extended Precision
2384         {
2385             version (BigEndian)
2386             {
2387                 exponent = (((*cast(short *) &realBytes[0]) & 0x7FFF) - 16383 - 63); // 16383 is the bias
2388                 mantissa = *cast(ulong *) &realBytes[2];
2389             }
2390             else version (LittleEndian)
2391             {
2392                 exponent = (((*cast(short *) &realBytes[8]) & 0x7FFF) - 16383 - 63); // 16383 is the bias
2393                 mantissa = *cast(ulong *) &realBytes[0];
2394             }
2395             else assert(0, "Could not determine endianness");
2396         }
2397         else if (T.mant_dig == 53) // Double Precision
2398         {
2399             /*
2400                 The IEEE 754 double-precision floating point type only stores
2401                 the fractional part of the mantissa, because there is an
2402                 implicit 1 prior to the fractional part. To retrieve the actual
2403                 mantissa encoded, we flip the bit that comes just before the
2404                 most significant bit of the fractional part of the number.
2405             */
2406             version (BigEndian)
2407             {
2408                 exponent = (((*cast(short *) &realBytes[0]) & 0x7FFF) - 1023 - 53); // 1023 is the bias
2409                 mantissa = (((*cast(ulong *) &realBytes[2]) & 0x000FFFFFFFFFFFFFu) | 0x0010000000000000u);
2410             }
2411             else version (LittleEndian)
2412             {
2413                 exponent = (((*cast(short *) &realBytes[8]) & 0x7FFF) - 1023 - 53); // 1023 is the bias
2414                 mantissa = (((*cast(ulong *) &realBytes[0]) & 0x000FFFFFFFFFFFFFu) | 0x0010000000000000u);
2415             }
2416             else assert(0, "Could not determine endianness");
2417         }
2418         else if (T.mant_dig == 24) // Single Precision
2419         {
2420             /*
2421                 The IEEE 754 single-precision floating point type only stores
2422                 the fractional part of the mantissa, because there is an
2423                 implicit 1 prior to the fractional part. To retrieve the actual
2424                 mantissa encoded, we flip the bit that comes just before the
2425                 most significant bit of the fractional part of the number.
2426             */
2427             version (BigEndian)
2428             {
2429                 exponent = ((((*cast(short *) &realBytes[0]) & 0x7F80) >> 7) - 127 - 23); // 127 is the bias
2430                 mantissa = cast(ulong) (((*cast(uint *) &realBytes[2]) & 0x007FFFFFu) | 0x00800000u);
2431             }
2432             else version (LittleEndian)
2433             {
2434                 exponent = ((((*cast(short *) &realBytes[8]) & 0x7F80) >> 7) - 127 - 23); // 127 is the bias
2435                 mantissa = cast(ulong) (((*cast(uint *) &realBytes[0]) & 0x007FFFFFu) | 0x00800000u);
2436             }
2437             else assert(0, "Could not determine endianness");
2438         }
2439         else assert(0, "Unrecognized real floating-point format.");
2440 
2441         /* NOTE:
2442             Section 11.3.1 of X.690 states that, for Canonical Encoding Rules
2443             (CER) and Distinguished Encoding Rules (DER), the mantissa must be
2444             zero or odd.
2445         */
2446         if (mantissa != 0u)
2447         {
2448             while (!(mantissa & 1u))
2449             {
2450                 mantissa >>= 1;
2451                 exponent++;
2452             }
2453             version(unittest) assert(mantissa & 1u);
2454         }
2455 
2456         ubyte[] exponentBytes;
2457         exponentBytes.length = short.sizeof;
2458         *cast(short *)exponentBytes.ptr = exponent;
2459         version (LittleEndian) exponentBytes = [ exponentBytes[1], exponentBytes[0] ]; // Manual reversal (optimization)
2460         if
2461         (
2462             (exponentBytes[0] == 0x00u && (!(exponentBytes[1] & 0x80u))) || // Unnecessary positive leading bytes
2463             (exponentBytes[0] == 0xFFu && (exponentBytes[1] & 0x80u)) // Unnecessary negative leading bytes
2464         )
2465             exponentBytes = exponentBytes[1 .. 2];
2466 
2467         ubyte[] mantissaBytes;
2468         mantissaBytes.length = ulong.sizeof;
2469         *cast(ulong *)mantissaBytes.ptr = cast(ulong) mantissa;
2470         version (LittleEndian) reverse(mantissaBytes);
2471 
2472         size_t startOfNonPadding = 0u;
2473         for (size_t i = 0u; i < mantissaBytes.length-1; i++)
2474         {
2475             if (mantissaBytes[i] != 0x00u) break;
2476             startOfNonPadding++;
2477         }
2478         mantissaBytes = mantissaBytes[startOfNonPadding .. $];
2479 
2480         ubyte infoByte =
2481             0x80u | // First bit gets set for base2, base8, or base16 encoding
2482             (positive ? 0x00u : 0x40u) | // 1 = negative, 0 = positive
2483             // Scale = 0
2484             cast(ubyte) (exponentBytes.length == 1u ?
2485                 ASN1RealExponentEncoding.followingOctet :
2486                 ASN1RealExponentEncoding.following2Octets);
2487 
2488         this.value = (infoByte ~ exponentBytes ~ mantissaBytes);
2489     }
2490 
2491     @system
2492     unittest
2493     {
2494         CERElement el = new CERElement();
2495 
2496         // float
2497         el.realNumber!float = cast(float) float.nan;
2498         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2499         el.realNumber!double = cast(double) float.nan;
2500         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2501         el.realNumber!real = cast(real) float.nan;
2502         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2503 
2504         el.realNumber!float = cast(float) 0.0;
2505         assert(el.value == []);
2506         el.realNumber!double = cast(float) 0.0;
2507         assert(el.value == []);
2508         el.realNumber!real = cast(float) 0.0;
2509         assert(el.value == []);
2510 
2511         el.realNumber!float = cast(float) -0.0;
2512         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2513         el.realNumber!double = cast(float) -0.0;
2514         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2515         el.realNumber!real = cast(float) -0.0;
2516         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2517 
2518         el.realNumber!float = cast(float) float.infinity;
2519         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2520         el.realNumber!double = cast(double) float.infinity;
2521         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2522         el.realNumber!real = cast(real) float.infinity;
2523         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2524 
2525         el.realNumber!float = cast(float) -float.infinity;
2526         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2527         el.realNumber!double = cast(double) -float.infinity;
2528         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2529         el.realNumber!real = cast(real) -float.infinity;
2530         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2531 
2532         // double
2533         el.realNumber!float = cast(float) double.nan;
2534         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2535         el.realNumber!double = cast(double) double.nan;
2536         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2537         el.realNumber!real = cast(real) double.nan;
2538         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2539 
2540         el.realNumber!float = cast(double) 0.0;
2541         assert(el.value == []);
2542         el.realNumber!double = cast(double) 0.0;
2543         assert(el.value == []);
2544         el.realNumber!real = cast(double) 0.0;
2545         assert(el.value == []);
2546 
2547         el.realNumber!float = cast(double) -0.0;
2548         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2549         el.realNumber!double = cast(double) -0.0;
2550         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2551         el.realNumber!real = cast(double) -0.0;
2552         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2553 
2554         el.realNumber!float = cast(float) double.infinity;
2555         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2556         el.realNumber!double = cast(double) double.infinity;
2557         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2558         el.realNumber!real = cast(real) double.infinity;
2559         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2560 
2561         el.realNumber!float = cast(float) -double.infinity;
2562         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2563         el.realNumber!double = cast(double) -double.infinity;
2564         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2565         el.realNumber!real = cast(real) -double.infinity;
2566         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2567 
2568         // real
2569         el.realNumber!float = cast(float) real.nan;
2570         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2571         el.realNumber!double = cast(double) real.nan;
2572         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2573         el.realNumber!real = cast(real) real.nan;
2574         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.notANumber ]);
2575 
2576         el.realNumber!float = cast(real) 0.0;
2577         assert(el.value == []);
2578         el.realNumber!double = cast(real) 0.0;
2579         assert(el.value == []);
2580         el.realNumber!real = cast(real) 0.0;
2581         assert(el.value == []);
2582 
2583         el.realNumber!float = cast(real) -0.0;
2584         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2585         el.realNumber!double = cast(real) -0.0;
2586         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2587         el.realNumber!real = cast(real) -0.0;
2588         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusZero ]);
2589 
2590         el.realNumber!float = cast(float) real.infinity;
2591         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2592         el.realNumber!double = cast(double) real.infinity;
2593         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2594         el.realNumber!real = cast(real) real.infinity;
2595         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.plusInfinity ]);
2596 
2597         el.realNumber!float = cast(float) -real.infinity;
2598         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2599         el.realNumber!double = cast(double) -real.infinity;
2600         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2601         el.realNumber!real = cast(real) -real.infinity;
2602         assert(el.value == [ cast(ubyte) ASN1SpecialRealValue.minusInfinity ]);
2603     }
2604 
2605     // Positive Testing Base-10 (Character-Encoded) REALs
2606     @system
2607     unittest
2608     {
2609         immutable string[] tests = [
2610             "1.E1",
2611             "2.E10",
2612             "4.E100",
2613             "1.E-1",
2614             "2.E-10",
2615             "4.E-100",
2616             "-1.E1",
2617             "-2.E10",
2618             "-4.E100",
2619             "-1.E-1",
2620             "-2.E-10",
2621             "-4.E-100",
2622             "19.E1",
2623             "29.E10",
2624             "49.E100",
2625             "19.E-1",
2626             "29.E-10",
2627             "49.E-100",
2628             "-19.E1",
2629             "-29.E10",
2630             "-49.E100",
2631             "-19.E-1",
2632             "-29.E-10",
2633             "-49.E-100",
2634             "33.E+0"
2635         ];
2636 
2637         CERElement el = new CERElement();
2638 
2639         foreach (test; tests)
2640         {
2641             el.value = [ 0b00000011u ];
2642             el.value ~= cast(ubyte[]) test;
2643             assertNotThrown!ASN1ValueException(el.realNumber!float);
2644             assertNotThrown!ASN1ValueException(el.realNumber!double);
2645             assertNotThrown!ASN1ValueException(el.realNumber!real);
2646         }
2647     }
2648 
2649     // Negative Testing Base-10 (Character-Encoded) REALs
2650     @system
2651     unittest
2652     {
2653         immutable string[] tests = [
2654             " 1.E1", // Leading whitespace
2655             "1.E1 ", // Trailing whitespace
2656             "1 .E1", // Internal whitespace
2657             "1. E1", // Internal whitespace
2658             "1.E 1", // Internal whitespace
2659             "+1.E1", // Leading plus sign
2660             "01.E1", // Leading zero
2661             "10.E1", // Trailing zero
2662             "1.0E1", // Fractional zero
2663             "1.E+1", // Leading plus sign
2664             "1.E01", // Leading zero
2665             "1E100", // No decimal point
2666             "1.1",   // No 'E'
2667             ""       // Empty string
2668         ];
2669 
2670         CERElement el = new CERElement();
2671 
2672         foreach (test; tests)
2673         {
2674             el.value = [ 0b00000011u ];
2675             el.value ~= cast(ubyte[]) test;
2676             assertThrown!ASN1ValueException(el.realNumber!float);
2677             assertThrown!ASN1ValueException(el.realNumber!double);
2678             assertThrown!ASN1ValueException(el.realNumber!real);
2679         }
2680     }
2681 
2682     /**
2683         Decodes a signed integer, which represents a selection from an
2684         $(MONO ENUMERATION) of choices.
2685 
2686         Throws:
2687         $(UL
2688             $(LI $(D ASN1ConstructionException)
2689                 if the encoded value is not primitively-constructed)
2690             $(LI $(D ASN1ValueSizeException)
2691                 if the value is too big to decode to a signed integral type,
2692                 or if the value is zero bytes)
2693             $(LI $(D ASN1ValuePaddingException)
2694                 if there are padding bytes)
2695         )
2696     */
2697     public @property @system
2698     T enumerated(T)() const
2699     if (isIntegral!T && isSigned!T)
2700     {
2701         if (this.construction != ASN1Construction.primitive)
2702             throw new ASN1ConstructionException
2703             (this.construction, "decode an ENUMERATED");
2704 
2705         if (this.value.length == 1u)
2706             return cast(T) cast(byte) this.value[0];
2707 
2708         if (this.value.length == 0u || this.value.length > T.sizeof)
2709             throw new ASN1ValueSizeException
2710             (1u, long.sizeof, this.value.length, "decode an ENUMERATED");
2711 
2712         if
2713         (
2714             (this.value[0] == 0x00u && (!(this.value[1] & 0x80u))) || // Unnecessary positive leading bytes
2715             (this.value[0] == 0xFFu && (this.value[1] & 0x80u)) // Unnecessary negative leading bytes
2716         )
2717             throw new ASN1ValueException
2718             (
2719                 "This exception was thrown because you attempted to decode " ~
2720                 "an ENUMERATED that was encoded on more than the minimum " ~
2721                 "necessary bytes. " ~
2722                 notWhatYouMeantText ~ forMoreInformationText ~
2723                 debugInformationText ~ reportBugsText
2724             );
2725 
2726         /* NOTE:
2727             Because the ENUMERATED is stored in two's complement form, you
2728             can't just apppend 0x00u to the big end of it until it is as long
2729             as T in bytes, then cast to T. Instead, you have to first determine
2730             if the encoded integer is negative or positive. If it is negative,
2731             then you actually want to append 0xFFu to the big end until it is
2732             as big as T, so you get the two's complement form of whatever T
2733             you choose.
2734 
2735             The line immediately below this determines whether the padding byte
2736             should be 0xFF or 0x00 based on the most significant bit of the
2737             most significant byte (which, since CER encodes big-endian, will
2738             always be the first byte). If set (1), the number is negative, and
2739             hence, the padding byte should be 0xFF. If not, it is positive,
2740             and the padding byte should be 0x00.
2741         */
2742         immutable ubyte paddingByte = ((this.value[0] & 0x80u) ? 0xFFu : 0x00u);
2743         ubyte[] value = this.value.dup; // Duplication is necessary to prevent modifying the source bytes
2744         while (value.length < T.sizeof)
2745             value = (paddingByte ~ value);
2746         version (LittleEndian) reverse(value);
2747         version (unittest) assert(value.length == T.sizeof);
2748         return *cast(T *) value.ptr;
2749     }
2750 
2751     /// Encodes an $(MONO ENUMERATED) type from an integer.
2752     public @property @system nothrow
2753     void enumerated(T)(in T value)
2754     out
2755     {
2756         assert(this.value.length > 0u);
2757     }
2758     do
2759     {
2760         scope(success) this.construction = ASN1Construction.primitive;
2761         if (value <= byte.max && value >= byte.min)
2762         {
2763             this.value = [ cast(ubyte) cast(byte) value ];
2764             return;
2765         }
2766 
2767         ubyte[] ub;
2768         ub.length = T.sizeof;
2769         *cast(T *)&ub[0] = value;
2770         version (LittleEndian) reverse(ub);
2771 
2772         /*
2773             An ENUMERATED must be encoded on the fewest number of bytes than can
2774             encode it. The loops below identify how many bytes can be
2775             truncated from the start of the ENUMERATED, with one loop for positive
2776             and another loop for negative numbers. ENUMERATED is encoded in the
2777             same exact way that INTEGER is encoded.
2778 
2779             From X.690, Section 8.3.2:
2780 
2781             If the contents octets of an integer value encoding consist of more
2782             than one octet, then the bits of the first octet and bit 8 of the
2783             second octet:
2784                 a) shall not all be ones; and
2785                 b) shall not all be zero.
2786                 NOTE – These rules ensure that an integer value is always
2787                 encoded in the smallest possible number of octets.
2788         */
2789         size_t startOfNonPadding = 0u;
2790         if (T.sizeof > 1u)
2791         {
2792             if (value >= 0)
2793             {
2794                 for (size_t i = 0u; i < ub.length-1; i++)
2795                 {
2796                     if (ub[i] != 0x00u) break;
2797                     if (!(ub[i+1] & 0x80u)) startOfNonPadding++;
2798                 }
2799             }
2800             else
2801             {
2802                 for (size_t i = 0u; i < ub.length-1; i++)
2803                 {
2804                     if (ub[i] != 0xFFu) break;
2805                     if (ub[i+1] & 0x80u) startOfNonPadding++;
2806                 }
2807             }
2808         }
2809 
2810         this.value = ub[startOfNonPadding .. $];
2811     }
2812 
2813     // Ensure that ENUMERATED 0 gets encoded on a single null byte.
2814     @system
2815     unittest
2816     {
2817         CERElement el = new CERElement();
2818 
2819         el.enumerated!byte = cast(byte) 0x00;
2820         assert(el.value == [ 0x00u ]);
2821 
2822         el.enumerated!short = cast(short) 0x0000;
2823         assert(el.value == [ 0x00u ]);
2824 
2825         el.enumerated!int = cast(int) 0;
2826         assert(el.value == [ 0x00u ]);
2827 
2828         el.enumerated!long = cast(long) 0;
2829         assert(el.value == [ 0x00u ]);
2830 
2831         el.value = [];
2832         assertThrown!ASN1ValueSizeException(el.enumerated!byte);
2833         assertThrown!ASN1ValueSizeException(el.enumerated!short);
2834         assertThrown!ASN1ValueSizeException(el.enumerated!int);
2835         assertThrown!ASN1ValueSizeException(el.enumerated!long);
2836     }
2837 
2838     // Test encoding -0 for the sake of CVE-2016-2108
2839     @system
2840     unittest
2841     {
2842         CERElement el = new CERElement();
2843 
2844         el.enumerated!byte = -0;
2845         assertNotThrown!RangeError(el.enumerated!byte);
2846         assertNotThrown!ASN1Exception(el.enumerated!byte);
2847 
2848         el.enumerated!short = -0;
2849         assertNotThrown!RangeError(el.enumerated!short);
2850         assertNotThrown!ASN1Exception(el.enumerated!short);
2851 
2852         el.enumerated!int = -0;
2853         assertNotThrown!RangeError(el.enumerated!int);
2854         assertNotThrown!ASN1Exception(el.enumerated!int);
2855 
2856         el.enumerated!long = -0;
2857         assertNotThrown!RangeError(el.enumerated!long);
2858         assertNotThrown!ASN1Exception(el.enumerated!long);
2859     }
2860 
2861     /**
2862         Decodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
2863             the $(LINK https://www.itu.int, International Telecommunications Union)'s
2864             $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2865 
2866         The specification defines $(MONO EmbeddedPDV) as:
2867 
2868         $(PRE
2869             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2870                 identification CHOICE {
2871                     syntaxes SEQUENCE {
2872                         abstract OBJECT IDENTIFIER,
2873                         transfer OBJECT IDENTIFIER },
2874                     syntax OBJECT IDENTIFIER,
2875                     presentation-context-id INTEGER,
2876                     context-negotiation SEQUENCE {
2877                         presentation-context-id INTEGER,
2878                         transfer-syntax OBJECT IDENTIFIER },
2879                     transfer-syntax OBJECT IDENTIFIER,
2880                     fixed NULL },
2881                 data-value-descriptor ObjectDescriptor OPTIONAL,
2882                 data-value OCTET STRING }
2883             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
2884         )
2885 
2886         This assumes $(MONO AUTOMATIC TAGS), so all of the $(MONO identification)
2887         choices will be $(MONO CONTEXT-SPECIFIC) and numbered from 0 to 5.
2888 
2889         The following additional constraints are applied to the abstract syntax
2890         when using Canonical Encoding Rules or Distinguished Encoding Rules,
2891         which are also defined in the
2892         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
2893         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules):
2894 
2895         $(PRE
2896             EmbeddedPDV ( WITH COMPONENTS {
2897                 ... ,
2898                 identification ( WITH COMPONENTS {
2899                     ... ,
2900                     presentation-context-id ABSENT,
2901                     context-negotiation ABSENT } ) } )
2902         )
2903 
2904         The stated purpose of the constraints shown above is to restrict the use of
2905         the $(MONO presentation-context-id), either by itself or within the
2906         context-negotiation, which makes the following the effective abstract
2907         syntax of $(MONO EmbeddedPDV) when using Canonical Encoding Rules or
2908         Distinguished Encoding Rules:
2909 
2910         $(PRE
2911             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2912                 identification CHOICE {
2913                     syntaxes SEQUENCE {
2914                         abstract OBJECT IDENTIFIER,
2915                         transfer OBJECT IDENTIFIER },
2916                     syntax OBJECT IDENTIFIER,
2917                     presentation-context-id INTEGER,
2918                     context-negotiation SEQUENCE {
2919                         presentation-context-id INTEGER,
2920                         transfer-syntax OBJECT IDENTIFIER },
2921                     transfer-syntax OBJECT IDENTIFIER,
2922                     fixed NULL },
2923                 data-value-descriptor ObjectDescriptor OPTIONAL,
2924                 data-value OCTET STRING }
2925                     ( WITH COMPONENTS {
2926                         ... ,
2927                         identification ( WITH COMPONENTS {
2928                             ... ,
2929                             presentation-context-id ABSENT,
2930                             context-negotiation ABSENT } ) } )
2931         )
2932 
2933         With the constraints applied, the abstract syntax for $(MONO EmbeddedPDV)s encoded
2934         using Canonical Encoding Rules or Distinguished Encoding Rules becomes:
2935 
2936         $(PRE
2937             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2938                 identification CHOICE {
2939                     syntaxes SEQUENCE {
2940                         abstract OBJECT IDENTIFIER,
2941                         transfer OBJECT IDENTIFIER },
2942                     syntax OBJECT IDENTIFIER,
2943                     transfer-syntax OBJECT IDENTIFIER,
2944                     fixed NULL },
2945                 data-value-descriptor ObjectDescriptor OPTIONAL,
2946                 data-value OCTET STRING }
2947         )
2948 
2949         Returns: an instance of $(D types.universal.embeddedpdv.EmbeddedPDV)
2950 
2951         Throws:
2952         $(UL
2953             $(LI $(D ASN1ValueException) if encoded EmbeddedPDV has too few or too many
2954                 elements, or if syntaxes or context-negotiation element has
2955                 too few or too many elements)
2956             $(LI $(D ASN1ValueSizeException) if encoded INTEGER is too large to decode)
2957             $(LI $(D ASN1RecursionException) if using constructed form and the element
2958                 is constructed of too many nested constructed elements)
2959             $(LI $(D ASN1TagClassException) if any nested primitives do not have the
2960                 correct tag class)
2961             $(LI $(D ASN1ConstructionException) if any element has the wrong construction)
2962             $(LI $(D ASN1TagNumberException) if any nested primitives do not have the
2963                 correct tag number)
2964         )
2965     */
2966     override public @property @system
2967     EmbeddedPDV embeddedPresentationDataValue() const
2968     {
2969         if (this.construction != ASN1Construction.constructed)
2970             throw new ASN1ConstructionException
2971             (this.construction, "decode an EmbeddedPDV");
2972 
2973         const CERElement[] components = this.sequence;
2974         ASN1ContextSwitchingTypeID identification = ASN1ContextSwitchingTypeID();
2975 
2976         if (components.length != 2u)
2977             throw new ASN1ValueException
2978             (
2979                 "This exception was thrown because you attempted to decode " ~
2980                 "an EmbeddedPDV that contained too many or too few elements. " ~
2981                 "An EmbeddedPDV should have only two elements: " ~
2982                 "an identification CHOICE, and a data-value OCTET STRING, " ~
2983                 "in that order. " ~
2984                 notWhatYouMeantText ~ forMoreInformationText ~
2985                 debugInformationText ~ reportBugsText
2986             );
2987 
2988         if (components[0].tagClass != ASN1TagClass.contextSpecific)
2989             throw new ASN1TagClassException
2990             (
2991                 [ ASN1TagClass.contextSpecific ],
2992                 components[0].tagClass,
2993                 "decode the first component of an EmbeddedPDV"
2994             );
2995 
2996         if (components[1].tagClass != ASN1TagClass.contextSpecific)
2997             throw new ASN1TagClassException
2998             (
2999                 [ ASN1TagClass.contextSpecific ],
3000                 components[1].tagClass,
3001                 "decode the second component of an EmbeddedPDV"
3002             );
3003 
3004         /* NOTE:
3005             See page 224 of Dubuisson, item 11:
3006             It sounds like, even if you have an ABSENT constraint applied,
3007             all automatically-tagged items still have the same numbers as
3008             though the constrained component were PRESENT.
3009         */
3010         if (components[0].tagNumber != 0u)
3011             throw new ASN1TagNumberException
3012             ([ 0u ], components[0].tagNumber, "decode the first component of an EmbeddedPDV");
3013 
3014         if (components[1].tagNumber != 2u)
3015             throw new ASN1TagNumberException
3016             ([ 2u ], components[1].tagNumber, "decode the second component of an EmbeddedPDV");
3017 
3018 
3019         ubyte[] bytes = components[0].value.dup;
3020         const CERElement identificationChoice = new CERElement(bytes);
3021         switch (identificationChoice.tagNumber)
3022         {
3023             case (0u): // syntaxes
3024             {
3025                 if (identificationChoice.construction != ASN1Construction.constructed)
3026                     throw new ASN1ConstructionException
3027                     (identificationChoice.construction, "decode the syntaxes component of an EmbeddedPDV");
3028 
3029                 const CERElement[] syntaxesComponents = identificationChoice.sequence;
3030 
3031                 if (syntaxesComponents.length != 2u)
3032                     throw new ASN1ValueException
3033                     (
3034                         "This exception was thrown because you attempted to " ~
3035                         "decode an EmbeddedPDV whose syntaxes component " ~
3036                         "contained an invalid number of elements. The " ~
3037                         "syntaxes component should contain abstract and transfer " ~
3038                         "syntax OBJECT IDENTIFIERS, in that order. " ~
3039                         notWhatYouMeantText ~ forMoreInformationText ~
3040                         debugInformationText ~ reportBugsText
3041                     );
3042 
3043                 // Class Validation
3044                 if (syntaxesComponents[0].tagClass != ASN1TagClass.contextSpecific)
3045                     throw new ASN1TagClassException
3046                     (
3047                         [ ASN1TagClass.contextSpecific ],
3048                         syntaxesComponents[0].tagClass,
3049                         "decode the first syntaxes component of an EmbeddedPDV"
3050                     );
3051 
3052                 if (syntaxesComponents[1].tagClass != ASN1TagClass.contextSpecific)
3053                     throw new ASN1TagClassException
3054                     (
3055                         [ ASN1TagClass.contextSpecific ],
3056                         syntaxesComponents[1].tagClass,
3057                         "decode the second syntaxes component of an EmbeddedPDV"
3058                     );
3059 
3060                 // Construction Validation
3061                 if (syntaxesComponents[0].construction != ASN1Construction.primitive)
3062                     throw new ASN1ConstructionException
3063                     (syntaxesComponents[0].construction, "decode the first syntaxes component of an EmbeddedPDV");
3064 
3065                 if (syntaxesComponents[1].construction != ASN1Construction.primitive)
3066                     throw new ASN1ConstructionException
3067                     (syntaxesComponents[1].construction, "decode the second syntaxes component of an EmbeddedPDV");
3068 
3069                 // Number Validation
3070                 if (syntaxesComponents[0].tagNumber != 0u)
3071                     throw new ASN1TagNumberException
3072                     (
3073                         [ 0u ],
3074                         syntaxesComponents[0].tagNumber,
3075                         "decode the first syntaxes component of an EmbeddedPDV"
3076                     );
3077 
3078                 if (syntaxesComponents[1].tagNumber != 1u)
3079                     throw new ASN1TagNumberException
3080                     (
3081                         [ 1u ],
3082                         syntaxesComponents[1].tagNumber,
3083                         "decode the second syntaxes component of an EmbeddedPDV"
3084                     );
3085 
3086                 identification.syntaxes  = ASN1Syntaxes(
3087                     syntaxesComponents[0].objectIdentifier,
3088                     syntaxesComponents[1].objectIdentifier
3089                 );
3090 
3091                 break;
3092             }
3093             case (1u): // syntax
3094             {
3095                 identification.syntax = identificationChoice.objectIdentifier;
3096                 break;
3097             }
3098             case (4u): // transfer-syntax
3099             {
3100                 identification.transferSyntax = identificationChoice.objectIdentifier;
3101                 break;
3102             }
3103             case (5u): // fixed
3104             {
3105                 identification.fixed = true;
3106                 break;
3107             }
3108             default:
3109                 throw new ASN1TagNumberException
3110                 ([ 0u, 1u, 4u, 5u ], identificationChoice.tagNumber, "decode an EmbeddedPDV identification");
3111         }
3112 
3113         EmbeddedPDV pdv = EmbeddedPDV();
3114         pdv.identification = identification;
3115         pdv.dataValue = components[1].octetString;
3116         return pdv;
3117     }
3118 
3119     /**
3120         Encodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
3121             the $(LINK https://www.itu.int, International Telecommunications Union)'s
3122             $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
3123 
3124         The specification defines $(MONO EmbeddedPDV) as:
3125 
3126         $(PRE
3127             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
3128                 identification CHOICE {
3129                     syntaxes SEQUENCE {
3130                         abstract OBJECT IDENTIFIER,
3131                         transfer OBJECT IDENTIFIER },
3132                     syntax OBJECT IDENTIFIER,
3133                     presentation-context-id INTEGER,
3134                     context-negotiation SEQUENCE {
3135                         presentation-context-id INTEGER,
3136                         transfer-syntax OBJECT IDENTIFIER },
3137                     transfer-syntax OBJECT IDENTIFIER,
3138                     fixed NULL },
3139                 data-value-descriptor ObjectDescriptor OPTIONAL,
3140                 data-value OCTET STRING }
3141             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
3142         )
3143 
3144         This assumes $(MONO AUTOMATIC TAGS), so all of the $(MONO identification)
3145         choices will be $(MONO CONTEXT-SPECIFIC) and numbered from 0 to 5.
3146 
3147         The following additional constraints are applied to the abstract syntax
3148         when using Canonical Encoding Rules or Distinguished Encoding Rules,
3149         which are also defined in the
3150         $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
3151         $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules):
3152 
3153         $(PRE
3154             EmbeddedPDV ( WITH COMPONENTS {
3155                 ... ,
3156                 identification ( WITH COMPONENTS {
3157                     ... ,
3158                     presentation-context-id ABSENT,
3159                     context-negotiation ABSENT } ) } )
3160         )
3161 
3162         The stated purpose of the constraints shown above is to restrict the use of
3163         the $(MONO presentation-context-id), either by itself or within the
3164         context-negotiation, which makes the following the effective abstract
3165         syntax of $(MONO EmbeddedPDV) when using Canonical Encoding Rules or
3166         Distinguished Encoding Rules:
3167 
3168         $(PRE
3169             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
3170                 identification CHOICE {
3171                     syntaxes SEQUENCE {
3172                         abstract OBJECT IDENTIFIER,
3173                         transfer OBJECT IDENTIFIER },
3174                     syntax OBJECT IDENTIFIER,
3175                     presentation-context-id INTEGER,
3176                     context-negotiation SEQUENCE {
3177                         presentation-context-id INTEGER,
3178                         transfer-syntax OBJECT IDENTIFIER },
3179                     transfer-syntax OBJECT IDENTIFIER,
3180                     fixed NULL },
3181                 data-value-descriptor ObjectDescriptor OPTIONAL,
3182                 data-value OCTET STRING }
3183                     ( WITH COMPONENTS {
3184                         ... ,
3185                         identification ( WITH COMPONENTS {
3186                             ... ,
3187                             presentation-context-id ABSENT,
3188                             context-negotiation ABSENT } ) } )
3189         )
3190 
3191         With the constraints applied, the abstract syntax for $(MONO EmbeddedPDV)s encoded
3192         using Canonical Encoding Rules or Distinguished Encoding Rules becomes:
3193 
3194         $(PRE
3195             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
3196                 identification CHOICE {
3197                     syntaxes SEQUENCE {
3198                         abstract OBJECT IDENTIFIER,
3199                         transfer OBJECT IDENTIFIER },
3200                     syntax OBJECT IDENTIFIER,
3201                     transfer-syntax OBJECT IDENTIFIER,
3202                     fixed NULL },
3203                 data-value-descriptor ObjectDescriptor OPTIONAL,
3204                 data-value OCTET STRING }
3205         )
3206 
3207         If the supplied $(MONO identification) for the EmbeddedPDV is a
3208         $(MONO presentation-context-id) or a $(MONO context-negotiation),
3209         no exception will be thrown; the $(MONO identification) will be set to
3210         $(MONO fixed) silently.
3211 
3212         Throws:
3213         $(UL
3214             $(LI $(D ASN1ValueException) if encoded ObjectDescriptor contains invalid characters)
3215         )
3216     */
3217     override public @property @system
3218     void embeddedPresentationDataValue(in EmbeddedPDV value)
3219     out
3220     {
3221         assert(this.value.length > 0u);
3222     }
3223     do
3224     {
3225         scope(success) this.construction = ASN1Construction.constructed;
3226         CERElement identification = new CERElement();
3227         identification.tagClass = ASN1TagClass.contextSpecific;
3228         identification.tagNumber = 0u; // CHOICE is EXPLICIT, even with automatic tagging.
3229 
3230         CERElement identificationChoice = new CERElement();
3231         identificationChoice.tagClass = ASN1TagClass.contextSpecific;
3232         if (!(value.identification.syntaxes.isNull))
3233         {
3234             CERElement abstractSyntax = new CERElement();
3235             abstractSyntax.tagClass = ASN1TagClass.contextSpecific;
3236             abstractSyntax.tagNumber = 0u;
3237             abstractSyntax.objectIdentifier = value.identification.syntaxes.abstractSyntax;
3238 
3239             CERElement transferSyntax = new CERElement();
3240             transferSyntax.tagClass = ASN1TagClass.contextSpecific;
3241             transferSyntax.tagNumber = 1u;
3242             transferSyntax.objectIdentifier = value.identification.syntaxes.transferSyntax;
3243 
3244             identificationChoice.construction = ASN1Construction.constructed;
3245             identificationChoice.tagNumber = 0u;
3246             identificationChoice.sequence = [ abstractSyntax, transferSyntax ];
3247         }
3248         else if (!(value.identification.syntax.isNull))
3249         {
3250             identificationChoice.tagNumber = 1u;
3251             identificationChoice.objectIdentifier = value.identification.syntax;
3252         }
3253         else if (!(value.identification.transferSyntax.isNull))
3254         {
3255             identificationChoice.tagNumber = 4u;
3256             identificationChoice.objectIdentifier = value.identification.transferSyntax;
3257         }
3258         else
3259         {
3260             identificationChoice.tagNumber = 5u;
3261             identificationChoice.value = [];
3262         }
3263 
3264         // This makes identification: [CONTEXT 0][L][CONTEXT #][L][V]
3265         identification.value = cast(ubyte[]) identificationChoice;
3266 
3267         CERElement dataValue = new CERElement();
3268         dataValue.tagClass = ASN1TagClass.contextSpecific;
3269         dataValue.tagNumber = 2u;
3270         dataValue.octetString = value.dataValue;
3271 
3272         this.sequence = [ identification, dataValue ];
3273     }
3274 
3275     /* NOTE:
3276         This unit test had to be moved out of ASN1Element because CER and CER
3277         do not support encoding of presentation-context-id in EmbeddedPDV.
3278 
3279         This unit test ensures that, if you attempt to create an EmbeddedPDV
3280         with presentation-context-id as the CHOICE of identification, the
3281         encoded EmbeddedPDV's identification defaults to fixed.
3282     */
3283     @system
3284     unittest
3285     {
3286         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
3287         id.presentationContextID = 27L;
3288 
3289         EmbeddedPDV input = EmbeddedPDV();
3290         input.identification = id;
3291         input.dataValue = [ 0x01u, 0x02u, 0x03u, 0x04u ];
3292 
3293         CERElement el = new CERElement();
3294         el.tagNumber = 0x08u;
3295         el.embeddedPDV = input;
3296         EmbeddedPDV output = el.embeddedPDV;
3297         assert(output.identification.fixed == true);
3298         assert(output.dataValue == [ 0x01u, 0x02u, 0x03u, 0x04u ]);
3299     }
3300 
3301     /* NOTE:
3302         This unit test had to be moved out of ASN1Element because CER and CER
3303         do not support encoding of context-negotiation in EmbeddedPDV.
3304 
3305         This unit test ensures that, if you attempt to create an EmbeddedPDV
3306         with context-negotiation as the CHOICE of identification, the
3307         encoded EmbeddedPDV's identification defaults to fixed.
3308     */
3309     @system
3310     unittest
3311     {
3312         ASN1ContextNegotiation cn = ASN1ContextNegotiation();
3313         cn.presentationContextID = 27L;
3314         cn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
3315 
3316         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
3317         id.contextNegotiation = cn;
3318 
3319         EmbeddedPDV input = EmbeddedPDV();
3320         input.identification = id;
3321         input.dataValue = [ 0x13u, 0x15u, 0x17u, 0x19u ];
3322 
3323         CERElement el = new CERElement();
3324         el.embeddedPDV = input;
3325         EmbeddedPDV output = el.embeddedPDV;
3326         assert(output.identification.fixed == true);
3327         assert(output.dataValue == [ 0x13u, 0x15u, 0x17u, 0x19u ]);
3328     }
3329 
3330     // Inspired by CVE-2017-9023
3331     @system
3332     unittest
3333     {
3334         ubyte[] data = [ // This is valid.
3335             0x0Bu, 0x0Au, // EmbeddedPDV, Length 11
3336                 0x80u, 0x02u, // CHOICE
3337                     0x85u, 0x00u, // NULL
3338                 0x82u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u ]; // OCTET STRING
3339 
3340         // Valid values for data[2]: 80
3341         for (ubyte i = 0x81u; i < 0x9Eu; i++)
3342         {
3343             data[2] = i;
3344             size_t x = 0u;
3345             CERElement el = new CERElement(x, data);
3346             assertThrown!ASN1Exception(el.embeddedPDV);
3347         }
3348 
3349         // Valid values for data[4]: 80-85
3350         for (ubyte i = 0x86u; i < 0x9Eu; i++)
3351         {
3352             data[4] = i;
3353             size_t x = 0u;
3354             CERElement el = new CERElement(x, data);
3355             assertThrown!ASN1Exception(el.embeddedPDV);
3356         }
3357 
3358         // Valid values for data[6]: 82
3359         for (ubyte i = 0x83u; i < 0x9Eu; i++)
3360         {
3361             data[6] = i;
3362             size_t x = 0u;
3363             CERElement el = new CERElement(x, data);
3364             assertThrown!ASN1Exception(el.embeddedPDV);
3365         }
3366     }
3367 
3368     /**
3369         Decodes the value to UTF-8 characters.
3370 
3371         Throws:
3372         $(UL
3373             $(LI $(D UTF8Exception)
3374                 if the encoded value does not decode to UTF-8)
3375             $(LI $(D ASN1RecursionException)
3376                 if using constructed form and the element
3377                 is constructed of too many nested constructed elements)
3378             $(LI $(D ASN1TagClassException)
3379                 if any nested primitives do not share the
3380                 same tag class as their outer constructed element)
3381             $(LI $(D ASN1TagNumberException)
3382                 if any nested primitives do not share the
3383                 same tag number as their outer constructed element)
3384         )
3385     */
3386     override public @property @system
3387     string unicodeTransformationFormat8String() const
3388     {
3389         if (this.construction == ASN1Construction.primitive)
3390         {
3391             if (this.value.length > 1000u)
3392                 throw new ASN1ValueSizeException
3393                 (0u, 1000u, this.value.length, "decode a primitively-encoded UTF8String");
3394 
3395             return cast(string) this.value;
3396         }
3397         else
3398         {
3399             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
3400                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a UTF8String");
3401             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
3402 
3403             CERElement[] substrings = this.sequence;
3404             if (substrings.length < 2u)
3405                 throw new ASN1ValueException
3406                 (
3407                     "This exception was thrown because you attempted to decode " ~
3408                     "a constructed UTF8String that was constituted by fewer " ~
3409                     "than two nested elements. When using the Canonical Encoding " ~
3410                     "Rules (CER), a legitimate constructed UTF8String must " ~
3411                     "contain at least two nested elements, and each but the " ~
3412                     "last must contain exactly 1000 content octets. The last " ~
3413                     "nested element may have between 1 and 1000 content " ~
3414                     "octets inclusively. This element should have been encoded " ~
3415                     "primitively using definite-length encoding."
3416                 );
3417 
3418             foreach (substring; substrings[0 .. $-1])
3419             {
3420                 if (substring.length != 1000u)
3421                     throw new ASN1ValueSizeException
3422                     (1000u, 1000u, substring.length, "decode a substring of a constructed UTF8String");
3423             }
3424 
3425             if (substrings[$-1].length == 0u)
3426                 throw new ASN1ValueSizeException
3427                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed UTF8String");
3428 
3429             Appender!string appendy = appender!string();
3430             foreach (substring; substrings)
3431             {
3432                 if (substring.tagClass != this.tagClass)
3433                     throw new ASN1TagClassException
3434                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed UTF8String");
3435 
3436                 if (substring.construction != ASN1Construction.primitive)
3437                     throw new ASN1ConstructionException
3438                     (substring.construction, "decode a substring of a constructed UTF8String");
3439 
3440                 if (substring.tagNumber != this.tagNumber)
3441                     throw new ASN1TagNumberException
3442                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed UTF8String");
3443 
3444                 appendy.put(substring.utf8String);
3445             }
3446             return appendy.data;
3447         }
3448     }
3449 
3450     /// Encodes a UTF-8 string to bytes.
3451     override public @property @system
3452     void unicodeTransformationFormat8String(in string value)
3453     {
3454         if (value.length <= 1000u)
3455         {
3456             this.value = cast(ubyte[]) value.dup;
3457         }
3458         else
3459         {
3460             CERElement[] primitives;
3461             size_t i = 0u;
3462             while (i+1000u < value.length)
3463             {
3464                 CERElement x = new CERElement();
3465                 x.tagNumber = this.tagNumber;
3466                 x.construction = ASN1Construction.primitive;
3467                 x.value = cast(ubyte[]) value[i .. i+1000u];
3468                 primitives ~= x;
3469                 i += 1000u;
3470             }
3471 
3472             CERElement y = new CERElement();
3473             y.tagNumber = this.tagNumber;
3474             y.construction = ASN1Construction.primitive;
3475             y.value = cast(ubyte[]) value[i .. $];
3476             primitives ~= y;
3477 
3478             this.sequence = primitives;
3479             this.construction = ASN1Construction.constructed;
3480         }
3481     }
3482 
3483     @system
3484     unittest
3485     {
3486         void test(size_t length)
3487         {
3488             char[] data;
3489             data.length = length;
3490             for (size_t i = 0u; i < data.length; i++)
3491             {
3492                 data[i] = cast(char) ((i % 0x10) + 0x41);
3493             }
3494             CERElement el = new CERElement();
3495             el.utf8String = cast(string) data;
3496             assert(el.utf8String == cast(string) data);
3497         }
3498         test(0u);
3499         test(1u);
3500         test(8u);
3501         test(127u);
3502         test(128u);
3503         test(129u);
3504         test(192u);
3505         test(999u);
3506         test(1000u);
3507         test(1001u);
3508         test(2017u);
3509     }
3510 
3511     /**
3512         Decodes a $(MONO RELATIVE OBJECT IDENTIFIER).
3513 
3514         Throws:
3515         $(UL
3516             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
3517             $(LI $(D ASN1ValuePaddingException) if a single OID number is encoded with
3518                 "leading zero bytes" ($(D 0x80u)))
3519             $(LI $(D ASN1ValueOverflowException) if a single OID number is too big to
3520                 decode to a $(D size_t))
3521             $(LI $(D ASN1TruncationException) if a single OID number is "cut off")
3522         )
3523 
3524         Standards:
3525         $(UL
3526             $(LI $(LINK http://www.itu.int/rec/T-REC-X.660-201107-I/en, X.660))
3527         )
3528     */
3529     override public @property @system
3530     OIDNode[] relativeObjectIdentifier() const
3531     {
3532         if (this.construction != ASN1Construction.primitive)
3533             throw new ASN1ConstructionException
3534             (this.construction, "decode an RELATIVE OID");
3535 
3536         if (this.value.length == 0u) return [];
3537 
3538         if (this.value[$-1] > 0x80u)
3539             throw new ASN1TruncationException
3540             (size_t.max, this.value.length, "decode a RELATIVE OID");
3541 
3542         size_t[] numbers;
3543         size_t components = 0u;
3544         foreach (immutable size_t i, immutable ubyte b; this.value)
3545         {
3546             if (!(b & 0x80u)) components++;
3547         }
3548         numbers.length = components;
3549 
3550         size_t currentNumber = 0u;
3551         ubyte bytesUsedInCurrentNumber = 0u;
3552         foreach (immutable ubyte b; this.value)
3553         {
3554             if (bytesUsedInCurrentNumber == 0u && b == 0x80u)
3555                 throw new ASN1ValuePaddingException
3556                 (
3557                     "This exception was thrown because you attempted to decode " ~
3558                     "a RELATIVE OID that contained a number that was " ~
3559                     "encoded on more than the minimum necessary octets. This " ~
3560                     "is indicated by an occurrence of the octet 0x80, which " ~
3561                     "is the encoded equivalent of a leading zero. " ~
3562                     notWhatYouMeantText ~ forMoreInformationText ~
3563                     debugInformationText ~ reportBugsText
3564                 );
3565 
3566             if (numbers[currentNumber] > (size_t.max >> 7))
3567                 throw new ASN1ValueOverflowException
3568                 (
3569                     "This exception was thrown because you attempted to decode " ~
3570                     "a RELATIVE OID that encoded a number that is too big to " ~
3571                     "decode to a native integral type. " ~
3572                     notWhatYouMeantText ~ forMoreInformationText ~
3573                     debugInformationText ~ reportBugsText
3574                 );
3575 
3576             numbers[currentNumber] <<= 7;
3577             numbers[currentNumber] |= cast(size_t) (b & 0x7Fu);
3578 
3579             if (!(b & 0x80u))
3580             {
3581                 currentNumber++;
3582                 bytesUsedInCurrentNumber = 0u;
3583             }
3584             else
3585             {
3586                 bytesUsedInCurrentNumber++;
3587             }
3588         }
3589 
3590         // Constructs the array of OIDNodes from the array of numbers.
3591         OIDNode[] nodes;
3592         nodes.length = numbers.length;
3593         foreach (immutable size_t i, immutable size_t number; numbers)
3594         {
3595             nodes[i] = OIDNode(number);
3596         }
3597 
3598         return nodes;
3599     }
3600 
3601     /**
3602         Encodes a $(MONO RELATIVE OBJECT IDENTIFIER).
3603 
3604         Standards:
3605             $(LINK http://www.itu.int/rec/T-REC-X.660-201107-I/en, X.660)
3606     */
3607     override public @property @system nothrow
3608     void relativeObjectIdentifier(in OIDNode[] value)
3609     {
3610         scope(success) this.construction = ASN1Construction.primitive;
3611         this.value.length = 0u;
3612         foreach (node; value)
3613         {
3614             size_t number = node.number;
3615             if (number < 128u)
3616             {
3617                 this.value ~= cast(ubyte) number;
3618                 continue;
3619             }
3620 
3621             ubyte[] encodedOIDNode;
3622             while (number != 0u)
3623             {
3624                 ubyte[] numberBytes;
3625                 numberBytes.length = size_t.sizeof;
3626                 *cast(size_t *) numberBytes.ptr = number;
3627                 if ((numberBytes[0] & 0x80u) == 0u) numberBytes[0] |= 0x80u;
3628                 encodedOIDNode = numberBytes[0] ~ encodedOIDNode;
3629                 number >>= 7u;
3630             }
3631 
3632             encodedOIDNode[$-1] &= 0x7Fu;
3633             this.value ~= encodedOIDNode;
3634         }
3635     }
3636 
3637     @system
3638     unittest
3639     {
3640         CERElement element = new CERElement();
3641 
3642         // All values of octet[0] should pass.
3643         for (ubyte i = 0x00u; i < 0x80u; i++)
3644         {
3645             element.value = [ i ];
3646             assertNotThrown!Exception(element.roid);
3647         }
3648 
3649         // All values of octet[0] should pass.
3650         for (ubyte i = 0x81u; i < 0xFFu; i++)
3651         {
3652             element.value = [ i, 0x14u ];
3653             assertNotThrown!Exception(element.roid);
3654         }
3655     }
3656 
3657     @system
3658     unittest
3659     {
3660         CERElement element = new CERElement();
3661 
3662         // Tests for the "leading zero byte," 0x80
3663         element.value = [ 0x29u, 0x80u, 0x14u ];
3664         assertThrown!ASN1ValuePaddingException(element.roid);
3665         element.value = [ 0x29u, 0x80u, 0x80u ];
3666         assertThrown!ASN1ValuePaddingException(element.roid);
3667         element.value = [ 0x80u, 0x80u, 0x80u ];
3668         assertThrown!ASN1ValuePaddingException(element.roid);
3669         element.value = [ 0x29u, 0x14u, 0x80u ];
3670         assertThrown!ASN1ValuePaddingException(element.roid);
3671         element.value = [ 0x29u, 0x80u ];
3672         assertThrown!ASN1ValuePaddingException(element.roid);
3673 
3674         // Test for non-terminating components
3675         element.value = [ 0x29u, 0x81u ];
3676         assertThrown!ASN1TruncationException(element.roid);
3677         element.value = [ 0x29u, 0x14u, 0x81u ];
3678         assertThrown!ASN1TruncationException(element.roid);
3679     }
3680 
3681     /**
3682         Decodes a sequence of elements
3683 
3684         Throws:
3685         $(UL
3686             $(LI $(D ASN1ConstructionException) if the element is marked as "primitive")
3687             $(LI And all of the exceptions thrown by the constructor)
3688         )
3689     */
3690     override public @property @system
3691     CERElement[] sequence() const
3692     {
3693         if (this.construction != ASN1Construction.constructed)
3694             throw new ASN1ConstructionException
3695             (this.construction, "decode a SEQUENCE");
3696 
3697         size_t i = 0u;
3698         Appender!(CERElement[]) result = appender!(CERElement[])();
3699         while (i < this.value.length)
3700             result.put(new CERElement(i, this.value));
3701         return result.data;
3702     }
3703 
3704     /// Encodes a sequence of elements
3705     override public @property @system
3706     void sequence(in CERElement[] value)
3707     {
3708         scope(success) this.construction = ASN1Construction.constructed;
3709         Appender!(ubyte[]) result = appender!(ubyte[])();
3710         foreach (element; value)
3711         {
3712             result.put(element.toBytes);
3713         }
3714         this.value = result.data;
3715     }
3716 
3717     /**
3718         Decodes a set of elements
3719 
3720         Throws:
3721         $(UL
3722             $(LI $(D ASN1ConstructionException) if the element is marked as "primitive")
3723             $(LI And all of the exceptions thrown by the constructor)
3724         )
3725     */
3726     override public @property @system
3727     CERElement[] set() const
3728     {
3729         if (this.construction != ASN1Construction.constructed)
3730             throw new ASN1ConstructionException
3731             (this.construction, "decode a SET");
3732 
3733         size_t i = 0u;
3734         Appender!(CERElement[]) result = appender!(CERElement[])();
3735         while (i < this.value.length)
3736             result.put(new CERElement(i, this.value));
3737         return result.data;
3738     }
3739 
3740     /// Encodes a set of elements
3741     override public @property @system
3742     void set(in CERElement[] value)
3743     {
3744         scope(success) this.construction = ASN1Construction.constructed;
3745         Appender!(ubyte[]) result = appender!(ubyte[])();
3746         foreach (element; value)
3747         {
3748             result.put(element.toBytes);
3749         }
3750         this.value = result.data;
3751     }
3752 
3753     /**
3754         Decodes a string, where the characters of the string are limited to
3755         0 - 9 and $(MONO SPACE).
3756 
3757         Throws:
3758         $(UL
3759             $(LI $(D ASN1ValueCharactersException) if any character other than 0-9 or space is encoded.)
3760             $(LI $(D ASN1RecursionException)
3761                 if using constructed form and the element
3762                 is constructed of too many nested constructed elements)
3763             $(LI $(D ASN1TagClassException)
3764                 if any nested primitives do not share the
3765                 same tag class as their outer constructed element)
3766             $(LI $(D ASN1TagNumberException)
3767                 if any nested primitives do not share the
3768                 same tag number as their outer constructed element)
3769         )
3770     */
3771     override public @property @system
3772     string numericString() const
3773     {
3774         if (this.construction == ASN1Construction.primitive)
3775         {
3776             if (this.value.length > 1000u)
3777                 throw new ASN1ValueSizeException
3778                 (0u, 1000u, this.value.length, "decode a primitively-encoded NumericString");
3779 
3780             foreach (immutable character; this.value)
3781             {
3782                 if (!canFind(numericStringCharacters, character))
3783                     throw new ASN1ValueCharactersException
3784                     ("1234567890 ", character, "NumericString");
3785             }
3786             return cast(string) this.value;
3787         }
3788         else
3789         {
3790             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
3791                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a NumericString");
3792             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
3793 
3794             CERElement[] substrings = this.sequence;
3795             if (substrings.length < 2u)
3796                 throw new ASN1ValueException
3797                 (
3798                     "This exception was thrown because you attempted to decode " ~
3799                     "a constructed NumericString that was constituted by fewer " ~
3800                     "than two nested elements. When using the Canonical Encoding " ~
3801                     "Rules (CER), a legitimate constructed NumericString must " ~
3802                     "contain at least two nested elements, and each but the " ~
3803                     "last must contain exactly 1000 content octets. The last " ~
3804                     "nested element may have between 1 and 1000 content " ~
3805                     "octets inclusively. This element should have been encoded " ~
3806                     "primitively using definite-length encoding."
3807                 );
3808 
3809             foreach (substring; substrings[0 .. $-1])
3810             {
3811                 if (substring.length != 1000u)
3812                     throw new ASN1ValueSizeException
3813                     (1000u, 1000u, substring.length, "decode a substring of a constructed NumericString");
3814             }
3815 
3816             if (substrings[$-1].length == 0u)
3817                 throw new ASN1ValueSizeException
3818                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed NumericString");
3819 
3820             Appender!string appendy = appender!string();
3821             foreach (substring; substrings)
3822             {
3823                 if (substring.tagClass != this.tagClass)
3824                     throw new ASN1TagClassException
3825                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed NumericString");
3826 
3827                 if (substring.construction != ASN1Construction.primitive)
3828                     throw new ASN1ConstructionException
3829                     (substring.construction, "decode a substring of a constructed NumericString");
3830 
3831                 if (substring.tagNumber != this.tagNumber)
3832                     throw new ASN1TagNumberException
3833                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed NumericString");
3834 
3835                 appendy.put(substring.numericString);
3836             }
3837             return appendy.data;
3838         }
3839     }
3840 
3841     /**
3842         Encodes a string, where the characters of the string are limited to
3843         0 - 9 and space.
3844 
3845         Throws:
3846         $(UL
3847             $(LI $(D ASN1ValueCharactersException) if any character other than 0-9 or space is supplied.)
3848         )
3849     */
3850     override public @property @system
3851     void numericString(in string value)
3852     {
3853         foreach (immutable character; value)
3854         {
3855             if (!canFind(numericStringCharacters, character))
3856                 throw new ASN1ValueCharactersException
3857                 ("1234567890 ", character, "NumericString");
3858         }
3859 
3860         if (value.length <= 1000u)
3861         {
3862             this.value = cast(ubyte[]) value.dup;
3863         }
3864         else
3865         {
3866             CERElement[] primitives;
3867             size_t i = 0u;
3868             while (i+1000u < value.length)
3869             {
3870                 CERElement x = new CERElement();
3871                 x.tagNumber = this.tagNumber;
3872                 x.construction = ASN1Construction.primitive;
3873                 x.value = cast(ubyte[]) value[i .. i+1000u];
3874                 primitives ~= x;
3875                 i += 1000u;
3876             }
3877 
3878             CERElement y = new CERElement();
3879             y.tagNumber = this.tagNumber;
3880             y.construction = ASN1Construction.primitive;
3881             y.value = cast(ubyte[]) value[i .. $];
3882             primitives ~= y;
3883 
3884             this.sequence = primitives;
3885             this.construction = ASN1Construction.constructed;
3886         }
3887     }
3888 
3889     @system
3890     unittest
3891     {
3892         void test(size_t length)
3893         {
3894             char[] data;
3895             data.length = length;
3896             for (size_t i = 0u; i < data.length; i++)
3897             {
3898                 data[i] = cast(char) ((i % 0x09) + 0x30);
3899             }
3900             CERElement el = new CERElement();
3901             el.numericString = cast(string) data;
3902             assert(el.numericString == cast(string) data);
3903         }
3904         test(0u);
3905         test(1u);
3906         test(8u);
3907         test(127u);
3908         test(128u);
3909         test(129u);
3910         test(192u);
3911         test(999u);
3912         test(1000u);
3913         test(1001u);
3914         test(2017u);
3915     }
3916 
3917     /**
3918         Decodes a string that will only contain characters a-z, A-Z, 0-9,
3919         space, apostrophe, parentheses, comma, minus, plus, period,
3920         forward slash, colon, equals, and question mark.
3921 
3922         Throws:
3923         $(UL
3924             $(LI $(D ASN1ValueCharactersException)
3925                 if any character other than a-z, A-Z,
3926                 0-9, space, apostrophe, parentheses, comma, minus, plus,
3927                 period, forward slash, colon, equals, or question mark are
3928                 encoded)
3929             $(LI $(D ASN1RecursionException)
3930                 if using constructed form and the element
3931                 is constructed of too many nested constructed elements)
3932             $(LI $(D ASN1TagClassException)
3933                 if any nested primitives do not share the
3934                 same tag class as their outer constructed element)
3935             $(LI $(D ASN1TagNumberException)
3936                 if any nested primitives do not share the
3937                 same tag number as their outer constructed element)
3938         )
3939     */
3940     override public @property @system
3941     string printableString() const
3942     {
3943         if (this.construction == ASN1Construction.primitive)
3944         {
3945             if (this.value.length > 1000u)
3946                 throw new ASN1ValueSizeException
3947                 (0u, 1000u, this.value.length, "decode a primitively-encoded PrintableString");
3948 
3949             foreach (immutable character; this.value)
3950             {
3951                 if (!canFind(printableStringCharacters, character))
3952                     throw new ASN1ValueCharactersException
3953                     (printableStringCharacters, character, "PrintableString");
3954             }
3955             return cast(string) this.value;
3956         }
3957         else
3958         {
3959             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
3960                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a PrintableString");
3961             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
3962 
3963             CERElement[] substrings = this.sequence;
3964             if (substrings.length < 2u)
3965                 throw new ASN1ValueException
3966                 (
3967                     "This exception was thrown because you attempted to decode " ~
3968                     "a constructed PrintableString that was constituted by fewer " ~
3969                     "than two nested elements. When using the Canonical Encoding " ~
3970                     "Rules (CER), a legitimate constructed PrintableString must " ~
3971                     "contain at least two nested elements, and each but the " ~
3972                     "last must contain exactly 1000 content octets. The last " ~
3973                     "nested element may have between 1 and 1000 content " ~
3974                     "octets inclusively. This element should have been encoded " ~
3975                     "primitively using definite-length encoding."
3976                 );
3977 
3978             foreach (substring; substrings[0 .. $-1])
3979             {
3980                 if (substring.length != 1000u)
3981                     throw new ASN1ValueSizeException
3982                     (1000u, 1000u, substring.length, "decode a substring of a constructed PrintableString");
3983             }
3984 
3985             if (substrings[$-1].length == 0u)
3986                 throw new ASN1ValueSizeException
3987                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed PrintableString");
3988 
3989             Appender!string appendy = appender!string();
3990             foreach (substring; substrings)
3991             {
3992                 if (substring.tagClass != this.tagClass)
3993                     throw new ASN1TagClassException
3994                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed PrintableString");
3995 
3996                 if (substring.construction != ASN1Construction.primitive)
3997                     throw new ASN1ConstructionException
3998                     (substring.construction, "decode a substring of a constructed PrintableString");
3999 
4000                 if (substring.tagNumber != this.tagNumber)
4001                     throw new ASN1TagNumberException
4002                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed PrintableString");
4003 
4004                 appendy.put(substring.printableString);
4005             }
4006             return appendy.data;
4007         }
4008     }
4009 
4010     /**
4011         Encodes a string that may only contain characters a-z, A-Z, 0-9,
4012         space, apostrophe, parentheses, comma, minus, plus, period,
4013         forward slash, colon, equals, and question mark.
4014 
4015         Throws:
4016             $(LI $(D ASN1ValueCharactersException) if any character other than a-z, A-Z,
4017                 0-9, space, apostrophe, parentheses, comma, minus, plus,
4018                 period, forward slash, colon, equals, or question mark are
4019                 supplied)
4020     */
4021     override public @property @system
4022     void printableString(in string value)
4023     {
4024         foreach (immutable character; value)
4025         {
4026             if (!canFind(printableStringCharacters, character))
4027                 throw new ASN1ValueCharactersException
4028                 (printableStringCharacters, character, "PrintableString");
4029         }
4030 
4031         if (value.length <= 1000u)
4032         {
4033             this.value = cast(ubyte[]) value.dup;
4034         }
4035         else
4036         {
4037             CERElement[] primitives;
4038             size_t i = 0u;
4039             while (i+1000u < value.length)
4040             {
4041                 CERElement x = new CERElement();
4042                 x.tagNumber = this.tagNumber;
4043                 x.construction = ASN1Construction.primitive;
4044                 x.value = cast(ubyte[]) value[i .. i+1000u];
4045                 primitives ~= x;
4046                 i += 1000u;
4047             }
4048 
4049             CERElement y = new CERElement();
4050             y.tagNumber = this.tagNumber;
4051             y.construction = ASN1Construction.primitive;
4052             y.value = cast(ubyte[]) value[i .. $];
4053             primitives ~= y;
4054 
4055             this.sequence = primitives;
4056             this.construction = ASN1Construction.constructed;
4057         }
4058     }
4059 
4060     @system
4061     unittest
4062     {
4063         void test(size_t length)
4064         {
4065             char[] data;
4066             data.length = length;
4067             for (size_t i = 0u; i < data.length; i++)
4068             {
4069                 data[i] = cast(char) ((i % 0x10) + 0x41);
4070             }
4071             CERElement el = new CERElement();
4072             el.printableString = cast(string) data;
4073             assert(el.printableString == cast(string) data);
4074         }
4075         test(0u);
4076         test(1u);
4077         test(8u);
4078         test(127u);
4079         test(128u);
4080         test(129u);
4081         test(192u);
4082         test(999u);
4083         test(1000u);
4084         test(1001u);
4085         test(2017u);
4086     }
4087 
4088     /**
4089         Literally just returns the value bytes.
4090 
4091         Returns: an unsigned byte array, where each byte is a T.61 character.
4092 
4093         Throws:
4094         $(UL
4095             $(LI $(D ASN1RecursionException)
4096                 if using constructed form and the element
4097                 is constructed of too many nested constructed elements)
4098             $(LI $(D ASN1TagClassException)
4099                 if any nested primitives do not share the
4100                 same tag class as their outer constructed element)
4101             $(LI $(D ASN1TagNumberException)
4102                 if any nested primitives do not share the
4103                 same tag number as their outer constructed element)
4104         )
4105     */
4106     override public @property @system
4107     ubyte[] teletexString() const
4108     {
4109         if (this.construction == ASN1Construction.primitive)
4110         {
4111             if (this.value.length > 1000u)
4112                 throw new ASN1ValueSizeException
4113                 (0u, 1000u, this.value.length, "decode a primitively-encoded TeletexString");
4114 
4115             return this.value.dup;
4116         }
4117         else
4118         {
4119             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
4120                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a TeletexString");
4121             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
4122 
4123             CERElement[] substrings = this.sequence;
4124             if (substrings.length < 2u)
4125                 throw new ASN1ValueException
4126                 (
4127                     "This exception was thrown because you attempted to decode " ~
4128                     "a constructed TeletexString that was constituted by fewer " ~
4129                     "than two nested elements. When using the Canonical Encoding " ~
4130                     "Rules (CER), a legitimate constructed TeletexString must " ~
4131                     "contain at least two nested elements, and each but the " ~
4132                     "last must contain exactly 1000 content octets. The last " ~
4133                     "nested element may have between 1 and 1000 content " ~
4134                     "octets inclusively. This element should have been encoded " ~
4135                     "primitively using definite-length encoding."
4136                 );
4137 
4138             foreach (substring; substrings[0 .. $-1])
4139             {
4140                 if (substring.length != 1000u)
4141                     throw new ASN1ValueSizeException
4142                     (1000u, 1000u, substring.length, "decode a substring of a constructed TeletexString");
4143             }
4144 
4145             if (substrings[$-1].length == 0u)
4146                 throw new ASN1ValueSizeException
4147                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed TeletexString");
4148 
4149             Appender!(ubyte[]) appendy = appender!(ubyte[])();
4150             foreach (substring; substrings)
4151             {
4152                 if (substring.tagClass != this.tagClass)
4153                     throw new ASN1TagClassException
4154                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed TeletexString");
4155 
4156                 if (substring.construction != ASN1Construction.primitive)
4157                     throw new ASN1ConstructionException
4158                     (substring.construction, "decode a substring of a constructed TeletexString");
4159 
4160                 if (substring.tagNumber != this.tagNumber)
4161                     throw new ASN1TagNumberException
4162                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed TeletexString");
4163 
4164                 appendy.put(substring.teletexString);
4165             }
4166             return appendy.data;
4167         }
4168     }
4169 
4170     /// Literally just sets the value bytes.
4171     override public @property @system
4172     void teletexString(in ubyte[] value)
4173     {
4174         if (value.length <= 1000u)
4175         {
4176             this.value = value.dup;
4177         }
4178         else
4179         {
4180             CERElement[] primitives;
4181             size_t i = 0u;
4182             while (i+1000u < value.length)
4183             {
4184                 CERElement x = new CERElement();
4185                 x.tagNumber = this.tagNumber;
4186                 x.construction = ASN1Construction.primitive;
4187                 x.value = value[i .. i+1000u].dup;
4188                 primitives ~= x;
4189                 i += 1000u;
4190             }
4191 
4192             CERElement y = new CERElement();
4193             y.tagNumber = this.tagNumber;
4194             y.construction = ASN1Construction.primitive;
4195             y.value = value[i .. $].dup;
4196             primitives ~= y;
4197 
4198             this.sequence = primitives;
4199             this.construction = ASN1Construction.constructed;
4200         }
4201     }
4202 
4203     @system
4204     unittest
4205     {
4206         void test(size_t length)
4207         {
4208             ubyte[] data;
4209             data.length = length;
4210             for (size_t i = 0u; i < data.length; i++)
4211             {
4212                 data[i] = (i % 9u);
4213             }
4214             CERElement el = new CERElement();
4215             el.teletexString = data;
4216             assert(el.teletexString == data);
4217         }
4218         test(0u);
4219         test(1u);
4220         test(8u);
4221         test(127u);
4222         test(128u);
4223         test(129u);
4224         test(192u);
4225         test(999u);
4226         test(1000u);
4227         test(1001u);
4228         test(2017u);
4229     }
4230 
4231     /**
4232         Literally just returns the value bytes.
4233 
4234         Returns: an unsigned byte array, where each byte is a Videotex character.
4235 
4236         Throws:
4237         $(UL
4238             $(LI $(D ASN1RecursionException)
4239                 if using constructed form and the element
4240                 is constructed of too many nested constructed elements)
4241             $(LI $(D ASN1TagClassException)
4242                 if any nested primitives do not share the
4243                 same tag class as their outer constructed element)
4244             $(LI $(D ASN1TagNumberException)
4245                 if any nested primitives do not share the
4246                 same tag number as their outer constructed element)
4247         )
4248     */
4249     override public @property @system
4250     ubyte[] videotexString() const
4251     {
4252         if (this.construction == ASN1Construction.primitive)
4253         {
4254             if (this.value.length > 1000u)
4255                 throw new ASN1ValueSizeException
4256                 (0u, 1000u, this.value.length, "decode a primitively-encoded VideotexString");
4257 
4258             return this.value.dup;
4259         }
4260         else
4261         {
4262             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
4263                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a VideotexString");
4264             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
4265 
4266             CERElement[] substrings = this.sequence;
4267             if (substrings.length < 2u)
4268                 throw new ASN1ValueException
4269                 (
4270                     "This exception was thrown because you attempted to decode " ~
4271                     "a constructed VideotexString that was constituted by fewer " ~
4272                     "than two nested elements. When using the Canonical Encoding " ~
4273                     "Rules (CER), a legitimate constructed VideotexString must " ~
4274                     "contain at least two nested elements, and each but the " ~
4275                     "last must contain exactly 1000 content octets. The last " ~
4276                     "nested element may have between 1 and 1000 content " ~
4277                     "octets inclusively. This element should have been encoded " ~
4278                     "primitively using definite-length encoding."
4279                 );
4280 
4281             foreach (substring; substrings[0 .. $-1])
4282             {
4283                 if (substring.length != 1000u)
4284                     throw new ASN1ValueSizeException
4285                     (1000u, 1000u, substring.length, "decode a substring of a constructed VideotexString");
4286             }
4287 
4288             if (substrings[$-1].length == 0u)
4289                 throw new ASN1ValueSizeException
4290                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed VideotexString");
4291 
4292             Appender!(ubyte[]) appendy = appender!(ubyte[])();
4293             foreach (substring; substrings)
4294             {
4295                 if (substring.tagClass != this.tagClass)
4296                     throw new ASN1TagClassException
4297                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed VideotexString");
4298 
4299                 if (substring.construction != ASN1Construction.primitive)
4300                     throw new ASN1ConstructionException
4301                     (substring.construction, "decode a substring of a constructed VideotexString");
4302 
4303                 if (substring.tagNumber != this.tagNumber)
4304                     throw new ASN1TagNumberException
4305                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed VideotexString");
4306 
4307                 appendy.put(substring.videotexString);
4308             }
4309             return appendy.data;
4310         }
4311     }
4312 
4313     /// Literally just sets the value bytes.
4314     override public @property @system
4315     void videotexString(in ubyte[] value)
4316     {
4317         if (value.length <= 1000u)
4318         {
4319             this.value = value.dup;
4320         }
4321         else
4322         {
4323             CERElement[] primitives;
4324             size_t i = 0u;
4325             while (i+1000u < value.length)
4326             {
4327                 CERElement x = new CERElement();
4328                 x.tagNumber = this.tagNumber;
4329                 x.construction = ASN1Construction.primitive;
4330                 x.value = value[i .. i+1000u].dup;
4331                 primitives ~= x;
4332                 i += 1000u;
4333             }
4334 
4335             CERElement y = new CERElement();
4336             y.tagNumber = this.tagNumber;
4337             y.construction = ASN1Construction.primitive;
4338             y.value = value[i .. $].dup;
4339             primitives ~= y;
4340 
4341             this.sequence = primitives;
4342             this.construction = ASN1Construction.constructed;
4343         }
4344     }
4345 
4346     @system
4347     unittest
4348     {
4349         void test(size_t length)
4350         {
4351             ubyte[] data;
4352             data.length = length;
4353             for (size_t i = 0u; i < data.length; i++)
4354             {
4355                 data[i] = (i % 9u);
4356             }
4357             CERElement el = new CERElement();
4358             el.videotexString = data;
4359             assert(el.videotexString == data);
4360         }
4361         test(0u);
4362         test(1u);
4363         test(8u);
4364         test(127u);
4365         test(128u);
4366         test(129u);
4367         test(192u);
4368         test(999u);
4369         test(1000u);
4370         test(1001u);
4371         test(2017u);
4372     }
4373 
4374     /**
4375         Decodes a string that only contains ASCII characters.
4376 
4377         $(MONO IA5String) differs from ASCII ever so slightly: IA5 is international,
4378         leaving 10 characters up to be locale-specific:
4379 
4380         $(TABLE
4381             $(TR $(TH Byte) $(TH ASCII Character))
4382             $(TR $(TD 0x40) $(TD @))
4383             $(TR $(TD 0x5B) $(TD [))
4384             $(TR $(TD 0x5C) $(TD \))
4385             $(TR $(TD 0x5D) $(TD ]))
4386             $(TR $(TD 0x5E) $(TD ^))
4387             $(TR $(TD 0x60) $(TD `))
4388             $(TR $(TD 0x7B) $(TD {))
4389             $(TR $(TD 0x7C) $(TD /))
4390             $(TR $(TD 0x7D) $(TD }))
4391             $(TR $(TD 0x7E) $(TD ~))
4392         )
4393 
4394         Throws:
4395         $(UL
4396             $(LI $(D ASN1ValueCharactersException) if any encoded character is not ASCII)
4397             $(LI $(D ASN1RecursionException)
4398                 if using constructed form and the element
4399                 is constructed of too many nested constructed elements)
4400             $(LI $(D ASN1TagClassException)
4401                 if any nested primitives do not share the
4402                 same tag class as their outer constructed element)
4403             $(LI $(D ASN1TagNumberException)
4404                 if any nested primitives do not share the
4405                 same tag number as their outer constructed element)
4406         )
4407     */
4408     override public @property @system
4409     string internationalAlphabetNumber5String() const
4410     {
4411         if (this.construction == ASN1Construction.primitive)
4412         {
4413             if (this.value.length > 1000u)
4414                 throw new ASN1ValueSizeException
4415                 (0u, 1000u, this.value.length, "decode a primitively-encoded IA5String");
4416 
4417             foreach (immutable character; this.value)
4418             {
4419                 if (!character.isASCII)
4420                     throw new ASN1ValueCharactersException
4421                     ("all ASCII characters", character, "IA5String");
4422             }
4423             return cast(string) this.value;
4424         }
4425         else
4426         {
4427             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
4428                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a IA5String");
4429             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
4430 
4431             CERElement[] substrings = this.sequence;
4432             if (substrings.length < 2u)
4433                 throw new ASN1ValueException
4434                 (
4435                     "This exception was thrown because you attempted to decode " ~
4436                     "a constructed IA5String that was constituted by fewer " ~
4437                     "than two nested elements. When using the Canonical Encoding " ~
4438                     "Rules (CER), a legitimate constructed IA5String must " ~
4439                     "contain at least two nested elements, and each but the " ~
4440                     "last must contain exactly 1000 content octets. The last " ~
4441                     "nested element may have between 1 and 1000 content " ~
4442                     "octets inclusively. This element should have been encoded " ~
4443                     "primitively using definite-length encoding."
4444                 );
4445 
4446             foreach (substring; substrings[0 .. $-1])
4447             {
4448                 if (substring.length != 1000u)
4449                     throw new ASN1ValueSizeException
4450                     (1000u, 1000u, substring.length, "decode a substring of a constructed IA5String");
4451             }
4452 
4453             if (substrings[$-1].length == 0u)
4454                 throw new ASN1ValueSizeException
4455                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed IA5String");
4456 
4457             Appender!string appendy = appender!string();
4458             foreach (substring; substrings)
4459             {
4460                 if (substring.tagClass != this.tagClass)
4461                     throw new ASN1TagClassException
4462                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed IA5String");
4463 
4464                 if (substring.construction != ASN1Construction.primitive)
4465                     throw new ASN1ConstructionException
4466                     (substring.construction, "decode a substring of a constructed IA5String");
4467 
4468                 if (substring.tagNumber != this.tagNumber)
4469                     throw new ASN1TagNumberException
4470                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed IA5String");
4471 
4472                 appendy.put(substring.ia5String);
4473             }
4474             return appendy.data;
4475         }
4476     }
4477 
4478     /**
4479         Encodes a string that may only contain ASCII characters.
4480 
4481         $(MONO IA5String) differs from ASCII ever so slightly: IA5 is international,
4482         leaving 10 characters up to be locale-specific:
4483 
4484         $(TABLE
4485             $(TR $(TH Byte) $(TH ASCII Character))
4486             $(TR $(TD 0x40) $(TD @))
4487             $(TR $(TD 0x5B) $(TD [))
4488             $(TR $(TD 0x5C) $(TD \))
4489             $(TR $(TD 0x5D) $(TD ]))
4490             $(TR $(TD 0x5E) $(TD ^))
4491             $(TR $(TD 0x60) $(TD `))
4492             $(TR $(TD 0x7B) $(TD {))
4493             $(TR $(TD 0x7C) $(TD /))
4494             $(TR $(TD 0x7D) $(TD }))
4495             $(TR $(TD 0x7E) $(TD ~))
4496         )
4497 
4498         Throws:
4499         $(UL
4500             $(LI $(D ASN1ValueCharactersException) if any encoded character is not ASCII)
4501         )
4502     */
4503     override public @property @system
4504     void internationalAlphabetNumber5String(in string value)
4505     {
4506         foreach (immutable character; value)
4507         {
4508             if (!character.isASCII)
4509                 throw new ASN1ValueCharactersException
4510                 ("all ASCII characters", character, "IA5String");
4511         }
4512 
4513         if (value.length <= 1000u)
4514         {
4515             this.value = cast(ubyte[]) value.dup;
4516         }
4517         else
4518         {
4519             CERElement[] primitives;
4520             size_t i = 0u;
4521             while (i+1000u < value.length)
4522             {
4523                 CERElement x = new CERElement();
4524                 x.tagNumber = this.tagNumber;
4525                 x.construction = ASN1Construction.primitive;
4526                 x.value = cast(ubyte[]) value[i .. i+1000u];
4527                 primitives ~= x;
4528                 i += 1000u;
4529             }
4530 
4531             CERElement y = new CERElement();
4532             y.tagNumber = this.tagNumber;
4533             y.construction = ASN1Construction.primitive;
4534             y.value = cast(ubyte[]) value[i .. $];
4535             primitives ~= y;
4536 
4537             this.sequence = primitives;
4538             this.construction = ASN1Construction.constructed;
4539         }
4540     }
4541 
4542     @system
4543     unittest
4544     {
4545         void test(size_t length)
4546         {
4547             char[] data;
4548             data.length = length;
4549             for (size_t i = 0u; i < data.length; i++)
4550             {
4551                 data[i] = cast(char) ((i % 0x40) + 0x30);
4552             }
4553             CERElement el = new CERElement();
4554             el.ia5String = cast(string) data;
4555             assert(el.ia5String == cast(string) data);
4556         }
4557         test(0u);
4558         test(1u);
4559         test(8u);
4560         test(127u);
4561         test(128u);
4562         test(129u);
4563         test(192u);
4564         test(999u);
4565         test(1000u);
4566         test(1001u);
4567         test(2017u);
4568     }
4569 
4570     /**
4571         Decodes a $(LINK https://dlang.org/phobos/std_datetime_date.html#.DateTime, DateTime).
4572         The value is just the ASCII character representation of the UTC-formatted timestamp.
4573 
4574         An UTC Timestamp looks like:
4575         $(UL
4576             $(LI $(MONO 9912312359Z))
4577             $(LI $(MONO 991231235959+0200))
4578         )
4579 
4580         If the first digit of the two-digit year is 7, 6, 5, 4, 3, 2, 1, or 0,
4581         meaning that the date refers to the first 80 years of the century, this
4582         assumes we are talking about the 21st century and prepend '20' when
4583         creating the ISO Date String. Otherwise, it assumes we are talking
4584         about the 20th century, and prepend '19' when creating the string.
4585 
4586         See_Also:
4587         $(UL
4588             $(LI $(LINK https://www.obj-sys.com/asn1tutorial/node15.html, UTCTime))
4589             $(LI $(LINK https://dlang.org/phobos/std_datetime_date.html#.DateTime, DateTime))
4590         )
4591 
4592         Throws:
4593         $(UL
4594             $(LI $(D ASN1ValueException) if the encoded value does not end with a 'Z')
4595             $(LI $(D ASN1ValueCharactersException) if any character is not valid in a $(MONO Visiblestring))
4596             $(LI $(D DateTimeException) if the encoded string cannot be decoded to a DateTime)
4597             $(LI $(D ASN1RecursionException)
4598                 if using constructed form and the element
4599                 is constructed of too many nested constructed elements)
4600             $(LI $(D ASN1TagClassException)
4601                 if any nested primitives do not share the
4602                 same tag class as their outer constructed element)
4603             $(LI $(D ASN1TagNumberException)
4604                 if any nested primitives do not share the
4605                 same tag number as their outer constructed element)
4606         )
4607     */
4608     override public @property @system
4609     DateTime coordinatedUniversalTime() const
4610     {
4611         if (this.construction != ASN1Construction.primitive)
4612             throw new ASN1ConstructionException(this.construction, "decode a UTCTime");
4613 
4614         // Mandated in X.690, section 11.8.2
4615         if (this.value.length != 13u) // YYMMDDhhmmssZ
4616             throw new ASN1ValueSizeException(13u, 13u, this.value.length, "decode a UTCTime");
4617 
4618         // Mandated in X.690, section 11.8.1
4619         if (this.value[$-1] != 'Z')
4620             throw new ASN1ValueException
4621             (
4622                 "This exception was thrown because you attempted to decode " ~
4623                 "a UTCTime whose encoding did not terminate with a 'Z', as " ~
4624                 "is expected by the Canonical Encoding Rules (CER) and " ~
4625                 "Distinguished Encoding Rules (DER). " ~
4626                 notWhatYouMeantText ~ forMoreInformationText ~
4627                 debugInformationText ~ reportBugsText
4628             );
4629 
4630         /** NOTE:
4631             .fromISOString() MUST be called from SysTime, not DateTime. There
4632             is a subtle difference in how .fromISOString() works in both SysTime
4633             and DateTime: SysTime's accepts the "Z" at the end (indicating that
4634             the time is in GMT).
4635 
4636             If you use DateTime.fromISOString, you will get a DateTimeException
4637             whose cryptic message reads "Invalid ISO String: " followed,
4638             strangely, by only the last six characters of the string.
4639         */
4640         immutable string dt = (((this.value[0] <= '7') ? "20" : "19") ~ cast(string) this.value);
4641         return cast(DateTime) SysTime.fromISOString(dt[0 .. 8].idup ~ "T" ~ dt[8 .. $].idup);
4642     }
4643 
4644     /**
4645         Encodes a DateTime. The value is just the ASCII character representation of
4646         the UTC-formatted timestamp.
4647 
4648         An UTC Timestamp looks like:
4649         $(UL
4650             $(LI $(MONO 9912312359Z))
4651             $(LI $(MONO 991231235959+0200))
4652         )
4653 
4654         See_Also:
4655             $(LINK https://www.obj-sys.com/asn1tutorial/node15.html, UTCTime)
4656     */
4657     override public @property @system
4658     void coordinatedUniversalTime(in DateTime value)
4659     out
4660     {
4661         // YYMMDDhhmmssZ is the only acceptable format for CER
4662         assert(this.value.length == 13u);
4663         assert(this.value[$-1] == 'Z');
4664     }
4665     do
4666     {
4667         scope(success) this.construction = ASN1Construction.primitive;
4668         immutable SysTime st = SysTime(value, UTC());
4669         this.value = cast(ubyte[]) ((st.toUTC()).toISOString()[2 .. $].replace("T", ""));
4670     }
4671 
4672     @system
4673     unittest
4674     {
4675         CERElement el = new CERElement();
4676         el.utcTime = DateTime(2017, 10, 3);
4677         assert(el.visibleString == "171003000000Z");
4678     }
4679 
4680     /**
4681         Decodes a DateTime. The value is just the ASCII character representation of
4682         the $(LINK https://www.iso.org/iso-8601-date-and-time-format.html, ISO 8601)-formatted timestamp.
4683 
4684         An ISO-8601 Timestamp looks like:
4685         $(UL
4686             $(LI $(MONO 19851106210627.3))
4687             $(LI $(MONO 19851106210627.3Z))
4688             $(LI $(MONO 19851106210627.3-0500))
4689         )
4690 
4691         Throws:
4692         $(UL
4693             $(LI $(D ASN1ValueException) if the encoded value did not end with a 'Z'
4694                 or contained a misplaced or unnecessary decimal point)
4695             $(LI $(D ASN1ValueCharactersException) if any character is not valid in a $(MONO Visiblestring))
4696             $(LI $(D DateTimeException) if the encoded string cannot be decoded to a DateTime)
4697             $(LI $(D ASN1ValuePaddingException) if the seconds fraction contains trailing zeroes)
4698             $(LI $(D ASN1RecursionException)
4699                 if using constructed form and the element
4700                 is constructed of too many nested constructed elements)
4701             $(LI $(D ASN1TagClassException)
4702                 if any nested primitives do not share the
4703                 same tag class as their outer constructed element)
4704             $(LI $(D ASN1TagNumberException)
4705                 if any nested primitives do not share the
4706                 same tag number as their outer constructed element)
4707         )
4708 
4709         Standards:
4710         $(UL
4711             $(LI $(LINK https://www.iso.org/iso-8601-date-and-time-format.html, ISO 8601))
4712         )
4713     */
4714     override public @property @system
4715     DateTime generalizedTime() const
4716     {
4717         if (this.construction != ASN1Construction.primitive)
4718             throw new ASN1ConstructionException(this.construction, "decode a GeneralizedTime");
4719 
4720         if (this.value.length < 15u || this.value.length > 1000u)
4721             throw new ASN1ValueSizeException(15u, 1000u, this.value.length, "decode a GeneralizedTime");
4722 
4723         // Inferred, because YYYYMMDDhhmmss.Z could not be valid.
4724         if (this.value.length == 16u)
4725             throw new ASN1ValueException
4726             (
4727                 "This exception was thrown because you attempted to decode a " ~
4728                 "GeneralizedTime that was encoded on too few bytes to be " ~
4729                 "correct. A valid GeneralizedTime cannot be 16 bytes. " ~
4730                 notWhatYouMeantText ~ forMoreInformationText ~
4731                 debugInformationText ~ reportBugsText
4732             );
4733 
4734         // Mandated in X.690, section 11.7.1
4735         if (this.value[$-1] != 'Z')
4736             throw new ASN1ValueException
4737             (
4738                 "This exception was thrown because you attempted to decode " ~
4739                 "a GeneralizedTime whose encoding did not terminate with a 'Z', " ~
4740                 "as is expected by the Canonical Encoding Rules (CER) and " ~
4741                 "Distinguished Encoding Rules (DER). " ~
4742                 notWhatYouMeantText ~ forMoreInformationText ~
4743                 debugInformationText ~ reportBugsText
4744             );
4745 
4746         ptrdiff_t indexOfDecimalPoint = (cast(string) this.value).indexOf('.');
4747         if (indexOfDecimalPoint != -1)
4748         {
4749             if (this.value[$-2] == '0')
4750                 throw new ASN1ValuePaddingException
4751                 (
4752                     "This exception was thrown because you attempted to decode " ~
4753                     "a GeneralizedTime that contained trailing zeroes in the " ~
4754                     "fraction-of-seconds part. " ~
4755                     notWhatYouMeantText ~ forMoreInformationText ~
4756                     debugInformationText ~ reportBugsText
4757                 );
4758 
4759             if (indexOfDecimalPoint != 14)
4760                 throw new ASN1ValueException
4761                 (
4762                     "This exception was thrown because you attempted to decode " ~
4763                     "a GeneralizedTime whose decimal point was misplaced. " ~
4764                     notWhatYouMeantText ~ forMoreInformationText ~
4765                     debugInformationText ~ reportBugsText
4766                 );
4767         }
4768         else
4769         {
4770             if ((cast(string) this.value).indexOf(',') != -1)
4771                 throw new ASN1ValueCharactersException
4772                 ("1234567890Z.", ',', "decode a GeneralizedTime");
4773         }
4774 
4775         /** NOTE:
4776             .fromISOString() MUST be called from SysTime, not DateTime. There
4777             is a subtle difference in how .fromISOString() works in both SysTime
4778             and DateTime: SysTime's accepts the "Z" at the end (indicating that
4779             the time is in GMT).
4780 
4781             If you use DateTime.fromISOString, you will get a DateTimeException
4782             whose cryptic message reads "Invalid ISO String: " followed,
4783             strangely, by only the last six characters of the string.
4784         */
4785         immutable string dt = cast(string) this.value;
4786         return cast(DateTime) SysTime.fromISOString(dt[0 .. 8].idup ~ "T" ~ dt[8 .. $].idup);
4787     }
4788 
4789     /**
4790         Encodes a DateTime.
4791 
4792         The value is just the ASCII character representation of
4793         the $(LINK https://www.iso.org/iso-8601-date-and-time-format.html,
4794         ISO 8601)-formatted timestamp.
4795 
4796         An ISO-8601 Timestamp looks like:
4797         $(UL
4798             $(LI $(MONO 19851106210627.3))
4799             $(LI $(MONO 19851106210627.3Z))
4800             $(LI $(MONO 19851106210627.3-0500))
4801         )
4802 
4803         Standards:
4804         $(UL
4805             $(LI $(LINK https://www.iso.org/iso-8601-date-and-time-format.html, ISO 8601))
4806         )
4807     */
4808     override public @property @system
4809     void generalizedTime(in DateTime value)
4810     out
4811     {
4812         assert(this.value.length > 0u);
4813         assert((cast(string) this.value).indexOf("T") == -1);
4814     }
4815     do
4816     {
4817         scope(success) this.construction = ASN1Construction.primitive;
4818         immutable SysTime st = SysTime(value, UTC());
4819         this.value = cast(ubyte[]) ((st.toUTC()).toISOString().replace("T", ""));
4820     }
4821 
4822     // Positive tests
4823     @system
4824     unittest
4825     {
4826         CERElement el = new CERElement();
4827 
4828         el.generalizedTime = DateTime(2017, 10, 3);
4829         assert(el.visibleString == "20171003000000Z");
4830 
4831         el.visibleString = "20171003000000.003Z";
4832         assertNotThrown!Exception(el.generalizedTime);
4833 
4834         el.visibleString = "20171003000000.0000003Z";
4835         assertNotThrown!Exception(el.generalizedTime);
4836     }
4837 
4838     // Negative tests
4839     @system
4840     unittest
4841     {
4842         CERElement el = new CERElement();
4843 
4844         el.visibleString = "20171003000000.Z";
4845         assertThrown!ASN1Exception(el.generalizedTime);
4846 
4847         el.visibleString = "20171003000000.0Z";
4848         assertThrown!ASN1Exception(el.generalizedTime);
4849 
4850         el.visibleString = "20171003000000.000000Z";
4851         assertThrown!ASN1Exception(el.generalizedTime);
4852 
4853         el.visibleString = "0171003000000Z";
4854         assertThrown!ASN1Exception(el.generalizedTime);
4855 
4856         el.visibleString = "2017100300000Z"; // Missing 1 trailing zero.
4857         assertThrown!ASN1Exception(el.generalizedTime);
4858     }
4859 
4860     /**
4861         Decodes an ASCII string that contains only characters between and
4862         including $(D 0x20) and $(D 0x75). Deprecated, according to page 182 of the
4863         Dubuisson book.
4864 
4865         Throws:
4866         $(UL
4867             $(LI $(D ASN1ValueCharactersException) if any non-graphical character (including space) is encoded)
4868             $(LI $(D ASN1RecursionException)
4869                 if using constructed form and the element
4870                 is constructed of too many nested constructed elements)
4871             $(LI $(D ASN1TagClassException)
4872                 if any nested primitives do not share the
4873                 same tag class as their outer constructed element)
4874             $(LI $(D ASN1TagNumberException)
4875                 if any nested primitives do not share the
4876                 same tag number as their outer constructed element)
4877         )
4878 
4879         Citations:
4880         $(UL
4881             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
4882                 $(I ASN.1: Communication between Heterogeneous Systems),
4883                 Morgan Kaufmann, 2001, pp. 175-178.)
4884             $(LI $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022))
4885             $(LI $(LINK https://www.iso.org/standard/22747.html, ISO 2022))
4886         )
4887     */
4888     override public @property @system
4889     string graphicString() const
4890     {
4891         if (this.construction == ASN1Construction.primitive)
4892         {
4893             if (this.value.length > 1000u)
4894                 throw new ASN1ValueSizeException
4895                 (0u, 1000u, this.value.length, "decode a primitively-encoded GraphicString");
4896 
4897             foreach (immutable character; this.value)
4898             {
4899                 if (!character.isGraphical && character != ' ')
4900                     throw new ASN1ValueCharactersException
4901                     ("all characters within the range 0x20 to 0x7E", character, "GraphicString");
4902             }
4903             return cast(string) this.value;
4904         }
4905         else
4906         {
4907             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
4908                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a GraphicString");
4909             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
4910 
4911             CERElement[] substrings = this.sequence;
4912             if (substrings.length < 2u)
4913                 throw new ASN1ValueException
4914                 (
4915                     "This exception was thrown because you attempted to decode " ~
4916                     "a constructed GraphicString that was constituted by fewer " ~
4917                     "than two nested elements. When using the Canonical Encoding " ~
4918                     "Rules (CER), a legitimate constructed GraphicString must " ~
4919                     "contain at least two nested elements, and each but the " ~
4920                     "last must contain exactly 1000 content octets. The last " ~
4921                     "nested element may have between 1 and 1000 content " ~
4922                     "octets inclusively. This element should have been encoded " ~
4923                     "primitively using definite-length encoding."
4924                 );
4925 
4926             foreach (substring; substrings[0 .. $-1])
4927             {
4928                 if (substring.length != 1000u)
4929                     throw new ASN1ValueSizeException
4930                     (1000u, 1000u, substring.length, "decode a substring of a constructed GraphicString");
4931             }
4932 
4933             if (substrings[$-1].length == 0u)
4934                 throw new ASN1ValueSizeException
4935                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed GraphicString");
4936 
4937             Appender!string appendy = appender!string();
4938             foreach (substring; substrings)
4939             {
4940                 if (substring.tagClass != this.tagClass)
4941                     throw new ASN1TagClassException
4942                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed GraphicString");
4943 
4944                 if (substring.construction != ASN1Construction.primitive)
4945                     throw new ASN1ConstructionException
4946                     (substring.construction, "decode a substring of a constructed GraphicString");
4947 
4948                 if (substring.tagNumber != this.tagNumber)
4949                     throw new ASN1TagNumberException
4950                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed GraphicString");
4951 
4952                 appendy.put(substring.graphicString);
4953             }
4954             return appendy.data;
4955         }
4956     }
4957 
4958     /**
4959         Encodes an ASCII string that may contain only characters between and
4960         including $(D 0x20) and $(D 0x75). Deprecated, according to page 182
4961         of the Dubuisson book.
4962 
4963         Throws:
4964         $(UL
4965             $(LI $(D ASN1ValueCharactersException) if any non-graphical character (including space) is supplied)
4966         )
4967 
4968         Citations:
4969         $(UL
4970             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
4971                 $(I ASN.1: Communication between Heterogeneous Systems),
4972                 Morgan Kaufmann, 2001, pp. 175-178.)
4973             $(LI $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022))
4974             $(LI $(LINK https://www.iso.org/standard/22747.html, ISO 2022))
4975         )
4976     */
4977     override public @property @system
4978     void graphicString(in string value)
4979     {
4980         foreach (immutable character; value)
4981         {
4982             if (!character.isGraphical && character != ' ')
4983                 throw new ASN1ValueCharactersException
4984                 ("all characters within the range 0x20 to 0x7E", character, "GraphicString");
4985         }
4986 
4987         if (value.length <= 1000u)
4988         {
4989             this.value = cast(ubyte[]) value.dup;
4990         }
4991         else
4992         {
4993             CERElement[] primitives;
4994             size_t i = 0u;
4995             while (i+1000u < value.length)
4996             {
4997                 CERElement x = new CERElement();
4998                 x.tagNumber = this.tagNumber;
4999                 x.construction = ASN1Construction.primitive;
5000                 x.value = cast(ubyte[]) value[i .. i+1000u];
5001                 primitives ~= x;
5002                 i += 1000u;
5003             }
5004 
5005             CERElement y = new CERElement();
5006             y.tagNumber = this.tagNumber;
5007             y.construction = ASN1Construction.primitive;
5008             y.value = cast(ubyte[]) value[i .. $];
5009             primitives ~= y;
5010 
5011             this.sequence = primitives;
5012             this.construction = ASN1Construction.constructed;
5013         }
5014     }
5015 
5016     @system
5017     unittest
5018     {
5019         void test(size_t length)
5020         {
5021             char[] data;
5022             data.length = length;
5023             for (size_t i = 0u; i < data.length; i++)
5024             {
5025                 data[i] = cast(char) ((i % 0x40) + 0x20);
5026             }
5027             CERElement el = new CERElement();
5028             el.graphicString = cast(string) data;
5029             assert(el.graphicString == cast(string) data);
5030         }
5031         test(0u);
5032         test(1u);
5033         test(8u);
5034         test(127u);
5035         test(128u);
5036         test(129u);
5037         test(192u);
5038         test(999u);
5039         test(1000u);
5040         test(1001u);
5041         test(2017u);
5042     }
5043 
5044     /**
5045         Decodes a string that only contains characters between and including
5046         $(D 0x20) and $(D 0x7E). (Honestly, I don't know how this differs from
5047         $(MONO GraphicalString).)
5048 
5049         Throws:
5050         $(UL
5051             $(LI $(D ASN1ValueCharactersException)
5052                 if any non-graphical character (including space) is encoded)
5053             $(LI $(D ASN1RecursionException)
5054                 if using constructed form and the element
5055                 is constructed of too many nested constructed elements)
5056             $(LI $(D ASN1TagClassException)
5057                 if any nested primitives do not share the
5058                 same tag class as their outer constructed element)
5059             $(LI $(D ASN1TagNumberException)
5060                 if any nested primitives do not share the
5061                 same tag number as their outer constructed element)
5062         )
5063     */
5064     override public @property @system
5065     string visibleString() const
5066     {
5067         if (this.construction == ASN1Construction.primitive)
5068         {
5069             if (this.value.length > 1000u)
5070                 throw new ASN1ValueSizeException
5071                 (0u, 1000u, this.value.length, "decode a primitively-encoded VisibleString");
5072 
5073             foreach (immutable character; this.value)
5074             {
5075                 if (!character.isGraphical && character != ' ')
5076                     throw new ASN1ValueCharactersException
5077                     ("all characters within the range 0x20 to 0x7E", character, "VisibleString");
5078             }
5079             return cast(string) this.value;
5080         }
5081         else
5082         {
5083             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
5084                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a VisibleString");
5085             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
5086 
5087             CERElement[] substrings = this.sequence;
5088             if (substrings.length < 2u)
5089                 throw new ASN1ValueException
5090                 (
5091                     "This exception was thrown because you attempted to decode " ~
5092                     "a constructed VisibleString that was constituted by fewer " ~
5093                     "than two nested elements. When using the Canonical Encoding " ~
5094                     "Rules (CER), a legitimate constructed VisibleString must " ~
5095                     "contain at least two nested elements, and each but the " ~
5096                     "last must contain exactly 1000 content octets. The last " ~
5097                     "nested element may have between 1 and 1000 content " ~
5098                     "octets inclusively. This element should have been encoded " ~
5099                     "primitively using definite-length encoding."
5100                 );
5101 
5102             foreach (substring; substrings[0 .. $-1])
5103             {
5104                 if (substring.length != 1000u)
5105                     throw new ASN1ValueSizeException
5106                     (1000u, 1000u, substring.length, "decode a substring of a constructed VisibleString");
5107             }
5108 
5109             if (substrings[$-1].length == 0u)
5110                 throw new ASN1ValueSizeException
5111                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed VisibleString");
5112 
5113             Appender!string appendy = appender!string();
5114             foreach (substring; substrings)
5115             {
5116                 if (substring.tagClass != this.tagClass)
5117                     throw new ASN1TagClassException
5118                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed VisibleString");
5119 
5120                 if (substring.construction != ASN1Construction.primitive)
5121                     throw new ASN1ConstructionException
5122                     (substring.construction, "decode a substring of a constructed VisibleString");
5123 
5124                 if (substring.tagNumber != this.tagNumber)
5125                     throw new ASN1TagNumberException
5126                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed VisibleString");
5127 
5128                 appendy.put(substring.visibleString);
5129             }
5130             return appendy.data;
5131         }
5132     }
5133 
5134     /**
5135         Encodes a string that only contains characters between and including
5136         $(D 0x20) and $(D 0x7E). (Honestly, I don't know how this differs from
5137         $(MONO GraphicalString).)
5138 
5139         Throws:
5140         $(UL
5141             $(LI $(D ASN1ValueCharactersException)
5142                 if any non-graphical character (including space) is supplied.)
5143         )
5144     */
5145     override public @property @system
5146     void visibleString(in string value)
5147     {
5148         foreach (immutable character; value)
5149         {
5150             if (!character.isGraphical && character != ' ')
5151                 throw new ASN1ValueCharactersException
5152                 ("all characters within the range 0x20 to 0x7E", character, "VisibleString");
5153         }
5154 
5155         if (value.length <= 1000u)
5156         {
5157             this.value = cast(ubyte[]) value.dup;
5158         }
5159         else
5160         {
5161             CERElement[] primitives;
5162             size_t i = 0u;
5163             while (i+1000u < value.length)
5164             {
5165                 CERElement x = new CERElement();
5166                 x.tagNumber = this.tagNumber;
5167                 x.construction = ASN1Construction.primitive;
5168                 x.value = cast(ubyte[]) value[i .. i+1000u];
5169                 primitives ~= x;
5170                 i += 1000u;
5171             }
5172 
5173             CERElement y = new CERElement();
5174             y.tagNumber = this.tagNumber;
5175             y.construction = ASN1Construction.primitive;
5176             y.value = cast(ubyte[]) value[i .. $];
5177             primitives ~= y;
5178 
5179             this.sequence = primitives;
5180             this.construction = ASN1Construction.constructed;
5181         }
5182     }
5183 
5184     @system
5185     unittest
5186     {
5187         void test(size_t length)
5188         {
5189             char[] data;
5190             data.length = length;
5191             for (size_t i = 0u; i < data.length; i++)
5192             {
5193                 data[i] = cast(char) ((i % 0x40) + 0x20);
5194             }
5195             CERElement el = new CERElement();
5196             el.visibleString = cast(string) data;
5197             assert(el.visibleString == cast(string) data);
5198         }
5199         test(0u);
5200         test(1u);
5201         test(8u);
5202         test(127u);
5203         test(128u);
5204         test(129u);
5205         test(192u);
5206         test(999u);
5207         test(1000u);
5208         test(1001u);
5209         test(2017u);
5210     }
5211 
5212     /**
5213         Decodes a string containing only ASCII characters. Deprecated, according
5214         to page 182 of the Dubuisson book.
5215 
5216         Throws:
5217         $(UL
5218             $(LI $(D ASN1ValueCharactersException) if any encoded character is not ASCII)
5219             $(LI $(D ASN1RecursionException)
5220                 if using constructed form and the element
5221                 is constructed of too many nested constructed elements)
5222             $(LI $(D ASN1TagClassException)
5223                 if any nested primitives do not share the
5224                 same tag class as their outer constructed element)
5225             $(LI $(D ASN1TagNumberException)
5226                 if any nested primitives do not share the
5227                 same tag number as their outer constructed element)
5228         )
5229 
5230         Citations:
5231         $(UL
5232             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
5233                 $(I ASN.1: Communication between Heterogeneous Systems),
5234                 Morgan Kaufmann, 2001, p. 182.)
5235             $(LI $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022))
5236             $(LI $(LINK https://www.iso.org/standard/22747.html, ISO 2022))
5237         )
5238     */
5239     override public @property @system
5240     string generalString() const
5241     {
5242         if (this.construction == ASN1Construction.primitive)
5243         {
5244             if (this.value.length > 1000u)
5245                 throw new ASN1ValueSizeException
5246                 (0u, 1000u, this.value.length, "decode a primitively-encoded GeneralString");
5247 
5248             foreach (immutable character; this.value)
5249             {
5250                 if (!character.isASCII)
5251                     throw new ASN1ValueCharactersException
5252                     ("all ASCII characters", character, "GeneralString");
5253             }
5254             return cast(string) this.value;
5255         }
5256         else
5257         {
5258             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
5259                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a GeneralString");
5260             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
5261 
5262             CERElement[] substrings = this.sequence;
5263             if (substrings.length < 2u)
5264                 throw new ASN1ValueException
5265                 (
5266                     "This exception was thrown because you attempted to decode " ~
5267                     "a constructed GeneralString that was constituted by fewer " ~
5268                     "than two nested elements. When using the Canonical Encoding " ~
5269                     "Rules (CER), a legitimate constructed GeneralString must " ~
5270                     "contain at least two nested elements, and each but the " ~
5271                     "last must contain exactly 1000 content octets. The last " ~
5272                     "nested element may have between 1 and 1000 content " ~
5273                     "octets inclusively. This element should have been encoded " ~
5274                     "primitively using definite-length encoding."
5275                 );
5276 
5277             foreach (substring; substrings[0 .. $-1])
5278             {
5279                 if (substring.length != 1000u)
5280                     throw new ASN1ValueSizeException
5281                     (1000u, 1000u, substring.length, "decode a substring of a constructed GeneralString");
5282             }
5283 
5284             if (substrings[$-1].length == 0u)
5285                 throw new ASN1ValueSizeException
5286                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed GeneralString");
5287 
5288             Appender!string appendy = appender!string();
5289             foreach (substring; substrings)
5290             {
5291                 if (substring.tagClass != this.tagClass)
5292                     throw new ASN1TagClassException
5293                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed GeneralString");
5294 
5295                 if (substring.construction != ASN1Construction.primitive)
5296                     throw new ASN1ConstructionException
5297                     (substring.construction, "decode a substring of a constructed GeneralString");
5298 
5299                 if (substring.tagNumber != this.tagNumber)
5300                     throw new ASN1TagNumberException
5301                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed GeneralString");
5302 
5303                 appendy.put(substring.generalString);
5304             }
5305             return appendy.data;
5306         }
5307     }
5308 
5309     /**
5310         Encodes a string containing only ASCII characters. Deprecated,
5311         according to page 182 of the Dubuisson book.
5312 
5313         Throws:
5314         $(UL
5315             $(LI $(D ASN1ValueCharactersException) if any encoded character is not ASCII)
5316         )
5317 
5318         Citations:
5319         $(UL
5320             $(LI Dubuisson, Olivier. “Basic Encoding Rules (BER).”
5321                 $(I ASN.1: Communication between Heterogeneous Systems),
5322                 Morgan Kaufmann, 2001, p. 182.)
5323         )
5324     */
5325     override public @property @system
5326     void generalString(in string value)
5327     {
5328         foreach (immutable character; value)
5329         {
5330             if (!character.isASCII)
5331                 throw new ASN1ValueCharactersException
5332                 ("all ASCII characters", character, "GeneralString");
5333         }
5334 
5335         if (value.length <= 1000u)
5336         {
5337             this.value = cast(ubyte[]) value.dup;
5338         }
5339         else
5340         {
5341             CERElement[] primitives;
5342             size_t i = 0u;
5343             while (i+1000u < value.length)
5344             {
5345                 CERElement x = new CERElement();
5346                 x.tagNumber = this.tagNumber;
5347                 x.construction = ASN1Construction.primitive;
5348                 x.value = cast(ubyte[]) value[i .. i+1000u];
5349                 primitives ~= x;
5350                 i += 1000u;
5351             }
5352 
5353             CERElement y = new CERElement();
5354             y.tagNumber = this.tagNumber;
5355             y.construction = ASN1Construction.primitive;
5356             y.value = cast(ubyte[]) value[i .. $];
5357             primitives ~= y;
5358 
5359             this.sequence = primitives;
5360             this.construction = ASN1Construction.constructed;
5361         }
5362     }
5363 
5364     @system
5365     unittest
5366     {
5367         void test(size_t length)
5368         {
5369             char[] data;
5370             data.length = length;
5371             for (size_t i = 0u; i < data.length; i++)
5372             {
5373                 data[i] = cast(char) ((i % 0x40) + 0x20);
5374             }
5375             CERElement el = new CERElement();
5376             el.generalString = cast(string) data;
5377             assert(el.generalString == cast(string) data);
5378         }
5379         test(0u);
5380         test(1u);
5381         test(8u);
5382         test(127u);
5383         test(128u);
5384         test(129u);
5385         test(192u);
5386         test(999u);
5387         test(1000u);
5388         test(1001u);
5389         test(2017u);
5390     }
5391 
5392     /**
5393         Decodes a $(MONO dstring) of UTF-32 characters.
5394 
5395         Throws:
5396         $(UL
5397             $(LI $(D ASN1ValueException)
5398                 if the encoded bytes is not evenly divisible by four)
5399             $(LI $(D ASN1RecursionException)
5400                 if using constructed form and the element
5401                 is constructed of too many nested constructed elements)
5402             $(LI $(D ASN1TagClassException)
5403                 if any nested primitives do not share the
5404                 same tag class as their outer constructed element)
5405             $(LI $(D ASN1TagNumberException)
5406                 if any nested primitives do not share the
5407                 same tag number as their outer constructed element)
5408         )
5409     */
5410     override public @property @system
5411     dstring universalString() const
5412     {
5413         if (this.construction == ASN1Construction.primitive)
5414         {
5415             if (this.value.length > 1000u)
5416                 throw new ASN1ValueSizeException
5417                 (0u, 1000u, this.value.length, "decode a primitively-encoded UniversalString");
5418 
5419             if (this.value.length == 0u) return ""d;
5420             if (this.value.length % 4u)
5421                 throw new ASN1ValueException
5422                 (
5423                     "This exception was thrown because you tried to decode " ~
5424                     "a UniversalString that contained a number of bytes that " ~
5425                     "is not divisible by four. " ~
5426                     notWhatYouMeantText ~ forMoreInformationText ~
5427                     debugInformationText ~ reportBugsText
5428                 );
5429 
5430             version (BigEndian)
5431             {
5432                 return cast(dstring) this.value;
5433             }
5434             else version (LittleEndian)
5435             {
5436                 dchar[] ret;
5437                 ret.length = (this.value.length >> 2);
5438                 foreach (immutable size_t i, ref dchar c; ret)
5439                 {
5440                     immutable size_t byteIndex = (i << 2);
5441                     *cast(ubyte[4] *) &c = [
5442                         this.value[(byteIndex + 3u)],
5443                         this.value[(byteIndex + 2u)],
5444                         this.value[(byteIndex + 1u)],
5445                         this.value[(byteIndex + 0u)]
5446                     ];
5447                 }
5448                 return cast(dstring) ret;
5449             }
5450             else
5451             {
5452                 static assert(0, "Could not determine endianness!");
5453             }
5454         }
5455         else
5456         {
5457             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
5458                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a UniversalString");
5459             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
5460 
5461             CERElement[] substrings = this.sequence;
5462             if (substrings.length < 2u)
5463                 throw new ASN1ValueException
5464                 (
5465                     "This exception was thrown because you attempted to decode " ~
5466                     "a constructed UniversalString that was constituted by fewer " ~
5467                     "than two nested elements. When using the Canonical Encoding " ~
5468                     "Rules (CER), a legitimate constructed UniversalString must " ~
5469                     "contain at least two nested elements, and each but the " ~
5470                     "last must contain exactly 1000 content octets. The last " ~
5471                     "nested element may have between 1 and 1000 content " ~
5472                     "octets inclusively. This element should have been encoded " ~
5473                     "primitively using definite-length encoding."
5474                 );
5475 
5476             foreach (substring; substrings[0 .. $-1])
5477             {
5478                 if (substring.length != 1000u)
5479                     throw new ASN1ValueSizeException
5480                     (1000u, 1000u, substring.length, "decode a substring of a constructed UniversalString");
5481             }
5482 
5483             if (substrings[$-1].length == 0u)
5484                 throw new ASN1ValueSizeException
5485                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed UniversalString");
5486 
5487             Appender!dstring appendy = appender!dstring();
5488             foreach (substring; substrings)
5489             {
5490                 if (substring.tagClass != this.tagClass)
5491                     throw new ASN1TagClassException
5492                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed UniversalString");
5493 
5494                 if (substring.construction != ASN1Construction.primitive)
5495                     throw new ASN1ConstructionException
5496                     (substring.construction, "decode a substring of a constructed UniversalString");
5497 
5498                 if (substring.tagNumber != this.tagNumber)
5499                     throw new ASN1TagNumberException
5500                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed UniversalString");
5501 
5502                 appendy.put(substring.universalString);
5503             }
5504             return appendy.data;
5505         }
5506     }
5507 
5508     /// Encodes a $(MONO dstring) of UTF-32 characters.
5509     override public @property @system
5510     void universalString(in dstring value)
5511     {
5512         if (value.length <= 250u)
5513         {
5514             version (BigEndian)
5515             {
5516                 this.value = cast(ubyte[]) value.dup;
5517             }
5518             else version (LittleEndian)
5519             {
5520                 foreach (immutable character; value)
5521                 {
5522                     ubyte[] charBytes = cast(ubyte[]) *cast(char[4] *) &character;
5523                     reverse(charBytes);
5524                     this.value ~= charBytes;
5525                 }
5526             }
5527             else
5528             {
5529                 static assert(0, "Could not determine endianness!");
5530             }
5531         }
5532         else
5533         {
5534             CERElement[] primitives;
5535             size_t i = 0u;
5536             while (i+250u < value.length)
5537             {
5538                 CERElement x = new CERElement();
5539                 x.tagNumber = this.tagNumber;
5540                 x.construction = ASN1Construction.primitive;
5541                 version (BigEndian)
5542                 {
5543                     x.value = cast(ubyte[]) value[i .. i+250u];
5544                 }
5545                 else version (LittleEndian)
5546                 {
5547                     foreach (immutable character; value[i .. i+250u])
5548                     {
5549                         ubyte[] charBytes = cast(ubyte[]) *cast(char[4] *) &character;
5550                         reverse(charBytes);
5551                         x.value ~= charBytes;
5552                     }
5553                 }
5554                 else
5555                 {
5556                     static assert(0, "Could not determine endianness!");
5557                 }
5558                 primitives ~= x;
5559                 i += 250u;
5560             }
5561 
5562             CERElement y = new CERElement();
5563             y.tagNumber = this.tagNumber;
5564             y.construction = ASN1Construction.primitive;
5565             version (BigEndian)
5566             {
5567                 y.value = cast(ubyte[]) value[i .. $];
5568             }
5569             else version (LittleEndian)
5570             {
5571                 foreach (immutable character; value[i .. $])
5572                 {
5573                     ubyte[] charBytes = cast(ubyte[]) *cast(char[4] *) &character;
5574                     reverse(charBytes);
5575                     y.value ~= charBytes;
5576                 }
5577             }
5578             else
5579             {
5580                 static assert(0, "Could not determine endianness!");
5581             }
5582             primitives ~= y;
5583 
5584             this.sequence = primitives;
5585             this.construction = ASN1Construction.constructed;
5586         }
5587     }
5588 
5589     @system
5590     unittest
5591     {
5592         void test(size_t length)
5593         {
5594             dchar[] data;
5595             data.length = length;
5596             for (size_t i = 0u; i < data.length; i++)
5597             {
5598                 data[i] = cast(dchar) ((i % 0x60u) + 0x20u);
5599             }
5600             CERElement el = new CERElement();
5601             el.universalString = cast(dstring) data;
5602             assert(el.universalString == cast(dstring) data);
5603         }
5604         test(0u);
5605         test(1u);
5606         test(8u);
5607         test(127u);
5608         test(128u);
5609         test(129u);
5610         test(192u);
5611         test(999u);
5612         test(1000u);
5613         test(1001u);
5614         test(2017u);
5615     }
5616 
5617     /**
5618         Decodes a $(MONO CharacterString), which is a constructed data type, defined
5619         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
5620             $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
5621 
5622         The specification defines $(MONO CharacterString) as:
5623 
5624         $(PRE
5625             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
5626                 identification CHOICE {
5627                     syntaxes SEQUENCE {
5628                         abstract OBJECT IDENTIFIER,
5629                         transfer OBJECT IDENTIFIER },
5630                     syntax OBJECT IDENTIFIER,
5631                     presentation-context-id INTEGER,
5632                     context-negotiation SEQUENCE {
5633                         presentation-context-id INTEGER,
5634                         transfer-syntax OBJECT IDENTIFIER },
5635                     transfer-syntax OBJECT IDENTIFIER,
5636                     fixed NULL },
5637                 string-value OCTET STRING }
5638         )
5639 
5640         This assumes $(MONO AUTOMATIC TAGS), so all of the $(MONO identification)
5641         choices will be $(MONO CONTEXT-SPECIFIC) and numbered from 0 to 5.
5642 
5643         Returns: an instance of $(D types.universal.characterstring.CharacterString).
5644 
5645         Throws:
5646         $(UL
5647             $(LI $(D ASN1ValueException) if encoded $(MONO CharacterString) has too few or too many
5648                 elements, or if $(MONO syntaxes) or $(MONO context-negotiation) element has
5649                 too few or too many elements)
5650             $(LI $(D ASN1ValueSizeException) if encoded $(MONO INTEGER) is too large to decode)
5651             $(LI $(D ASN1RecursionException) if using constructed form and the element
5652                 is constructed of too many nested constructed elements)
5653             $(LI $(D ASN1TagClassException) if any nested primitives do not have the
5654                 correct tag class)
5655             $(LI $(D ASN1ConstructionException) if any element has the wrong construction)
5656             $(LI $(D ASN1TagNumberException) if any nested primitives do not have the
5657                 correct tag number)
5658         )
5659     */
5660     override public @property @system
5661     CharacterString characterString() const
5662     {
5663         if (this.construction != ASN1Construction.constructed)
5664             throw new ASN1ConstructionException
5665             (this.construction, "decode an INTEGER");
5666 
5667         const CERElement[] components = this.sequence;
5668         ASN1ContextSwitchingTypeID identification = ASN1ContextSwitchingTypeID();
5669 
5670         if (components.length != 2u)
5671             throw new ASN1ValueException
5672             (
5673                 "This exception was thrown because you attempted to decode " ~
5674                 "a CharacterString that contained too many or too few elements. " ~
5675                 "A CharacterString should have only two elements: " ~
5676                 "an identification CHOICE, and a data-value OCTET STRING, " ~
5677                 "in that order. " ~
5678                 notWhatYouMeantText ~ forMoreInformationText ~
5679                 debugInformationText ~ reportBugsText
5680             );
5681 
5682         if (components[0].tagClass != ASN1TagClass.contextSpecific)
5683             throw new ASN1TagClassException
5684             (
5685                 [ ASN1TagClass.contextSpecific ],
5686                 components[0].tagClass,
5687                 "decode the first component of a CharacterString"
5688             );
5689 
5690         if (components[1].tagClass != ASN1TagClass.contextSpecific)
5691             throw new ASN1TagClassException
5692             (
5693                 [ ASN1TagClass.contextSpecific ],
5694                 components[1].tagClass,
5695                 "decode the second component of a CharacterString"
5696             );
5697 
5698         /* NOTE:
5699             See page 224 of Dubuisson, item 11:
5700             It sounds like, even if you have an ABSENT constraint applied,
5701             all automatically-tagged items still have the same numbers as
5702             though the constrained component were PRESENT.
5703         */
5704         if (components[0].tagNumber != 0u)
5705             throw new ASN1TagNumberException
5706             ([ 0u ], components[0].tagNumber, "decode the first component of a CharacterString");
5707 
5708         if (components[1].tagNumber != 2u)
5709             throw new ASN1TagNumberException
5710             ([ 2u ], components[1].tagNumber, "decode the second component of a CharacterString");
5711 
5712         ubyte[] bytes = components[0].value.dup;
5713         const CERElement identificationChoice = new CERElement(bytes);
5714         switch (identificationChoice.tagNumber)
5715         {
5716             case (0u): // syntaxes
5717             {
5718                 if (identificationChoice.construction != ASN1Construction.constructed)
5719                     throw new ASN1ConstructionException
5720                     (identificationChoice.construction, "decode the syntaxes component of a CharacterString");
5721 
5722                 const CERElement[] syntaxesComponents = identificationChoice.sequence;
5723 
5724                 if (syntaxesComponents.length != 2u)
5725                     throw new ASN1ValueException
5726                     (
5727                         "This exception was thrown because you attempted to " ~
5728                         "decode a CharacterString whose syntaxes component " ~
5729                         "contained an invalid number of elements. The " ~
5730                         "syntaxes component should contain abstract and transfer " ~
5731                         "syntax OBJECT IDENTIFIERS, in that order. " ~
5732                         notWhatYouMeantText ~ forMoreInformationText ~
5733                         debugInformationText ~ reportBugsText
5734                     );
5735 
5736                 // Class Validation
5737                 if (syntaxesComponents[0].tagClass != ASN1TagClass.contextSpecific)
5738                     throw new ASN1TagClassException
5739                     (
5740                         [ ASN1TagClass.contextSpecific ],
5741                         syntaxesComponents[0].tagClass,
5742                         "decode the first syntaxes component of a CharacterString"
5743                     );
5744 
5745                 if (syntaxesComponents[1].tagClass != ASN1TagClass.contextSpecific)
5746                     throw new ASN1TagClassException
5747                     (
5748                         [ ASN1TagClass.contextSpecific ],
5749                         syntaxesComponents[1].tagClass,
5750                         "decode the second syntaxes component of a CharacterString"
5751                     );
5752 
5753                 // Construction Validation
5754                 if (syntaxesComponents[0].construction != ASN1Construction.primitive)
5755                     throw new ASN1ConstructionException
5756                     (syntaxesComponents[0].construction, "decode the first syntaxes component of a CharacterString");
5757 
5758                 if (syntaxesComponents[1].construction != ASN1Construction.primitive)
5759                     throw new ASN1ConstructionException
5760                     (syntaxesComponents[1].construction, "decode the second syntaxes component of a CharacterString");
5761 
5762                 // Number Validation
5763                 if (syntaxesComponents[0].tagNumber != 0u)
5764                     throw new ASN1TagNumberException
5765                     (
5766                         [ 0u ],
5767                         syntaxesComponents[0].tagNumber,
5768                         "decode the first syntaxes component of a CharacterString"
5769                     );
5770 
5771                 if (syntaxesComponents[1].tagNumber != 1u)
5772                     throw new ASN1TagNumberException
5773                     (
5774                         [ 1u ],
5775                         syntaxesComponents[1].tagNumber,
5776                         "decode the second syntaxes component of a CharacterString"
5777                     );
5778 
5779                 identification.syntaxes  = ASN1Syntaxes(
5780                     syntaxesComponents[0].objectIdentifier,
5781                     syntaxesComponents[1].objectIdentifier
5782                 );
5783 
5784                 break;
5785             }
5786             case (1u): // syntax
5787             {
5788                 identification.syntax = identificationChoice.objectIdentifier;
5789                 break;
5790             }
5791             case (4u): // transfer-syntax
5792             {
5793                 identification.transferSyntax = identificationChoice.objectIdentifier;
5794                 break;
5795             }
5796             case (5u): // fixed
5797             {
5798                 identification.fixed = true;
5799                 break;
5800             }
5801             default:
5802                 throw new ASN1TagNumberException
5803                 (
5804                     [ 0u, 1u, 4u, 5u ],
5805                     identificationChoice.tagNumber,
5806                     "decode a CharacterString identification"
5807                 );
5808         }
5809 
5810         CharacterString cs = CharacterString();
5811         cs.identification = identification;
5812         cs.stringValue = components[1].octetString;
5813         return cs;
5814     }
5815 
5816     /**
5817         Encodes a $(MONO CharacterString), which is a constructed data type, defined
5818         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
5819             $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
5820 
5821         The specification defines $(MONO CharacterString) as:
5822 
5823         $(PRE
5824             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
5825                 identification CHOICE {
5826                     syntaxes SEQUENCE {
5827                         abstract OBJECT IDENTIFIER,
5828                         transfer OBJECT IDENTIFIER },
5829                     syntax OBJECT IDENTIFIER,
5830                     presentation-context-id INTEGER,
5831                     context-negotiation SEQUENCE {
5832                         presentation-context-id INTEGER,
5833                         transfer-syntax OBJECT IDENTIFIER },
5834                     transfer-syntax OBJECT IDENTIFIER,
5835                     fixed NULL },
5836                 string-value OCTET STRING }
5837         )
5838 
5839         This assumes $(MONO AUTOMATIC TAGS), so all of the $(MONO identification)
5840         choices will be $(MONO CONTEXT-SPECIFIC) and numbered from 0 to 5.
5841     */
5842     override public @property @system
5843     void characterString(in CharacterString value)
5844     out
5845     {
5846         assert(this.value.length > 0u);
5847     }
5848     do
5849     {
5850         scope(success) this.construction = ASN1Construction.constructed;
5851         CERElement identification = new CERElement();
5852         identification.tagClass = ASN1TagClass.contextSpecific;
5853         identification.tagNumber = 0u; // CHOICE is EXPLICIT, even with automatic tagging.
5854 
5855         CERElement identificationChoice = new CERElement();
5856         identificationChoice.tagClass = ASN1TagClass.contextSpecific;
5857         if (!(value.identification.syntaxes.isNull))
5858         {
5859             CERElement abstractSyntax = new CERElement();
5860             abstractSyntax.tagClass = ASN1TagClass.contextSpecific;
5861             abstractSyntax.tagNumber = 0u;
5862             abstractSyntax.objectIdentifier = value.identification.syntaxes.abstractSyntax;
5863 
5864             CERElement transferSyntax = new CERElement();
5865             transferSyntax.tagClass = ASN1TagClass.contextSpecific;
5866             transferSyntax.tagNumber = 1u;
5867             transferSyntax.objectIdentifier = value.identification.syntaxes.transferSyntax;
5868 
5869             identificationChoice.construction = ASN1Construction.constructed;
5870             identificationChoice.tagNumber = 0u;
5871             identificationChoice.sequence = [ abstractSyntax, transferSyntax ];
5872         }
5873         else if (!(value.identification.syntax.isNull))
5874         {
5875             identificationChoice.tagNumber = 1u;
5876             identificationChoice.objectIdentifier = value.identification.syntax;
5877         }
5878         else if (!(value.identification.transferSyntax.isNull))
5879         {
5880             identificationChoice.tagNumber = 4u;
5881             identificationChoice.objectIdentifier = value.identification.transferSyntax;
5882         }
5883         else
5884         {
5885             identificationChoice.tagNumber = 5u;
5886             identificationChoice.value = [];
5887         }
5888 
5889         // This makes identification: [CONTEXT 0][L][CONTEXT #][L][V]
5890         identification.value = cast(ubyte[]) identificationChoice;
5891 
5892         CERElement stringValue = new CERElement();
5893         stringValue.tagClass = ASN1TagClass.contextSpecific;
5894         stringValue.tagNumber = 2u;
5895         stringValue.octetString = value.stringValue;
5896 
5897         this.sequence = [ identification, stringValue ];
5898     }
5899 
5900     /* NOTE:
5901         This unit test had to be moved out of ASN1Element because CER and CER
5902         do not support encoding of context-negotiation in CharacterString.
5903 
5904         This unit test ensures that, if you attempt to create a CharacterString
5905         with context-negotiation as the CHOICE of identification, the
5906         encoded CharacterString's identification defaults to fixed.
5907     */
5908     @system
5909     unittest
5910     {
5911         ASN1ContextNegotiation cn = ASN1ContextNegotiation();
5912         cn.presentationContextID = 27L;
5913         cn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
5914 
5915         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
5916         id.contextNegotiation = cn;
5917 
5918         CharacterString input = CharacterString();
5919         input.identification = id;
5920         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
5921 
5922         CERElement el = new CERElement();
5923         el.characterString = input;
5924         CharacterString output = el.characterString;
5925         assert(output.identification.fixed == true);
5926         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
5927     }
5928 
5929     // Inspired by CVE-2017-9023
5930     @system
5931     unittest
5932     {
5933         ubyte[] data = [ // This is valid.
5934             0x1Eu, 0x0Au, // CharacterString, Length 11
5935                 0x80u, 0x02u, // CHOICE
5936                     0x85u, 0x00u, // NULL
5937                 0x82u, 0x04u, 0x01u, 0x02u, 0x03u, 0x04u ]; // OCTET STRING
5938 
5939         // Valid values for data[2]: 80
5940         for (ubyte i = 0x81u; i < 0x9Eu; i++)
5941         {
5942             data[2] = i;
5943             size_t x = 0u;
5944             CERElement el = new CERElement(x, data);
5945             assertThrown!ASN1Exception(el.characterString);
5946         }
5947 
5948         // Valid values for data[4]: 80-85
5949         for (ubyte i = 0x86u; i < 0x9Eu; i++)
5950         {
5951             data[4] = i;
5952             size_t x = 0u;
5953             CERElement el = new CERElement(x, data);
5954             assertThrown!ASN1Exception(el.characterString);
5955         }
5956 
5957         // Valid values for data[6]: 82
5958         for (ubyte i = 0x83u; i < 0x9Eu; i++)
5959         {
5960             data[6] = i;
5961             size_t x = 0u;
5962             CERElement el = new CERElement(x, data);
5963             assertThrown!ASN1Exception(el.characterString);
5964         }
5965     }
5966 
5967     /**
5968         Decodes a $(MONO wstring) of UTF-16 characters.
5969 
5970         Throws:
5971         $(UL
5972             $(LI $(D ASN1ValueException)
5973                 if the encoded bytes is not evenly divisible by two)
5974             $(LI $(D ASN1RecursionException)
5975                 if using constructed form and the element
5976                 is constructed of too many nested constructed elements)
5977             $(LI $(D ASN1TagClassException)
5978                 if any nested primitives do not share the
5979                 same tag class as their outer constructed element)
5980             $(LI $(D ASN1TagNumberException)
5981                 if any nested primitives do not share the
5982                 same tag number as their outer constructed element)
5983         )
5984     */
5985     override public @property @system
5986     wstring basicMultilingualPlaneString() const
5987     {
5988         if (this.construction == ASN1Construction.primitive)
5989         {
5990             if (this.value.length > 1000u)
5991                 throw new ASN1ValueSizeException
5992                 (0u, 1000u, this.value.length, "decode a primitively-encoded BMPString");
5993 
5994             if (this.value.length == 0u) return ""w;
5995             if (this.value.length % 2u)
5996                 throw new ASN1ValueException
5997                 (
5998                     "This exception was thrown because you tried to decode " ~
5999                     "a BMPString that contained a number of bytes that " ~
6000                     "is not divisible by two. " ~
6001                     notWhatYouMeantText ~ forMoreInformationText ~
6002                     debugInformationText ~ reportBugsText
6003                 );
6004 
6005             version (BigEndian)
6006             {
6007                 return cast(wstring) this.value;
6008             }
6009             else version (LittleEndian)
6010             {
6011                 wchar[] ret;
6012                 ret.length = (this.value.length >> 1);
6013                 foreach (immutable size_t i, ref wchar c; ret)
6014                 {
6015                     immutable size_t byteIndex = (i << 1);
6016                     *cast(ubyte[2] *) &c = [
6017                         this.value[(byteIndex + 1u)],
6018                         this.value[(byteIndex + 0u)]
6019                     ];
6020                 }
6021                 return cast(wstring) ret;
6022             }
6023             else
6024             {
6025                 static assert(0, "Could not determine endianness!");
6026             }
6027         }
6028         else
6029         {
6030             if (this.valueRecursionCount++ == this.nestingRecursionLimit)
6031                 throw new ASN1RecursionException(this.nestingRecursionLimit, "parse a BMPString");
6032             scope(exit) this.valueRecursionCount--; // So exceptions do not leave residual recursion.
6033 
6034             CERElement[] substrings = this.sequence;
6035             if (substrings.length < 2u)
6036                 throw new ASN1ValueException
6037                 (
6038                     "This exception was thrown because you attempted to decode " ~
6039                     "a constructed BMPString that was constituted by fewer " ~
6040                     "than two nested elements. When using the Canonical Encoding " ~
6041                     "Rules (CER), a legitimate constructed BMPString must " ~
6042                     "contain at least two nested elements, and each but the " ~
6043                     "last must contain exactly 1000 content octets. The last " ~
6044                     "nested element may have between 1 and 1000 content " ~
6045                     "octets inclusively. This element should have been encoded " ~
6046                     "primitively using definite-length encoding."
6047                 );
6048 
6049             foreach (substring; substrings[0 .. $-1])
6050             {
6051                 if (substring.length != 1000u)
6052                     throw new ASN1ValueSizeException
6053                     (1000u, 1000u, substring.length, "decode a substring of a constructed BMPString");
6054             }
6055 
6056             if (substrings[$-1].length == 0u)
6057                 throw new ASN1ValueSizeException
6058                 (1u, 1000u, substrings[$-1].length, "decode the last substring of a constructed BMPString");
6059 
6060             Appender!wstring appendy = appender!wstring();
6061             foreach (substring; substrings)
6062             {
6063                 if (substring.tagClass != this.tagClass)
6064                     throw new ASN1TagClassException
6065                     ([ this.tagClass ], substring.tagClass, "decode a substring of a constructed BMPString");
6066 
6067                 if (substring.construction != ASN1Construction.primitive)
6068                     throw new ASN1ConstructionException
6069                     (substring.construction, "decode a substring of a constructed BMPString");
6070 
6071                 if (substring.tagNumber != this.tagNumber)
6072                     throw new ASN1TagNumberException
6073                     ([ this.tagNumber ], substring.tagNumber, "decode a substring of a constructed BMPString");
6074 
6075                 appendy.put(substring.bmpString);
6076             }
6077             return appendy.data;
6078         }
6079     }
6080 
6081     /// Encodes a $(MONO wstring) of UTF-16 characters.
6082     override public @property @system
6083     void basicMultilingualPlaneString(in wstring value)
6084     {
6085         if (value.length <= 500u)
6086         {
6087             version (BigEndian)
6088             {
6089                 this.value = cast(ubyte[]) value.dup;
6090             }
6091             else version (LittleEndian)
6092             {
6093                 foreach (immutable character; value)
6094                 {
6095                     ubyte[] charBytes = cast(ubyte[]) *cast(char[2] *) &character;
6096                     reverse(charBytes);
6097                     this.value ~= charBytes;
6098                 }
6099             }
6100             else
6101             {
6102                 static assert(0, "Could not determine endianness!");
6103             }
6104         }
6105         else
6106         {
6107             CERElement[] primitives;
6108             size_t i = 0u;
6109             while (i+500u < value.length)
6110             {
6111                 CERElement x = new CERElement();
6112                 x.tagNumber = this.tagNumber;
6113                 x.construction = ASN1Construction.primitive;
6114                 version (BigEndian)
6115                 {
6116                     x.value = cast(ubyte[]) value[i .. i+500u];
6117                 }
6118                 else version (LittleEndian)
6119                 {
6120                     foreach (immutable character; value[i .. i+500u])
6121                     {
6122                         ubyte[] charBytes = cast(ubyte[]) *cast(char[2] *) &character;
6123                         reverse(charBytes);
6124                         x.value ~= charBytes;
6125                     }
6126                 }
6127                 else
6128                 {
6129                     static assert(0, "Could not determine endianness!");
6130                 }
6131                 primitives ~= x;
6132                 i += 500u;
6133             }
6134 
6135             CERElement y = new CERElement();
6136             y.tagNumber = this.tagNumber;
6137             y.construction = ASN1Construction.primitive;
6138             version (BigEndian)
6139             {
6140                 y.value = cast(ubyte[]) value[i .. $];
6141             }
6142             else version (LittleEndian)
6143             {
6144                 foreach (immutable character; value[i .. $])
6145                 {
6146                     ubyte[] charBytes = cast(ubyte[]) *cast(char[2] *) &character;
6147                     reverse(charBytes);
6148                     y.value ~= charBytes;
6149                 }
6150             }
6151             else
6152             {
6153                 static assert(0, "Could not determine endianness!");
6154             }
6155             primitives ~= y;
6156 
6157             this.sequence = primitives;
6158             this.construction = ASN1Construction.constructed;
6159         }
6160     }
6161 
6162     @system
6163     unittest
6164     {
6165         void test(size_t length)
6166         {
6167             wchar[] data;
6168             data.length = length;
6169             for (size_t i = 0u; i < data.length; i++)
6170             {
6171                 data[i] = cast(wchar) ((i % 0x60u) + 0x20u);
6172             }
6173             CERElement el = new CERElement();
6174             el.bmpString = cast(wstring) data;
6175             assert(el.bmpString == cast(wstring) data);
6176         }
6177         test(0u);
6178         test(1u);
6179         test(8u);
6180         test(127u);
6181         test(128u);
6182         test(129u);
6183         test(192u);
6184         test(999u);
6185         test(1000u);
6186         test(1001u);
6187         test(2017u);
6188     }
6189 
6190     /// Creates an $(MONO END OF CONTENT) by default
6191     public @safe @nogc nothrow
6192     this
6193     (
6194         ASN1TagClass tagClass = ASN1TagClass.universal,
6195         ASN1Construction construction = ASN1Construction.primitive,
6196         size_t tagNumber = 0u
6197     )
6198     {
6199         this.tagClass = tagClass;
6200         this.construction = construction;
6201         this.tagNumber = tagNumber;
6202         this.value = [];
6203     }
6204 
6205     /**
6206         Creates a $(D CERElement) from the supplied bytes, inferring that the first
6207         byte is the type tag. The supplied $(D ubyte[]) array is "chomped" by
6208         reference, so the original array will grow shorter as $(D CERElement)s are
6209         generated.
6210 
6211         Throws:
6212             All of the same exceptions as $(D fromBytes())
6213 
6214         Examples:
6215         ---
6216         // Decoding looks like:
6217         CERElement[] result;
6218         while (bytes.length > 0)
6219             result ~= new CERElement(bytes);
6220 
6221         // Encoding looks like:
6222         ubyte[] result;
6223         foreach (cv; cervalues)
6224         {
6225             result ~= cast(ubyte[]) cv;
6226         }
6227         ---
6228     */
6229     public @system
6230     this (ref ubyte[] bytes)
6231     {
6232         size_t bytesRead = this.fromBytes(bytes);
6233         bytes = bytes[bytesRead .. $];
6234     }
6235 
6236     /**
6237         Creates a $(D CERElement) from the supplied bytes, inferring that the first
6238         byte is the type tag. Unlike the construct that accepts a $(D ubyte[])
6239         reference, this constructor does not "chomp" the array. This constructor
6240         expects the encoded bytes to encode only one $(CERElement). If there are
6241         any remaining bytes after decoding, an exception is thrown.
6242 
6243         Throws:
6244             All of the same exceptions as $(D fromBytes()), but also throws an
6245             $(D ASN1LengthException) if there are excess bytes.
6246     */
6247     public @system
6248     this (in ubyte[] bytes)
6249     {
6250         immutable size_t bytesRead = this.fromBytes(bytes);
6251         if (bytesRead != bytes.length)
6252             throw new ASN1LengthException
6253             (
6254                 "This exception was thrown because you attempted to decode " ~
6255                 "a single ASN.1 element that was encoded on too many bytes. " ~
6256                 "The entire element was decoded from " ~ text(bytesRead) ~ " " ~
6257                 "bytes, but " ~ text(bytes.length) ~ " bytes were supplied to " ~
6258                 "decode."
6259             );
6260     }
6261 
6262     /**
6263         Creates a $(D CERElement) from the supplied bytes, inferring that the first
6264         byte is the type tag. The supplied $(D ubyte[]) array is read, starting
6265         from the index specified by $(D bytesRead), and increments
6266         $(D bytesRead) by the number of bytes read.
6267 
6268         Throws:
6269             All of the same exceptions as $(D fromBytes())
6270 
6271         Examples:
6272         ---
6273         // Decoding looks like:
6274         CERElement[] result;
6275         size_t i = 0u;
6276         while (i < bytes.length)
6277             result ~= new CERElement(i, bytes);
6278 
6279         // Encoding looks like:
6280         ubyte[] result;
6281         foreach (cv; cervalues)
6282         {
6283             result ~= cast(ubyte[]) cv;
6284         }
6285         ---
6286     */
6287     public @system
6288     this (ref size_t bytesRead, in ubyte[] bytes)
6289     {
6290         bytesRead += this.fromBytes(bytes[bytesRead .. $].dup);
6291     }
6292 
6293     /**
6294         Returns: the number of bytes read
6295 
6296         Throws:
6297         $(UL
6298             $(LI $(D ASN1TagPaddingException) if the tag number is "padded" with
6299                 "leading zero bytes" ($(D 0x80u)))
6300             $(LI $(D ASN1TagOverflowException) if the tag number is too large to
6301                 fit into a $(D size_t))
6302             $(LI $(D ASN1LengthUndefinedException) if the reserved length byte of
6303                 $(D 0xFF) is encountered)
6304             $(LI $(D ASN1LengthOverflowException) if the length is too large to fit
6305                 into a $(D size_t))
6306             $(LI $(D ASN1TruncationException) if the tag, length, or value appear to
6307                 be truncated)
6308             $(LI $(D ASN1ConstructionException) if the length is indefinite, but the
6309                 element is marked as being encoded primitively)
6310             $(LI $(D ASN1RecursionException) if, when trying to determine the end of
6311                 an indefinite-length encoded element, the parser has to recurse
6312                 too deep)
6313         )
6314     */
6315     public
6316     size_t fromBytes (in ubyte[] bytes)
6317     {
6318         if (bytes.length < 2u)
6319             throw new ASN1TruncationException
6320             (
6321                 2u,
6322                 bytes.length,
6323                 "decode the tag of a Canonical Encoding Rules (CER) encoded element"
6324             );
6325 
6326         // Index of what we are currently parsing.
6327         size_t cursor = 0u;
6328 
6329         switch (bytes[cursor] & 0b11000000u)
6330         {
6331             case (0b00000000u): this.tagClass = ASN1TagClass.universal; break;
6332             case (0b01000000u): this.tagClass = ASN1TagClass.application; break;
6333             case (0b10000000u): this.tagClass = ASN1TagClass.contextSpecific; break;
6334             case (0b11000000u): this.tagClass = ASN1TagClass.privatelyDefined; break;
6335             default: assert(0, "Impossible tag class appeared!");
6336         }
6337 
6338         this.construction = ((bytes[cursor] & 0b00100000u) ?
6339             ASN1Construction.constructed : ASN1Construction.primitive);
6340 
6341         this.tagNumber = (bytes[cursor] & 0b00011111u);
6342         cursor++;
6343         if (this.tagNumber >= 31u)
6344         {
6345             /* NOTE:
6346                 Section 8.1.2.4.2, point C of the International
6347                 Telecommunications Union's X.690 specification says:
6348 
6349                 "bits 7 to 1 of the first subsequent octet shall not all be zero."
6350 
6351                 in reference to the bytes used to encode the tag number in long
6352                 form, which happens when the least significant five bits of the
6353                 first byte are all set.
6354 
6355                 This essentially means that the long-form tag number must be
6356                 encoded on the fewest possible octets. If the first byte is
6357                 0x80, then it is not encoded on the fewest possible octets.
6358             */
6359             if (bytes[cursor] == 0b10000000u)
6360                 throw new ASN1TagPaddingException
6361                 (
6362                     "This exception was thrown because you attempted to decode " ~
6363                     "a Canonical Encoding Rules (CER) encoded element whose tag " ~
6364                     "number was encoded in long form in the octets following " ~
6365                     "the first octet of the type tag, and whose tag number " ~
6366                     "was encoded with a 'leading zero' byte, 0x80. When " ~
6367                     "using Canonical Encoding Rules (CER), the tag number must " ~
6368                     "be encoded on the smallest number of octets possible, " ~
6369                     "which the inclusion of leading zero bytes necessarily " ~
6370                     "contradicts. " ~
6371                     forMoreInformationText ~ debugInformationText ~ reportBugsText
6372                 );
6373 
6374             this.tagNumber = 0u;
6375 
6376             // This loop looks for the end of the encoded tag number.
6377             immutable size_t limit = ((bytes.length-1 >= size_t.sizeof) ? size_t.sizeof : bytes.length-1);
6378             while (cursor < limit)
6379             {
6380                 if (!(bytes[cursor++] & 0x80u)) break;
6381             }
6382 
6383             if (bytes[cursor-1] & 0x80u)
6384             {
6385                 if (limit == bytes.length-1)
6386                 {
6387                     throw new ASN1TruncationException
6388                     (size_t.max, bytes.length, "decode an ASN.1 tag number");
6389                 }
6390                 else
6391                 {
6392                     throw new ASN1TagOverflowException
6393                     (
6394                         "This exception was thrown because you attempted to decode " ~
6395                         "a Canonical Encoding Rules (CER) encoded element that encoded " ~
6396                         "a tag number that was either too large to decode or " ~
6397                         "terminated prematurely."
6398                     );
6399                 }
6400             }
6401 
6402             for (size_t i = 1; i < cursor; i++)
6403             {
6404                 this.tagNumber <<= 7;
6405                 this.tagNumber |= cast(size_t) (bytes[i] & 0x7Fu);
6406             }
6407         }
6408 
6409         // Length
6410         if ((bytes[cursor] & 0x80u) == 0x80u)
6411         {
6412             immutable ubyte numberOfLengthOctets = (bytes[cursor] & 0x7Fu);
6413             if (numberOfLengthOctets) // Definite Long or Reserved
6414             {
6415                 if (numberOfLengthOctets == 0b01111111u) // Reserved
6416                     throw new ASN1LengthUndefinedException();
6417 
6418                 // Definite Long, if it has made it this far
6419 
6420                 if (this.construction == ASN1Construction.constructed)
6421                     throw new ASN1ConstructionException
6422                     (this.construction, "decode a long definite-length Canonical Encoding Rules (CER) encoded element");
6423 
6424                 if (numberOfLengthOctets > size_t.sizeof)
6425                     throw new ASN1LengthOverflowException();
6426 
6427                 if (cursor + numberOfLengthOctets >= bytes.length)
6428                     throw new ASN1TruncationException
6429                     (
6430                         numberOfLengthOctets,
6431                         ((bytes.length - 1) - cursor),
6432                         "decode the length of a Basic Encoding Rules (BER) encoded element"
6433                     );
6434 
6435                 if (bytes[++cursor] == 0x00u)
6436                     throw new ASN1LengthException
6437                     (
6438                         "This exception was thrown because you attempted to " ~
6439                         "decode a Canonical Encoding Rules (CER) encoded " ~
6440                         "element whose length was encoded in definite long " ~
6441                         "form, and encoded on more octets than necessary, " ~
6442                         "which is prohibited by the specification for " ~
6443                         "Canonical Encoding Rules (CER). " ~
6444                         forMoreInformationText ~ debugInformationText ~ reportBugsText
6445                     );
6446 
6447                 ubyte[] lengthNumberOctets;
6448                 lengthNumberOctets.length = size_t.sizeof;
6449                 for (ubyte i = numberOfLengthOctets; i > 0u; i--)
6450                 {
6451                     lengthNumberOctets[size_t.sizeof-i] = bytes[cursor+numberOfLengthOctets-i];
6452                 }
6453                 version (LittleEndian) reverse(lengthNumberOctets);
6454                 size_t length = *cast(size_t *) lengthNumberOctets.ptr;
6455 
6456                 if (length <= 127u)
6457                     throw new ASN1LengthException
6458                     (
6459                         "This exception was thrown because you attempted to " ~
6460                         "decode a Canonical Encoding Rules (CER) encoded " ~
6461                         "element whose length was encoded in definite long " ~
6462                         "form, and encoded on more octets than necessary, " ~
6463                         "which is prohibited by the specification for " ~
6464                         "Canonical Encoding Rules (CER). Specifically, it " ~
6465                         "was encoded in definite-long form when it was less " ~
6466                         "than or equal to 127, which could have been encoded " ~
6467                         "in definite-short form. " ~
6468                         forMoreInformationText ~ debugInformationText ~ reportBugsText
6469                     );
6470 
6471                 if ((cursor + length) < cursor) // This catches an overflow.
6472                     throw new ASN1LengthException
6473                     (
6474                         "This exception was thrown because you attempted to " ~
6475                         "decode a Canonical Encoding Rules (CER) encoded element " ~
6476                         "that indicated that it was exceedingly large--so " ~
6477                         "large, in fact, that it cannot be stored on this " ~
6478                         "computer (18 exabytes if you are on a 64-bit system). " ~
6479                         "This may indicate that the data you attempted to " ~
6480                         "decode was either corrupted, malformed, or deliberately " ~
6481                         "crafted to hack you. You would be wise to ensure that " ~
6482                         "you are running the latest stable version of this " ~
6483                         "library. "
6484                     );
6485 
6486                 cursor += (numberOfLengthOctets);
6487 
6488                 if ((cursor + length) > bytes.length)
6489                     throw new ASN1TruncationException
6490                     (
6491                         length,
6492                         (bytes.length - cursor),
6493                         "decode the value of a Canonical Encoding Rules (CER) encoded element"
6494                     );
6495 
6496                 this.value = bytes[cursor .. cursor+length].dup;
6497                 return (cursor + length);
6498             }
6499             else // Indefinite
6500             {
6501                 if (this.construction != ASN1Construction.constructed)
6502                     throw new ASN1ConstructionException
6503                     (this.construction, "decode an indefinite-length element");
6504 
6505                 if (++(this.lengthRecursionCount) > this.nestingRecursionLimit)
6506                 {
6507                     this.lengthRecursionCount = 0u;
6508                     throw new ASN1RecursionException
6509                     (
6510                         this.nestingRecursionLimit,
6511                         "decode a Canonical Encoding Rules (CER) encoded element " ~
6512                         "whose value was encoded with indefinite length form " ~
6513                         "and constructed recursively from deeply nested elements"
6514                     );
6515                 }
6516 
6517                 immutable size_t startOfValue = ++cursor;
6518                 size_t sentinel = cursor; // Used to track the length of the nested elements.
6519                 while (sentinel < bytes.length)
6520                 {
6521                     CERElement child = new CERElement(sentinel, bytes);
6522                     if
6523                     (
6524                         child.tagClass == ASN1TagClass.universal &&
6525                         child.construction == ASN1Construction.primitive &&
6526                         child.tagNumber == ASN1UniversalType.endOfContent &&
6527                         child.length == 0u
6528                     )
6529                     break;
6530                 }
6531 
6532                 if (sentinel == bytes.length && (bytes[sentinel-1] != 0x00u || bytes[sentinel-2] != 0x00u))
6533                     throw new ASN1TruncationException
6534                     (
6535                         length,
6536                         (length + 2u),
6537                         "find the END OF CONTENT octets (two consecutive null " ~
6538                         "bytes) of an indefinite-length encoded " ~
6539                         "Canonical Encoding Rules (CER) element"
6540                     );
6541 
6542                 this.lengthRecursionCount--;
6543                 this.value = bytes[startOfValue .. sentinel-2u].dup;
6544                 return (sentinel);
6545             }
6546         }
6547         else // Definite Short
6548         {
6549             if (this.construction == ASN1Construction.constructed)
6550                 throw new ASN1ConstructionException
6551                 (this.construction, "decode a short definite-length Canonical Encoding Rules (CER) encoded element");
6552 
6553             ubyte length = (bytes[cursor++] & 0x7Fu);
6554 
6555             if ((cursor + length) > bytes.length)
6556                 throw new ASN1TruncationException
6557                 (
6558                     length,
6559                     ((bytes.length - 1) - cursor),
6560                     "decode the value of a Canonical Encoding Rules (CER) encoded element"
6561                 );
6562 
6563             this.value = bytes[cursor .. cursor+length].dup;
6564             return (cursor + length);
6565         }
6566     }
6567 
6568     /**
6569         This differs from $(D this.value) in that
6570         $(D this.value) only returns the value octets, whereas
6571         $(D this.toBytes) returns the type tag, length tag / octets,
6572         and the value octets, all concatenated.
6573 
6574         This is the exact same as $(D this.opCast!(ubyte[])()).
6575 
6576         Returns: type tag, length tag, and value, all concatenated as a $(D ubyte) array.
6577     */
6578     public @property @system nothrow
6579     ubyte[] toBytes() const
6580     {
6581         ubyte[] tagBytes = [ 0x00u ];
6582         tagBytes[0] |= cast(ubyte) this.tagClass;
6583         tagBytes[0] |= cast(ubyte) this.construction;
6584 
6585         if (this.tagNumber < 31u)
6586         {
6587             tagBytes[0] |= cast(ubyte) this.tagNumber;
6588         }
6589         else
6590         {
6591             /*
6592                 Per section 8.1.2.4 of X.690:
6593                 The last five bits of the first byte being set indicate that
6594                 the tag number is encoded in base-128 on the subsequent octets,
6595                 using the first bit of each subsequent octet to indicate if the
6596                 encoding continues on the next octet, just like how the
6597                 individual numbers of OBJECT IDENTIFIER and RELATIVE OBJECT
6598                 IDENTIFIER are encoded.
6599             */
6600             tagBytes[0] |= cast(ubyte) 0b00011111u;
6601             size_t number = this.tagNumber; // We do not want to modify by reference.
6602             ubyte[] encodedNumber;
6603             while (number != 0u)
6604             {
6605                 ubyte[] numberbytes;
6606                 numberbytes.length = size_t.sizeof+1;
6607                 *cast(size_t *) numberbytes.ptr = number;
6608                 if ((numberbytes[0] & 0x80u) == 0u) numberbytes[0] |= 0x80u;
6609                 encodedNumber = numberbytes[0] ~ encodedNumber;
6610                 number >>= 7u;
6611             }
6612             tagBytes ~= encodedNumber;
6613             tagBytes[$-1] &= 0x7Fu; // Set first bit of last byte to zero.
6614         }
6615 
6616         ubyte[] lengthOctets = [ 0x00u ];
6617         switch (this.construction)
6618         {
6619             case (ASN1Construction.primitive):
6620             {
6621                 if (this.length < 127u)
6622                 {
6623                     lengthOctets = [ cast(ubyte) this.length ];
6624                 }
6625                 else
6626                 {
6627                     size_t length = cast(size_t) this.value.length;
6628                     ubyte[] lengthNumberOctets = cast(ubyte[]) *cast(ubyte[size_t.sizeof] *) &length;
6629                     version (LittleEndian) reverse(lengthNumberOctets);
6630                     size_t startOfNonPadding = 0u;
6631                     for (size_t i = 0u; i < size_t.sizeof; i++)
6632                     {
6633                         if (lengthNumberOctets[i] != 0x00u) break;
6634                         startOfNonPadding++;
6635                     }
6636                     lengthNumberOctets = lengthNumberOctets[startOfNonPadding .. $];
6637                     lengthOctets = [ cast(ubyte) (0x80u + lengthNumberOctets.length) ];
6638                     lengthOctets ~= lengthNumberOctets;
6639                 }
6640                 break;
6641             }
6642             case (ASN1Construction.constructed):
6643             {
6644                 lengthOctets = [ 0x80u ];
6645                 break;
6646             }
6647             default:
6648             {
6649                 assert(0, "Invalid ASN1Construction encountered!");
6650             }
6651         }
6652         return (
6653             tagBytes ~
6654             lengthOctets ~
6655             this.value ~
6656             (this.construction == ASN1Construction.constructed ? cast(ubyte[]) [ 0x00u, 0x00u ] : cast(ubyte[]) [])
6657         );
6658     }
6659 
6660     /**
6661         This differs from $(D this.value) in that
6662         $(D this.value) only returns the value octets, whereas
6663         $(D this.toBytes) returns the type tag, length tag / octets,
6664         and the value octets, all concatenated.
6665 
6666         This is the exact same as $(D this.toBytes()).
6667 
6668         Returns: type tag, length tag, and value, all concatenated as a $(D ubyte) array.
6669     */
6670     public @system nothrow
6671     ubyte[] opCast(T = ubyte[])()
6672     {
6673         return this.toBytes();
6674     }
6675 
6676 }
6677 
6678 // Tests of all types using definite-short encoding.
6679 @system
6680 unittest
6681 {
6682     // Test data
6683     immutable ubyte[] dataEndOfContent = [ 0x00u, 0x00u ];
6684     immutable ubyte[] dataBoolean = [ 0x01u, 0x01u, 0xFFu ];
6685     immutable ubyte[] dataInteger = [ 0x02u, 0x01u, 0x1Bu ];
6686     immutable ubyte[] dataBitString = [ 0x03u, 0x03u, 0x07u, 0xF0u, 0x80u ];
6687     immutable ubyte[] dataOctetString = [ 0x04u, 0x04u, 0xFF, 0x00u, 0x88u, 0x14u ];
6688     immutable ubyte[] dataNull = [ 0x05u, 0x00u ];
6689     immutable ubyte[] dataOID = [ 0x06u, 0x04u, 0x2Bu, 0x06u, 0x04u, 0x01u ];
6690     immutable ubyte[] dataOD = [ 0x07u, 0x05u, 'H', 'N', 'E', 'L', 'O' ];
6691     immutable ubyte[] dataExternal = [
6692         0x28u, 0x80u, 0x06u, 0x03u, 0x29u, 0x05u, 0x07u, 0x82u,
6693         0x04u, 0x01u, 0x02u, 0x03u, 0x04u, 0x00u, 0x00u ];
6694     immutable ubyte[] dataReal = [ 0x09u, 0x03u, 0x80u, 0xFBu, 0x05u ]; // 0.15625 (From StackOverflow question)
6695     immutable ubyte[] dataEnum = [ 0x0Au, 0x01u, 0x3Fu ];
6696     immutable ubyte[] dataEmbeddedPDV = [
6697         0x2Bu, 0x80u, 0x80u, 0x02u, 0x85u, 0x00u, 0x82u, 0x04u,
6698         0x01u, 0x02u, 0x03u, 0x04u, 0x00u, 0x00u ];
6699     immutable ubyte[] dataUTF8 = [ 0x0Cu, 0x05u, 'H', 'E', 'N', 'L', 'O' ];
6700     immutable ubyte[] dataROID = [ 0x0Du, 0x03u, 0x06u, 0x04u, 0x01u ];
6701     // sequence
6702     // set
6703     immutable ubyte[] dataNumeric = [ 0x12u, 0x07u, '8', '6', '7', '5', '3', '0', '9' ];
6704     immutable ubyte[] dataPrintable = [ 0x13u, 0x06u, '8', '6', ' ', 'b', 'f', '8' ];
6705     immutable ubyte[] dataTeletex = [ 0x14u, 0x06u, 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ];
6706     immutable ubyte[] dataVideotex = [ 0x15u, 0x06u, 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ];
6707     immutable ubyte[] dataIA5 = [ 0x16u, 0x08u, 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S' ];
6708     immutable ubyte[] dataUTC = [ 0x17u, 0x0Du, '1', '7', '0', '8', '3', '1', '1', '3', '4', '5', '0', '0', 'Z' ];
6709     immutable ubyte[] dataGT = [ 0x18u, 0x0Fu, '2', '0', '1', '7', '0', '8', '3', '1', '1', '3', '4', '5', '0', '0', 'Z' ];
6710     immutable ubyte[] dataGraphic = [ 0x19u, 0x0Bu, 'P', 'o', 'w', 'e', 'r', 'T', 'h', 'i', 'r', 's', 't' ];
6711     immutable ubyte[] dataVisible = [ 0x1Au, 0x0Bu, 'P', 'o', 'w', 'e', 'r', 'T', 'h', 'i', 'r', 's', 't' ];
6712     immutable ubyte[] dataGeneral = [ 0x1Bu, 0x0Bu, 'P', 'o', 'w', 'e', 'r', 'T', 'h', 'i', 'r', 's', 't' ];
6713     immutable ubyte[] dataUniversal = [
6714         0x1Cu, 0x10u,
6715         0x00u, 0x00u, 0x00u, 0x61u,
6716         0x00u, 0x00u, 0x00u, 0x62u,
6717         0x00u, 0x00u, 0x00u, 0x63u,
6718         0x00u, 0x00u, 0x00u, 0x64u
6719     ]; // Big-endian "abcd"
6720     immutable ubyte[] dataCharacter = [
6721         0x3Du, 0x80u, 0x80u, 0x06u, 0x81u, 0x04u, 0x29u, 0x06u,
6722         0x04u, 0x01u, 0x82u, 0x05u, 0x48u, 0x45u, 0x4Eu, 0x4Cu,
6723         0x4Fu, 0x00u, 0x00u ];
6724     immutable ubyte[] dataBMP = [ 0x1Eu, 0x08u, 0x00u, 0x61u, 0x00u, 0x62u, 0x00u, 0x63u, 0x00u, 0x64u ]; // Big-endian "abcd"
6725 
6726     // Combine it all
6727     ubyte[] data =
6728         (dataEndOfContent ~
6729         dataBoolean ~
6730         dataInteger ~
6731         dataBitString ~
6732         dataOctetString ~
6733         dataNull ~
6734         dataOID ~
6735         dataOD ~
6736         dataExternal ~
6737         dataReal ~
6738         dataEnum ~
6739         dataEmbeddedPDV ~
6740         dataUTF8 ~
6741         dataROID ~
6742         dataNumeric ~
6743         dataPrintable ~
6744         dataTeletex ~
6745         dataVideotex ~
6746         dataIA5 ~
6747         dataUTC ~
6748         dataGT ~
6749         dataGraphic ~
6750         dataVisible ~
6751         dataGeneral ~
6752         dataUniversal ~
6753         dataCharacter ~
6754         dataBMP).dup;
6755 
6756     CERElement[] result;
6757 
6758     size_t i = 0u;
6759     while (i < data.length)
6760         result ~= new CERElement(i, data);
6761 
6762     // Pre-processing
6763     External x = result[8].external;
6764     EmbeddedPDV m = result[11].embeddedPresentationDataValue;
6765     CharacterString c = result[25].characterString;
6766 
6767     // Ensure accessors decode the data correctly.
6768     assert(result[1].boolean == true);
6769     assert(result[2].integer!long == 27L);
6770     assert(result[3].bitString == [ true, true, true, true, false, false, false, false, true ]);
6771     assert(result[4].octetString == [ 0xFFu, 0x00u, 0x88u, 0x14u ]);
6772     assert(result[6].objectIdentifier == new OID(OIDNode(0x01u), OIDNode(0x03u), OIDNode(0x06u), OIDNode(0x04u), OIDNode(0x01u)));
6773     assert(result[7].objectDescriptor == result[7].objectDescriptor);
6774     assert((x.identification.syntax == new OID(1u, 1u, 5u, 7u)) && (x.dataValue == [ 0x01u, 0x02u, 0x03u, 0x04u ]));
6775     assert(result[9].realNumber!float == 0.15625);
6776     assert(result[9].realNumber!double == 0.15625);
6777     assert(result[10].enumerated!long == 63L);
6778     assert((m.identification.fixed == true) && (m.dataValue == [ 0x01u, 0x02u, 0x03u, 0x04u ]));
6779     assert(result[12].utf8String == "HENLO");
6780     assert(result[13].relativeObjectIdentifier == [ OIDNode(6), OIDNode(4), OIDNode(1) ]);
6781     assert(result[14].numericString == "8675309");
6782     assert(result[15].printableString ==  "86 bf8");
6783     assert(result[16].teletexString == [ 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ]);
6784     assert(result[17].videotexString == [ 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ]);
6785     assert(result[18].ia5String == "BORTHERS");
6786     assert(result[19].utcTime == DateTime(2017, 8, 31, 13, 45));
6787     assert(result[20].generalizedTime == DateTime(2017, 8, 31, 13, 45));
6788     assert(result[21].graphicString == "PowerThirst");
6789     assert(result[22].visibleString == "PowerThirst");
6790     assert(result[23].generalString == "PowerThirst");
6791     assert(result[24].universalString == "abcd"d);
6792     assert((c.identification.syntax == new OID(1u, 1u, 6u, 4u, 1u)) && (c.stringValue == "HENLO"w));
6793 
6794     result = [];
6795     while (data.length > 0)
6796         result ~= new CERElement(data);
6797 
6798     // Pre-processing
6799     x = result[8].external;
6800     m = result[11].embeddedPresentationDataValue;
6801     c = result[25].characterString;
6802 
6803     // Ensure accessors decode the data correctly.
6804     assert(result[1].boolean == true);
6805     assert(result[2].integer!long == 27L);
6806     assert(result[3].bitString == [ true, true, true, true, false, false, false, false, true ]);
6807     assert(result[4].octetString == [ 0xFFu, 0x00u, 0x88u, 0x14u ]);
6808     assert(result[6].objectIdentifier == new OID(OIDNode(0x01u), OIDNode(0x03u), OIDNode(0x06u), OIDNode(0x04u), OIDNode(0x01u)));
6809     assert(result[7].objectDescriptor == result[7].objectDescriptor);
6810     assert((x.identification.syntax == new OID(1u, 1u, 5u, 7u)) && (x.dataValue == [ 0x01u, 0x02u, 0x03u, 0x04u ]));
6811     assert(result[9].realNumber!float == 0.15625);
6812     assert(result[9].realNumber!double == 0.15625);
6813     assert(result[10].enumerated!long == 63L);
6814     assert((m.identification.fixed == true) && (m.dataValue == [ 0x01u, 0x02u, 0x03u, 0x04u ]));
6815     assert(result[12].utf8String == "HENLO");
6816     assert(result[13].relativeObjectIdentifier == [ OIDNode(6), OIDNode(4), OIDNode(1) ]);
6817     assert(result[14].numericString == "8675309");
6818     assert(result[15].printableString ==  "86 bf8");
6819     assert(result[16].teletexString == [ 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ]);
6820     assert(result[17].videotexString == [ 0xFFu, 0x05u, 0x04u, 0x03u, 0x02u, 0x01u ]);
6821     assert(result[18].ia5String == "BORTHERS");
6822     assert(result[19].utcTime == DateTime(2017, 8, 31, 13, 45));
6823     assert(result[20].generalizedTime == DateTime(2017, 8, 31, 13, 45));
6824     assert(result[21].graphicString == "PowerThirst");
6825     assert(result[22].visibleString == "PowerThirst");
6826     assert(result[23].generalString == "PowerThirst");
6827     assert(result[24].universalString == "abcd"d);
6828     assert((c.identification.syntax == new OID(1u, 1u, 6u, 4u, 1u)) && (c.stringValue == "HENLO"w));
6829 }
6830 
6831 // Test of definite-long encoding
6832 @system
6833 unittest
6834 {
6835     ubyte[] data = [ // 192 characters of boomer-posting
6836         0x0Cu, 0x81u, 0xC0u,
6837         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6838         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6839         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6840         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6841         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6842         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6843         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6844         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6845         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6846         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6847         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6848         'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n'
6849     ];
6850 
6851     data = (data ~ data ~ data); // Triple the data, to catch any bugs that arise with subsequent values.
6852 
6853     CERElement[] result;
6854     size_t i = 0u;
6855     while (i < data.length)
6856         result ~= new CERElement(i, data);
6857 
6858     assert(result.length == 3);
6859     assert(result[0].utf8String[0 .. 5] == "AMREN");
6860     assert(result[1].utf8String[6 .. 14] == "BORTHERS");
6861     assert(result[2].utf8String[$-2] == '!');
6862 
6863     result = [];
6864     while (data.length > 0)
6865         result ~= new CERElement(data);
6866 
6867     assert(result.length == 3);
6868     assert(result[0].utf8String[0 .. 5] == "AMREN");
6869     assert(result[1].utf8String[6 .. 14] == "BORTHERS");
6870     assert(result[2].utf8String[$-2] == '!');
6871 }
6872 
6873 // Test of indefinite-length encoding
6874 // @system
6875 // unittest
6876 // {
6877 //     ubyte[] data = [ // 192 characters of boomer-posting
6878 //         0x2Cu, 0x80u,
6879 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6880 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6881 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6882 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6883 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6884 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6885 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6886 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6887 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6888 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6889 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6890 //         0x0Cu, 0x10u, 'A', 'M', 'R', 'E', 'N', ' ', 'B', 'O', 'R', 'T', 'H', 'E', 'R', 'S', '!', '\n',
6891 //         0x00u, 0x00u
6892 //     ];
6893 
6894 //     data = (data ~ data ~ data); // Triple the data, to catch any bugs that arise with subsequent values.
6895 
6896 //     CERElement[] result;
6897 //     size_t i = 0u;
6898 //     while (i < data.length)
6899 //         result ~= new CERElement(i, data);
6900 
6901 //     assert(result.length == 3);
6902 //     assert(result[0].length == 216u);
6903 //     assert(result[0].utf8String[2 .. 7] == "AMREN");
6904 //     assert(result[1].utf8String[8 .. 16] == "BORTHERS");
6905 //     assert(result[2].utf8String[$-2] == '!');
6906 
6907 //     result = [];
6908 //     while (data.length > 0)
6909 //         result ~= new CERElement(data);
6910 
6911 //     assert(result.length == 3);
6912 //     assert(result[0].utf8String[2 .. 7] == "AMREN");
6913 //     assert(result[1].utf8String[8 .. 16] == "BORTHERS");
6914 //     assert(result[2].utf8String[$-2] == '!');
6915 // }
6916 
6917 // Test deeply (but not too deeply) nested indefinite-length elements
6918 @system
6919 unittest
6920 {
6921     ubyte[] data = [
6922         0x2Cu, 0x80u,
6923             0x2Cu, 0x80u,
6924                 0x0Cu, 0x02u, 'H', 'I',
6925                 0x00u, 0x00u,
6926             0x00u, 0x00u ];
6927     assertNotThrown!ASN1Exception(new CERElement(data));
6928 }
6929 
6930 // Try to induce infinite recursion for an indefinite-length element
6931 @system
6932 unittest
6933 {
6934     ubyte[] invalid = [];
6935     for (size_t i = 0u; i < CERElement.nestingRecursionLimit+1; i++)
6936     {
6937         invalid ~= [ 0x2Cu, 0x80u ];
6938     }
6939     assertThrown!ASN1RecursionException(new CERElement(invalid));
6940 }
6941 
6942 // Try to crash everything with a short indefinite-length element
6943 @system
6944 unittest
6945 {
6946     ubyte[] invalid;
6947     invalid = [ 0x2Cu, 0x80u, 0x01u ];
6948     assertThrown!ASN1Exception(new CERElement(invalid));
6949     invalid = [ 0x2Cu, 0x80u, 0x00u ];
6950     assertThrown!ASN1Exception(new CERElement(invalid));
6951 }
6952 
6953 // Test an embedded value with two adjacent null octets
6954 @system
6955 unittest
6956 {
6957     ubyte[] data = [
6958         0x24u, 0x80u,
6959             0x04u, 0x04u, 0x00u, 0x00u, 0x00u, 0x00u, // These should not indicate the end.
6960             0x00u, 0x00u ]; // These should.
6961     assert((new CERElement(data)).value == [ 0x04u, 0x04u, 0x00u, 0x00u, 0x00u, 0x00u ]);
6962 }
6963 
6964 /*
6965     Test of OCTET STRING encoding on 500 bytes (+4 for type and length tags)
6966 
6967     The number 500 was specifically selected for this test because CER
6968     uses 1000 as the threshold after which OCTET STRING must be represented
6969     as a constructed sequence of definite-length-encoded OCTET STRINGS,
6970     followed by an EOC element, but 500 is also big enough to require
6971     the length to be encoded on two octets in definite-long form.
6972 */
6973 @system
6974 unittest
6975 {
6976     ubyte[] test;
6977     test.length = 504u;
6978     test[0] = cast(ubyte) ASN1UniversalType.octetString;
6979     test[1] = 0b10000010u; // Length is encoded on next two octets
6980     test[2] = 0x01u; // Most significant byte of length
6981     test[3] = 0xF4u; // Least significant byte of length
6982     test[4] = 0x0Au; // First byte of the encoded value
6983     test[5 .. $-1] = 0x0Bu;
6984     test[$-1] = 0x0Cu;
6985 
6986     CERElement el;
6987     assertNotThrown!Exception(el = new CERElement(test));
6988     ubyte[] output = el.octetString;
6989     assert(output.length == 500u);
6990     assert(output[0] == 0x0Au);
6991     assert(output[1] == 0x0Bu);
6992     assert(output[$-2] == 0x0Bu);
6993     assert(output[$-1] == 0x0Cu);
6994 }
6995 
6996 // Assert all single-byte encodings do not decode successfully.
6997 @system
6998 unittest
6999 {
7000     for (ubyte i = 0x00u; i < ubyte.max; i++)
7001     {
7002         ubyte[] data = [i];
7003         assertThrown!Exception(new CERElement(data));
7004     }
7005 
7006     size_t index;
7007     for (ubyte i = 0x00u; i < ubyte.max; i++)
7008     {
7009         immutable ubyte[] data = [i];
7010         assertThrown!Exception(new CERElement(index, data));
7011     }
7012 }
7013 
7014 // Test long-form tag number (when # >= 31) with leading zero bytes (0x80)
7015 @system
7016 unittest
7017 {
7018     ubyte[] invalid;
7019     invalid = [ 0b10011111u, 0b10000000u ];
7020     assertThrown!ASN1TagException(new CERElement(invalid));
7021 
7022     invalid = [ 0b10011111u, 0b10000000u, 0b10000000u ];
7023     assertThrown!ASN1TagException(new CERElement(invalid));
7024 
7025     invalid = [ 0b10011111u, 0b10000000u, 0b10000111u ];
7026     assertThrown!ASN1TagException(new CERElement(invalid));
7027 }
7028 
7029 // Test long-form tag numbers do not encode with leading zeroes
7030 @system
7031 unittest
7032 {
7033     ubyte[] invalid;
7034     CERElement element = new CERElement();
7035     element.tagNumber = 73u;
7036     assert((element.toBytes)[1] != 0x80u);
7037 }
7038 
7039 // Test that a value that is a byte too short does not throw a RangeError.
7040 @system
7041 unittest
7042 {
7043     ubyte[] test = [ 0x00u, 0x03u, 0x00u, 0x00u ];
7044     assertThrown!ASN1TruncationException(new CERElement(test));
7045 }
7046 
7047 // Test that a misleading definite-long length byte does not throw a RangeError.
7048 @system
7049 unittest
7050 {
7051     ubyte[] invalid = [ 0b00000000u, 0b10000001u ];
7052     assertThrown!ASN1TruncationException(new CERElement(invalid));
7053 }
7054 
7055 // Test that leading zeroes in definite long length encodings throw exceptions
7056 @system
7057 unittest
7058 {
7059     ubyte[] invalid = [ 0b00000000u, 0b10000010u, 0b00000000u, 0b00000001u ];
7060     assertThrown!ASN1LengthException(new CERElement(invalid));
7061 }
7062 
7063 // Test that a very large value does not cause a segfault or something
7064 @system
7065 unittest
7066 {
7067     size_t biggest = cast(size_t) int.max;
7068     ubyte[] big = [ 0x00u, 0x80u ];
7069     big[1] += cast(ubyte) int.sizeof;
7070     big ~= cast(ubyte[]) *cast(ubyte[int.sizeof] *) &biggest;
7071     big ~= [ 0x01u, 0x02u, 0x03u ]; // Plus some arbitrary data.
7072     assertThrown!ASN1TruncationException(new CERElement(big));
7073 }
7074 
7075 // Test that the largest possible item does not cause a segfault or something
7076 @system
7077 unittest
7078 {
7079     size_t biggest = size_t.max;
7080     ubyte[] big = [ 0x00u, 0x80u ];
7081     big[1] += cast(ubyte) size_t.sizeof;
7082     big ~= cast(ubyte[]) *cast(ubyte[size_t.sizeof] *) &biggest;
7083     big ~= [ 0x01u, 0x02u, 0x03u ]; // Plus some arbitrary data.
7084     assertThrown!ASN1LengthException(new CERElement(big));
7085 }
7086 
7087 // Test that a short indefinite-length element does not throw a RangeError
7088 @system
7089 unittest
7090 {
7091     ubyte[] naughty = [ 0x3F, 0x00u, 0x80, 0x00u ];
7092     size_t bytesRead = 0u;
7093     assertThrown!ASN1TruncationException(new CERElement(bytesRead, naughty));
7094 }
7095 
7096 // Test that a short indefinite-length element does not throw a RangeError
7097 @system
7098 unittest
7099 {
7100     ubyte[] naughty = [ 0x3F, 0x00u, 0x80, 0x00u, 0x00u ];
7101     size_t bytesRead = 0u;
7102     assertNotThrown!ASN1TruncationException(new CERElement(bytesRead, naughty));
7103 }
7104 
7105 // Test that a valueless long-form definite-length element does not throw a RangeError
7106 @system
7107 unittest
7108 {
7109     ubyte[] naughty = [ 0x00u, 0x82, 0x00u, 0x01u ];
7110     size_t bytesRead = 0u;
7111     assertThrown!ASN1LengthException(new CERElement(bytesRead, naughty));
7112 }
7113 
7114 // PyASN1 Comparison Testing
7115 @system
7116 unittest
7117 {
7118     CERElement e = new CERElement();
7119     e.boolean = false;
7120     assert(e.value == [ 0x00u ]);
7121     e.integer = 5;
7122     assert(e.value == [ 0x05u ]);
7123     e.bitString = [
7124         true, false, true, true, false, false, true, true,
7125         true, false, false, false ];
7126     assert(e.value == [ 0x04u, 0xB3u, 0x80u ]);
7127     e.bitString = [
7128         true, false, true, true, false, true, false, false ];
7129     assert(e.value == [ 0x00u, 0xB4u ]);
7130     e.objectIdentifier = new OID(1, 2, 0, 256, 79999, 7);
7131     assert(e.value == [
7132         0x2Au, 0x00u, 0x82u, 0x00u, 0x84u, 0xF0u, 0x7Fu, 0x07u ]);
7133     e.enumerated = 5;
7134     assert(e.value == [ 0x05u ]);
7135     e.enumerated = 90000;
7136     assert(e.value == [ 0x01u, 0x5Fu, 0x90u ]);
7137 }
7138 
7139 // Test that all data types that cannot have value length = 0 throw exceptions.
7140 // See CVE-2015-5726.
7141 @system
7142 unittest
7143 {
7144     CERElement el = new CERElement();
7145     assertThrown!ASN1Exception(el.boolean);
7146     assertThrown!ASN1Exception(el.integer!byte);
7147     assertThrown!ASN1Exception(el.integer!short);
7148     assertThrown!ASN1Exception(el.integer!int);
7149     assertThrown!ASN1Exception(el.integer!long);
7150     assertThrown!ASN1Exception(el.bitString);
7151     assertThrown!ASN1Exception(el.objectIdentifier);
7152     assertThrown!ASN1Exception(el.enumerated!byte);
7153     assertThrown!ASN1Exception(el.enumerated!short);
7154     assertThrown!ASN1Exception(el.enumerated!int);
7155     assertThrown!ASN1Exception(el.enumerated!long);
7156     assertThrown!ASN1Exception(el.generalizedTime);
7157     assertThrown!ASN1Exception(el.utcTime);
7158 }