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