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