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