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