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