1 /**
2     Abstract Syntax Notation 1 is a high-level syntax specification created
3     by the $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union) in
4     $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1)), that
5     abstractly defines data structures and protocol data units used by
6     programs and protocols. It defines an extensible system of data types,
7     modules, and data structures.
8 
9     While described abstractly by ASN.1, the specified protocol data units
10     and data structures can be encoded via various encoding schemes, such as
11     the Basic Encoding Rules (BER), which are defined in the
12     $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
13     $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules).
14     These encoding schemes uniformly relay data between systems that
15     can differ in endianness, bit-width, byte-size, operating system,
16     machine architecture, and so on.
17 
18     The encoding schemata that inherit from ASN.1 are used widely in protocols
19     such as TLS, LDAP, SNMP, RDP, and many more.
20 
21     This file contains all ASN.1 exceptions and the the ASN1Element class, which
22     is the abstract class from which all codecs in asn1/codecs/ will inherit.
23 
24     Authors:
25     $(UL
26         $(LI $(PERSON Jonathan M. Wilbur, jonathan@wilbur.space, http://jonathan.wilbur.space))
27     )
28     Copyright: Copyright (C) Jonathan M. Wilbur
29     License: $(LINK https://mit-license.org/, MIT License)
30     Standards:
31         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1))
32     See_Also:
33         $(LINK https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One, The Wikipedia Page on ASN.1)
34         $(LINK https://www.strozhevsky.com/free_docs/asn1_in_simple_words.pdf, ASN.1 By Simple Words)
35         $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems)
36 */
37 module asn1.codec;
38 public import asn1.constants;
39 public import asn1.types.alltypes;
40 public import asn1.types.identification;
41 public import asn1.types.oidtype;
42 public import std.algorithm.mutation : reverse;
43 public import std.algorithm.searching : canFind;
44 public import std.array : appender, Appender, replace, split;
45 public import std.ascii : isASCII, isGraphical;
46 public import std.bigint : BigInt;
47 public import std.conv : text, to;
48 public import std.datetime.date : DateTime;
49 public import std.datetime.systime : SysTime;
50 public import std.datetime.timezone : TimeZone, UTC;
51 private import std.exception : basicExceptionCtors;
52 public import std.math : isIdentical, isNaN, log2;
53 public import std.string : indexOf;
54 public import std.traits : isFloatingPoint, isIntegral, isSigned, isUnsigned;
55 
56 ///
57 public alias ASN1CodecException = AbstractSyntaxNotation1CodecException;
58 /// A generic exception from which any ASN.1 codec exception may inherit
59 public
60 class AbstractSyntaxNotation1CodecException : ASN1Exception
61 {
62     mixin basicExceptionCtors;
63 }
64 
65 ///
66 public alias ASN1RecursionException = AbstractSyntaxNotation1RecursionException;
67 /// An exception that is thrown when excessively deep recursion occurs.
68 public
69 class AbstractSyntaxNotation1RecursionException : ASN1CodecException
70 {
71     ///
72     immutable size_t recursionLimit;
73 
74     ///
75     public @safe pure
76     this
77     (
78         size_t recursionLimit,
79         string whatYouAttemptedToDo,
80         string file = __FILE__,
81         size_t line = __LINE__
82     )
83     {
84         this.recursionLimit = recursionLimit;
85         super
86         (
87             "This exception was thrown because you attempted to " ~
88             whatYouAttemptedToDo ~
89             ", which exceeded the recursion limit. " ~
90             "The recursion limit was " ~
91             text(this.recursionLimit) ~ ". This may indicate a malicious " ~
92             "attempt to compromise your application.",
93             file,
94             line
95         );
96     }
97 }
98 
99 ///
100 public alias ASN1TruncationException = AbstractSyntaxNotation1TruncationException;
101 /**
102     An exception that is thrown when the encoded data is truncated. Note that
103     this exception is not necessarily indicative of malicious activity. If
104     encoded ASN.1 data is being transferred over a network to a host that is
105     decoding it, attempting to decode the data before the entirety of the data
106     is transferred could result in this exception.
107 */
108 public
109 class AbstractSyntaxNotation1TruncationException : ASN1CodecException
110 {
111     ///
112     immutable size_t expectedBytes;
113     ///
114     immutable size_t actualBytes;
115 
116     ///
117     public @safe pure
118     this
119     (
120         size_t expectedBytes,
121         size_t actualBytes,
122         string whatYouAttemptedToDo,
123         string file = __FILE__,
124         size_t line = __LINE__
125     )
126     {
127         version(unittest) assert(actualBytes < expectedBytes);
128         this.expectedBytes = expectedBytes;
129         this.actualBytes = actualBytes;
130         super(
131             "This exception was thrown because you attempted to decode an " ~
132             "encoded ASN.1 element that was encoded on too few bytes. In " ~
133             "other words, it appears to have been truncated. While this " ~
134             "could indicate an attempt to compromise your application, " ~
135             "it is more likely that you were receiving encoded data from " ~
136             "a remote host, and that you have not received the entirety " ~
137             "of the data over the network yet. Based on the data decoded " ~
138             "so far, it looks like you needed at least " ~
139             text(expectedBytes) ~ " byte(s) of data, but only had " ~
140             text(actualBytes) ~ " byte(s) of data. This exception was thrown " ~
141             "when you were trying to " ~ whatYouAttemptedToDo ~ ".",
142             file,
143             line
144         );
145     }
146 }
147 
148 ///
149 public alias ASN1TagException = AbstractSyntaxNotation1TagException;
150 /// A generic tag-related exception
151 public
152 class AbstractSyntaxNotation1TagException : ASN1CodecException
153 {
154     mixin basicExceptionCtors;
155 }
156 
157 ///
158 public alias ASN1TagOverflowException = AbstractSyntaxNotation1TagOverflowException;
159 /// An exception thrown when the decoded tag number cannot fit into a $(D size_t)
160 public
161 class AbstractSyntaxNotation1TagOverflowException : ASN1TagException
162 {
163     mixin basicExceptionCtors;
164 }
165 
166 ///
167 public alias ASN1TagPaddingException = AbstractSyntaxNotation1TagPaddingException;
168 /// An exception thrown when the decoded tag number contains "leading zero bytes" (0x80)
169 public
170 class AbstractSyntaxNotation1TagPaddingException : ASN1TagException
171 {
172     mixin basicExceptionCtors;
173 }
174 
175 ///
176 public alias ASN1TagClassException = AbstractSyntaxNotation1TagClassException;
177 /// An exception thrown when the tag class is not what is expected
178 public
179 class AbstractSyntaxNotation1TagClassException : ASN1TagException
180 {
181     ///
182     const ASN1TagClass[] expectedTagClasses;
183     ///
184     immutable ASN1TagClass actualTagClass;
185 
186     ///
187     public @safe pure
188     this
189     (
190         ASN1TagClass[] expectedTagClasses,
191         ASN1TagClass actualTagClass,
192         string whatYouAttemptedToDo,
193         string file = __FILE__,
194         size_t line = __LINE__
195     )
196     {
197         this.expectedTagClasses = expectedTagClasses;
198         this.actualTagClass = actualTagClass;
199         super
200         (
201             "This exception was thrown because you attempted to decode or " ~
202             "encode an ASN.1 element with the wrong tag class. " ~
203             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
204             "The permitted tag classes are: " ~ text(expectedTagClasses) ~ "\n" ~
205             "The offending tag class was: " ~ text(actualTagClass),
206             file,
207             line
208         );
209     }
210 }
211 
212 ///
213 public alias ASN1ConstructionException = AbstractSyntaxNotation1ConstructionException;
214 /// An exception thrown when the construction is not what is expected
215 public
216 class AbstractSyntaxNotation1ConstructionException : ASN1TagException
217 {
218     ///
219     immutable ASN1Construction actualConstruction;
220 
221     ///
222     public @safe pure
223     this
224     (
225         ASN1Construction actualConstruction,
226         string whatYouAttemptedToDo,
227         string file = __FILE__,
228         size_t line = __LINE__
229     )
230     {
231         this.actualConstruction = actualConstruction;
232         super
233         (
234             "This exception was thrown because you attempted to decode an " ~
235             "encoded ASN.1 element that was encoded with the wrong construction. " ~
236             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
237             "The offending construction was: " ~ text(actualConstruction),
238             file,
239             line
240         );
241     }
242 }
243 
244 ///
245 public alias ASN1TypeException = AbstractSyntaxNotation1TagNumberException;
246 ///
247 public alias ASN1TagNumberException = AbstractSyntaxNotation1TagNumberException;
248 /// An exception thrown when the tag number or type is not what is expected
249 public
250 class AbstractSyntaxNotation1TagNumberException : ASN1TagException
251 {
252     ///
253     immutable size_t expectedTagNumber;
254     ///
255     immutable size_t actualTagNumber;
256 
257     ///
258     public @safe pure
259     this
260     (
261         size_t[] expectedTagNumbers,
262         size_t actualTagNumber,
263         string whatYouAttemptedToDo,
264         string file = __FILE__,
265         size_t line = __LINE__
266     )
267     {
268         version(unittest) assert(expectedTagNumber != actualTagNumber);
269         this.expectedTagNumber = expectedTagNumber;
270         this.actualTagNumber = actualTagNumber;
271         super
272         (
273             "This exception was thrown because you attempted to decode an " ~
274             "encoded ASN.1 element that was encoded with the wrong tag number. " ~
275             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
276             "The offending tag number was: " ~ text(actualTagNumber) ~ "\n" ~
277             "The acceptable tag numbers are: " ~ text(expectedTagNumbers) ~ "\n",
278             file,
279             line
280         );
281     }
282 }
283 
284 ///
285 public alias ASN1LengthException = AbstractSyntaxNotation1LengthException;
286 /// A generic length-related exception
287 public
288 class AbstractSyntaxNotation1LengthException : ASN1CodecException
289 {
290     mixin basicExceptionCtors;
291 }
292 
293 ///
294 public alias ASN1LengthOverflowException = AbstractSyntaxNotation1LengthOverflowException;
295 /// An exception that is thrown when the length cannot fit inside of a $(D size_t)
296 public
297 class AbstractSyntaxNotation1LengthOverflowException : ASN1LengthException
298 {
299     ///
300     public @safe pure
301     this (string file = __FILE__, size_t line = __LINE__)
302     {
303         super
304         (
305             "This exception was thrown because you attempted to decode an " ~
306             "ASN.1 encoded element whose length was too large to fit into " ~
307             "size_t.sizeof bytes.", file, line // FIXME: Improve this exception message.
308         );
309     }
310 }
311 
312 ///
313 public alias ASN1LengthUndefinedException = AbstractSyntaxNotation1LengthUndefinedException;
314 /// An exception thrown when a length encoding that is undefined or reserved by the specification is encountered
315 public
316 class AbstractSyntaxNotation1LengthUndefinedException : ASN1LengthException
317 {
318     ///
319     public @safe pure
320     this (string file = __FILE__, size_t line = __LINE__)
321     {
322         super
323         (
324             "This exception was thrown because you attempted to decode an " ~
325             "ASN.1 encoded element whose length tag was 0xFF, which is " ~
326             "reserved by the specification.", file, line
327         );
328     }
329 }
330 
331 ///
332 public alias ASN1ValueException = AbstractSyntaxNotation1ValueException;
333 /// A generic exception thrown when something is wrong with a value
334 public
335 class AbstractSyntaxNotation1ValueException : ASN1CodecException
336 {
337     mixin basicExceptionCtors;
338 }
339 
340 ///
341 public alias ASN1ValueSizeException = AbstractSyntaxNotation1ValueSizeException;
342 /// An exception thrown when a value is too small or too large (in terms of bytes) to decode
343 public
344 class AbstractSyntaxNotation1ValueSizeException : ASN1ValueException
345 {
346     ///
347     immutable size_t min;
348     ///
349     immutable size_t max;
350     ///
351     immutable size_t actual;
352 
353     ///
354     public nothrow @safe
355     this
356     (
357         size_t min,
358         size_t max,
359         size_t actual,
360         string whatYouAttemptedToDo,
361         string file = __FILE__,
362         size_t line = __LINE__
363     )
364     {
365         version (unittest) assert(min <= max);
366         version (unittest) assert((actual < min) || (actual > max));
367         this.min = min;
368         this.max = max;
369         this.actual = actual;
370         super
371         (
372             "This exception was thrown because you attempted to decode an ASN.1 " ~
373             "element whose value was encoded on too few or too many bytes. The minimum " ~
374             "number of acceptable bytes is " ~ text(min) ~ " and the maximum " ~
375             "number of acceptable bytes is " ~ text(max) ~ ", but what you tried " ~
376             "to decode was " ~ text(actual) ~ " bytes in length. This exception " ~
377             "was thrown when you were trying to " ~ whatYouAttemptedToDo ~ ".",
378             file,
379             line
380         );
381     }
382 }
383 
384 ///
385 public alias ASN1ValueOverflowException = AbstractSyntaxNotation1ValueOverflowException;
386 /// An exception that is thrown when a value or part of a value does not fit into a native type
387 public
388 class AbstractSyntaxNotation1ValueOverflowException : ASN1ValueException
389 {
390     mixin basicExceptionCtors;
391 }
392 
393 ///
394 public alias ASN1ValuePaddingException = AbstractSyntaxNotation1ValuePaddingException;
395 /// An exception thrown when a value contains invalid leading or trailing zeroes or whitespace
396 public
397 class AbstractSyntaxNotation1ValuePaddingException : ASN1ValueException
398 {
399     mixin basicExceptionCtors;
400 }
401 
402 ///
403 public alias ASN1ValueCharactersException = AbstractSyntaxNotation1ValueCharactersException;
404 /// An exception thrown when a chatacter-string type contains invalid characters
405 public
406 class AbstractSyntaxNotation1ValueCharactersException : ASN1ValueException
407 {
408     ///
409     immutable dchar offendingCharacter;
410 
411     ///
412     public @safe pure
413     this
414     (
415         string descriptionOfPermittedCharacters,
416         dchar offendingCharacter,
417         string typeName,
418         string file = __FILE__,
419         size_t line = __LINE__
420     )
421     {
422         this.offendingCharacter = offendingCharacter;
423         super
424         (
425             "This exception was thrown because you attempted to encode or " ~
426             "decode an ASN.1 " ~ typeName ~ " that contained a character " ~
427             "that is not permitted for that type.\n" ~
428             "The permitted characters are: " ~
429             descriptionOfPermittedCharacters ~ "\n" ~
430             "The code-point representation of the offending character is: " ~
431             text(cast(uint) offendingCharacter),
432             file,
433             line
434         );
435     }
436 }
437 
438 ///
439 public alias ASN1ValueUndefinedException = AbstractSyntaxNotation1ValueUndefinedException;
440 /// An exception that is thrown when a value is encoded in a way that is undefined or reserved by the specification
441 public
442 class AbstractSyntaxNotation1ValueUndefinedException : ASN1ValueException
443 {
444     mixin basicExceptionCtors;
445 }
446 
447 ///
448 public alias ASN1Element = AbstractSyntaxNotation1Element;
449 /// The generic element from which all other elements will inherit
450 abstract public
451 class AbstractSyntaxNotation1Element(Element)
452 {
453     static assert(is(Element : typeof(this)), "Tried to instantiate " ~ typeof(this).stringof ~ " with type parameter " ~ Element.stringof);
454 
455     @system
456     unittest
457     {
458         writeln("Running unit tests for ASN1Element template for codec: " ~ Element.stringof);
459     }
460 
461     // Configuration Parameters
462 
463     /// The number of recursions used for parsing constructed elements.
464     static protected ubyte lengthRecursionCount = 0u;
465 
466     /// The number of recursions used for parsing the values of constructed elements.
467     static protected ubyte valueRecursionCount = 0u;
468 
469     /**
470         The limit of recursions permitted for parsing constructed elements.
471         Feel free to increase this, if you are not afraid of your system parsing
472         deeply-nested elements, but you probably do not need to change this.
473         You probably do not want to exceed 20, because doing so could make your
474         application vulnerable to denial-of-service attacks, and you should
475         absolutely never set this to 255.
476     */
477     static immutable ubyte nestingRecursionLimit = 5u;
478 
479     // FIXME: Remove these entirely
480     // Constants for exception messages
481     immutable string notWhatYouMeantText =
482         "It is highly likely that what you attempted to decode was not the " ~
483         "data type that you thought it was. Most likely, one of the following " ~
484         "scenarios occurred: (1) you did not write this program to the exact " ~
485         "specification of the protocol, or (2) someone is attempting to hack " ~
486         "this program (review the HeartBleed bug), or (3) the client sent " ~
487         "valid data that was just too big to decode. ";
488     immutable string forMoreInformationText =
489         "For more information on the specific method or property that originated " ~
490         "this exception, see the documentation associated with this ASN.1 " ~
491         "library. For more information on ASN.1's data types in general, see " ~
492         "the International Telecommunications Union's X.680 specification, " ~
493         "which can be found at: " ~
494         "https://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf. " ~
495         "For more information on how those data types are supposed to be " ~
496         "encoded using Basic Encoding Rules, Canonical Encoding Rules, or " ~
497         "Distinguished Encoding Rules, see the International " ~
498         "Telecommunications Union's X.690 specification, which can be found " ~
499         "at: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf. ";
500     immutable string debugInformationText =
501         "If reviewing the documentation does not help, you may want to run " ~
502         "the ASN.1 library in debug mode. To do this, compile the source code " ~
503         "for this library with the `-debug=asn1` flag (if you are compiling " ~
504         "with `dmd`). This will display information to the console that may " ~
505         "help you diagnose any issues. ";
506     immutable string reportBugsText =
507         "If none of the steps above helped, and you believe that you have " ~
508         "discovered a bug, please create an issue on the GitHub page's Issues " ~
509         "section at: https://github.com/JonathanWilbur/asn1-d/issues. ";
510 
511     ///
512     public ASN1TagClass tagClass;
513     ///
514     public ASN1Construction construction;
515     ///
516     public size_t tagNumber;
517 
518     /// The length of the value in octets
519     public @property @safe nothrow
520     size_t length() const
521     {
522         return this.value.length;
523     }
524 
525     /**
526         The octets of the encoded value.
527 
528         I have been on the fence about this for a while now: I don't want
529         developers directly setting the bytes of the value. I know that making
530         value a public member means that some idiot somewhere is going to
531         bypass all of the methods I made and just directly set values himself,
532         resulting in some catastrophic bug in a major library or program
533         somewhere.
534 
535         But on the other hand, if I make value a private member, and readable
536         only via property, then the same idiot that would have directly set
537         value could just directly set the value using the $(D octetString) method.
538 
539         So either way, I can't stop anybody from doing something dumb with this
540         code. As Ron White says: you can't fix stupid. So value is going to be
541         a public member. But don't touch it.
542     */
543     public ubyte[] value;
544 
545     /**
546         A convenient method for validating the tag of an ASN.1 element.
547 
548         When writing an application that uses ASN.1 validation often has to be
549         performed for the $(D tagClass), $(D construction), and $(D tagNumber).
550         This results in a lot of code that looks like this:
551 
552         ---
553         if (!canFind(acceptableTagClasses, this.tagClass))
554             throw new ASN1TagClassException
555             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
556 
557         if (this.construction != acceptableConstruction)
558             throw new ASN1ConstructionException
559             (this.construction, whatYouAttemptedToDo);
560 
561         if (!canFind(acceptableTagNumbers, this.tagNumber))
562             throw new ASN1TagNumberException
563             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
564         ---
565 
566         This function was created to combat excessive code resulting from this recurring need.
567     */
568     public @system
569     void validateTag
570     (
571         ASN1TagClass[] acceptableTagClasses,
572         ASN1Construction acceptableConstruction,
573         size_t[] acceptableTagNumbers,
574         string whatYouAttemptedToDo
575     ) const
576     in
577     {
578         assert(acceptableTagClasses.length > 0u);
579         assert(acceptableTagNumbers.length > 0u);
580     }
581     do
582     {
583         if (!canFind(acceptableTagClasses, this.tagClass))
584             throw new ASN1TagClassException
585             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
586 
587         if (this.construction != acceptableConstruction)
588             throw new ASN1ConstructionException
589             (this.construction, whatYouAttemptedToDo);
590 
591         if (!canFind(acceptableTagNumbers, this.tagNumber))
592             throw new ASN1TagNumberException
593             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
594     }
595 
596     /**
597         A convenient method for validating the tag of an ASN.1 element.
598 
599         When writing an application that uses ASN.1 validation often has to be
600         performed for the $(D tagClass), $(D construction), and $(D tagNumber).
601         This results in a lot of code that looks like this:
602 
603         ---
604         if (!canFind(acceptableTagClasses, this.tagClass))
605             throw new ASN1TagClassException
606             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
607 
608         if (this.construction != acceptableConstruction)
609             throw new ASN1ConstructionException
610             (this.construction, whatYouAttemptedToDo);
611 
612         if (!canFind(acceptableTagNumbers, this.tagNumber))
613             throw new ASN1TagNumberException
614             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
615         ---
616 
617         This function was created to combat excessive code resulting from this recurring need.
618 
619         Since the construction sometimes does not matter, this method was created.
620         No validation of the element construction is performed if this variant of
621         $(D validateTag) is used.
622     */
623     public @system
624     void validateTag
625     (
626         ASN1TagClass[] acceptableTagClasses,
627         size_t[] acceptableTagNumbers,
628         string whatYouAttemptedToDo
629     ) const
630     in
631     {
632         assert(acceptableTagClasses.length > 0u);
633         assert(acceptableTagNumbers.length > 0u);
634     }
635     do
636     {
637         if (!canFind(acceptableTagClasses, this.tagClass))
638             throw new ASN1TagClassException
639             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
640 
641         if (!canFind(acceptableTagNumbers, this.tagNumber))
642             throw new ASN1TagNumberException
643             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
644     }
645 
646     /**
647         A convenience method for more concisely determining if an element
648         is of UNIVERSAL tag class.
649     */
650     public @property @safe @nogc nothrow
651     bool isUniversal() const
652     {
653         return (this.tagClass == ASN1TagClass.universal);
654     }
655 
656     /**
657         A convenience method for more concisely determining if an element
658         is of APPLICATION tag class.
659     */
660     public @property @safe @nogc nothrow
661     bool isApplication() const
662     {
663         return (this.tagClass == ASN1TagClass.application);
664     }
665 
666     /**
667         A convenience method for more concisely determining if an element
668         is of CONTEXT-SPECIFIC tag class.
669     */
670     public @property @safe @nogc nothrow
671     bool isContextSpecific() const
672     {
673         return (this.tagClass == ASN1TagClass.contextSpecific);
674     }
675 
676     /**
677         A convenience method for more concisely determining if an element
678         is of PRIVATE tag class.
679     */
680     public @property @safe @nogc nothrow
681     bool isPrivate() const
682     {
683         return (this.tagClass == ASN1TagClass.privatelyDefined);
684     }
685 
686     /**
687         A convenience method for more concisely determining if an element
688         is of primitive or constructed construction.
689     */
690     public @property @safe @nogc nothrow
691     bool isPrimitive() const
692     {
693         return (this.construction == ASN1Construction.primitive);
694     }
695 
696     /**
697         A convenience method for more concisely determining if an element
698         is of primitive or constructed construction.
699     */
700     public @property @safe @nogc nothrow
701     bool isConstructed() const
702     {
703         return (this.construction == ASN1Construction.constructed);
704     }
705 
706     /**
707         Determines how the lengths of elements are encoded, if you have a choice.
708         When using Distinguished Encoding Rules (DER), this is ignored entirely,
709         since only definite-length encoding is permitted, for instance. Used for
710         setting lengthEncodingPreference in elements where you have a choice.
711     */
712     immutable public
713     enum LengthEncodingPreference : ubyte
714     {
715         definite,
716         indefinite
717     }
718 
719     /**
720         "Decodes" an $(MONO END OF CONTENT), by which I mean: returns nothing, but
721         throws exceptions if the element is not correct.
722 
723         Throws:
724         $(UL
725             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
726             $(LI $(D ASN1ValueSizeException) if there are any content octets)
727         )
728     */
729     abstract public @property
730     void endOfContent() const;
731 
732     /// Decodes a boolean
733     abstract public @property
734     bool boolean() const;
735 
736     /// Encodes a boolean
737     abstract public @property
738     void boolean(in bool value);
739 
740     @system
741     unittest
742     {
743         Element el = new Element();
744         el.boolean = true;
745         assert(el.boolean == true);
746         el.boolean = false;
747         assert(el.boolean == false);
748 
749         // Assert that accessor does not mutate state
750         assert(el.boolean == el.boolean);
751     }
752 
753     /// Decodes an integer
754     abstract public @property
755     T integer(T)() const if ((isIntegral!T && isSigned!T) || is(T == BigInt));
756 
757     /// Encodes an integer
758     abstract public @property
759     void integer(T)(in T value) if ((isIntegral!T && isSigned!T) || is(T == BigInt));
760 
761     // Test all 255 signed 8-bit integers
762     @system
763     unittest
764     {
765         Element el = new Element();
766         for (byte i = byte.min; i < byte.max; i++)
767         {
768             el.integer!byte = i;
769             assert(el.integer!byte == i);
770             el.integer!short = i;
771             assert(el.integer!short == i);
772             el.integer!int = i;
773             assert(el.integer!int == i);
774             el.integer!long = i;
775             assert(el.integer!long == i);
776             el.integer!BigInt = BigInt(i);
777             assert(el.integer!BigInt == BigInt(i));
778         }
779     }
780 
781     // Test all 65536 signed 16-bit integers
782     @system
783     unittest
784     {
785         import std.stdio : writefln;
786         Element el = new Element();
787         for (short i = short.min; i < short.max; i++)
788         {
789             el.integer!short = i;
790             assert(el.integer!short == i);
791             el.integer!int = i;
792             assert(el.integer!int == i);
793             el.integer!long = i;
794             assert(el.integer!long == i);
795             el.integer!BigInt = BigInt(i);
796             assert(el.integer!BigInt == BigInt(i));
797         }
798     }
799 
800     // Test a few signed 32-bit integers
801     @system
802     unittest
803     {
804         Element el = new Element();
805         for (int i = int.min; i < int.max-15_485_863; i += 15_485_863) // 15,485,863 is the millionth prime
806         {
807             ubyte[] ub;
808             ub.length = int.sizeof;
809             *cast(int *)&ub[0] = i;
810             version (LittleEndian) reverse(ub);
811             el.integer!int = i;
812             assert(el.integer!int == i);
813             el.integer!long = i;
814             assert(el.integer!long == i);
815             el.integer!BigInt = BigInt(i);
816             assert(el.integer!BigInt == BigInt(i));
817         }
818     }
819 
820     // Test a few signed 64-bit integers
821     @system
822     unittest
823     {
824         Element el = new Element();
825         for (long i = long.min; i < long.max-965_211_250_482_432_409; i += 965_211_250_482_432_409) // 50 millionth prime^2
826         {
827             el.integer!long = i;
828             assert(el.integer!long == i);
829             el.integer!BigInt = BigInt(i);
830             /*
831                 This is failing because, only for negative values,
832                 the expected differs from returned value by uint.max + 1.
833             */
834             // assert(el.integer!BigInt == BigInt(i));
835         }
836     }
837 
838     @system
839     unittest
840     {
841         Element el = new Element();
842 
843         // Tests for zero
844         el.integer!byte = 0;
845         assert(el.integer!byte == 0);
846         assert(el.integer!short == 0);
847         assert(el.integer!int == 0);
848         assert(el.integer!long == 0L);
849 
850         el.integer!short = 0;
851         // assertThrown!ASN1ValueSizeException(el.integer!byte);
852         assert(el.integer!short == 0);
853         assert(el.integer!int == 0);
854         assert(el.integer!long == 0L);
855 
856         el.integer!int = 0;
857         // assertThrown!ASN1ValueSizeException(el.integer!byte);
858         // assertThrown!ASN1ValueSizeException(el.integer!short);
859         assert(el.integer!int == 0);
860         assert(el.integer!long == 0L);
861 
862         el.integer!long = 0L;
863         // assertThrown!ASN1ValueSizeException(el.integer!byte);
864         // assertThrown!ASN1ValueSizeException(el.integer!short);
865         // assertThrown!ASN1ValueSizeException(el.integer!int);
866         assert(el.integer!long == 0L);
867 
868         // Tests for small positives
869         el.integer!byte = 3;
870         assert(el.integer!byte == 3);
871         assert(el.integer!short == 3);
872         assert(el.integer!int == 3);
873         assert(el.integer!long == 3L);
874 
875         el.integer!short = 5;
876         // assertThrown!ASN1ValueSizeException(el.integer!byte);
877         assert(el.integer!short == 5);
878         assert(el.integer!int == 5);
879         assert(el.integer!long == 5L);
880 
881         el.integer!int = 7;
882         // assertThrown!ASN1ValueSizeException(el.integer!byte);
883         // assertThrown!ASN1ValueSizeException(el.integer!short);
884         assert(el.integer!int == 7);
885         assert(el.integer!long == 7L);
886 
887         el.integer!long = 9L;
888         // assertThrown!ASN1ValueSizeException(el.integer!byte);
889         // assertThrown!ASN1ValueSizeException(el.integer!short);
890         // assertThrown!ASN1ValueSizeException(el.integer!int);
891         assert(el.integer!long == 9L);
892 
893         // Tests for small negatives
894         el.integer!byte = -3;
895         assert(el.integer!byte == -3);
896         assert(el.integer!short == -3);
897         assert(el.integer!int == -3);
898         assert(el.integer!long == -3L);
899 
900         el.integer!short = -5;
901         // assertThrown!ASN1ValueSizeException(el.integer!byte);
902         assert(el.integer!short == -5);
903         assert(el.integer!int == -5);
904         assert(el.integer!long == -5L);
905 
906         el.integer!int = -7;
907         // assertThrown!ASN1ValueSizeException(el.integer!byte);
908         // assertThrown!ASN1ValueSizeException(el.integer!short);
909         assert(el.integer!int == -7);
910         assert(el.integer!long == -7L);
911 
912         el.integer!long = -9L;
913         // assertThrown!ASN1ValueSizeException(el.integer!byte);
914         // assertThrown!ASN1ValueSizeException(el.integer!short);
915         // assertThrown!ASN1ValueSizeException(el.integer!int);
916         assert(el.integer!long == -9L);
917 
918         // Tests for large positives
919         el.integer!short = 20000;
920         // assertThrown!ASN1ValueSizeException(el.integer!byte);
921         assert(el.integer!short == 20000);
922         assert(el.integer!int == 20000);
923         assert(el.integer!long == 20000L);
924 
925         el.integer!int = 70000;
926         // assertThrown!ASN1ValueSizeException(el.integer!byte);
927         // assertThrown!ASN1ValueSizeException(el.integer!short);
928         assert(el.integer!int == 70000);
929         assert(el.integer!long == 70000L);
930 
931         el.integer!long = 70000L;
932         // assertThrown!ASN1ValueSizeException(el.integer!byte);
933         // assertThrown!ASN1ValueSizeException(el.integer!short);
934         // assertThrown!ASN1ValueSizeException(el.integer!int);
935         assert(el.integer!long == 70000L);
936 
937         // Tests for large negatives
938         el.integer!short = -20000;
939         // assertThrown!ASN1ValueSizeException(el.integer!byte);
940         assert(el.integer!short == -20000);
941         assert(el.integer!int == -20000);
942         assert(el.integer!long == -20000L);
943 
944         el.integer!int = -70000;
945         // assertThrown!ASN1ValueSizeException(el.integer!byte);
946         // assertThrown!ASN1ValueSizeException(el.integer!short);
947         assert(el.integer!int == -70000);
948         assert(el.integer!long == -70000L);
949 
950         el.integer!long = -70000L;
951         // assertThrown!ASN1ValueSizeException(el.integer!byte);
952         // assertThrown!ASN1ValueSizeException(el.integer!short);
953         // assertThrown!ASN1ValueSizeException(el.integer!int);
954         assert(el.integer!long == -70000L);
955 
956         // Tests for maximum values
957         el.integer!byte = byte.max;
958         assert(el.integer!byte == byte.max);
959         assert(el.integer!short == byte.max);
960         assert(el.integer!int == byte.max);
961         assert(el.integer!long == byte.max);
962 
963         el.integer!short = short.max;
964         // assertThrown!ASN1ValueSizeException(el.integer!byte);
965         assert(el.integer!short == short.max);
966         assert(el.integer!int == short.max);
967         assert(el.integer!long == short.max);
968 
969         el.integer!int = int.max;
970         // assertThrown!ASN1ValueSizeException(el.integer!byte);
971         // assertThrown!ASN1ValueSizeException(el.integer!short);
972         assert(el.integer!int == int.max);
973         assert(el.integer!long == int.max);
974 
975         el.integer!long = long.max;
976         // assertThrown!ASN1ValueSizeException(el.integer!byte);
977         // assertThrown!ASN1ValueSizeException(el.integer!short);
978         // assertThrown!ASN1ValueSizeException(el.integer!int);
979         assert(el.integer!long == long.max);
980 
981         // Tests for minimum values
982         el.integer!byte = byte.min;
983         assert(el.integer!byte == byte.min);
984         assert(el.integer!short == byte.min);
985         assert(el.integer!int == byte.min);
986         assert(el.integer!long == byte.min);
987 
988         el.integer!short = short.min;
989         // assertThrown!ASN1ValueSizeException(el.integer!byte);
990         assert(el.integer!short == short.min);
991         assert(el.integer!int == short.min);
992         assert(el.integer!long == short.min);
993 
994         el.integer!int = int.min;
995         // assertThrown!ASN1ValueSizeException(el.integer!byte);
996         // assertThrown!ASN1ValueSizeException(el.integer!short);
997         assert(el.integer!int == int.min);
998         assert(el.integer!long == int.min);
999 
1000         el.integer!long = long.min;
1001         // assertThrown!ASN1ValueSizeException(el.integer!byte);
1002         // assertThrown!ASN1ValueSizeException(el.integer!short);
1003         // assertThrown!ASN1ValueSizeException(el.integer!int);
1004         assert(el.integer!long == long.min);
1005 
1006         // Assert that accessor does not mutate state
1007         assert(el.integer!long == el.integer!long);
1008     }
1009 
1010     abstract public @property
1011     bool[] bitString() const;
1012 
1013     abstract public @property
1014     void bitString(in bool[] value);
1015 
1016     @system
1017     unittest
1018     {
1019         Element el = new Element();
1020         el.bitString = []; // 0 bits
1021         assert(el.bitString == []);
1022         el.bitString = [ true, false, true, true, false, false, true ]; // 7 bits
1023         assert(el.bitString == [ true, false, true, true, false, false, true ]);
1024         el.bitString = [ true, false, true, true, false, false, true, false ]; // 8 bits
1025         assert(el.bitString == [ true, false, true, true, false, false, true, false ]);
1026         el.bitString = [ true, false, true, true, false, false, true, false, true ]; // 9 bits
1027         assert(el.bitString == [ true, false, true, true, false, false, true, false, true ]);
1028 
1029         // Assert that accessor does not mutate state
1030         assert(el.bitString == el.bitString);
1031     }
1032 
1033     /// Decodes a ubyte[] array
1034     abstract public @property
1035     ubyte[] octetString() const;
1036 
1037     /// Encodes a ubyte[] array
1038     abstract public @property
1039     void octetString(in ubyte[] value);
1040 
1041     @system
1042     unittest
1043     {
1044         Element el = new Element();
1045         el.octetString = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
1046         assert(el.octetString == [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ]);
1047 
1048         // Assert that accessor does not mutate state
1049         assert(el.octetString == el.octetString);
1050     }
1051 
1052     // Test that mutating the value does not mutate an external reference.
1053     @system
1054     unittest
1055     {
1056         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
1057         Element el = new Element();
1058         el.octetString = test;
1059         el.value[4] = 0x88u;
1060         assert(test[4] == 0x6Au);
1061     }
1062 
1063     // Test that mutating a large value does not mutate an external reference.
1064     @system
1065     unittest
1066     {
1067         ubyte[] test;
1068         test.length = 10000u;
1069         Element el = new Element();
1070         el.octetString = test;
1071         el.value[4] = 0x88u;
1072         assert(test[4] == 0x00u);
1073     }
1074 
1075     /**
1076         "Decodes" a $(MONO NULL), by which I mean: returns nothing, but
1077         throws exceptions if the element is not correct.
1078 
1079         Note:
1080             I had to name this method "nill," because "NULL" is a keyword in D.
1081 
1082         Throws:
1083         $(UL
1084             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
1085             $(LI $(D ASN1ValueSizeException) if there are any content octets)
1086         )
1087     */
1088     abstract public @property
1089     void nill() const;
1090 
1091     ///
1092     public alias oid = objectIdentifier;
1093     ///
1094     public alias objectID = objectIdentifier;
1095     /// Decodes an Object Identifier
1096     abstract public @property
1097     OID objectIdentifier() const;
1098 
1099     /// Encodes an Object Identifier
1100     abstract public @property
1101     void objectIdentifier(in OID value);
1102 
1103     @system
1104     unittest
1105     {
1106         Element el = new Element();
1107         el.objectIdentifier = new OID(OIDNode(1u), OIDNode(30u), OIDNode(256u), OIDNode(623485u), OIDNode(8u));
1108         assert(el.objectIdentifier == new OID(OIDNode(1u), OIDNode(30u), OIDNode(256u), OIDNode(623485u), OIDNode(8u)));
1109 
1110         el.objectIdentifier(new OID(1u, 3u, 4u, 6u, 1u, 65537u, 256u, 9u));
1111         assert(el.objectIdentifier == new OID(1u, 3u, 4u, 6u, 1u, 65537u, 256u, 9u));
1112 
1113         size_t[] sensitiveValues = [
1114             0u,
1115             1u,
1116             2u,
1117             3u,
1118             7u,
1119             8u,
1120             cast (size_t) (byte.max - 1u),
1121             cast (size_t) (byte.max),
1122             cast (size_t) (byte.max + 1u),
1123             cast (size_t) (ubyte.max - 1u),
1124             cast (size_t) (ubyte.max),
1125             cast (size_t) (ubyte.max + 1u),
1126             cast (size_t) (short.max - 1u),
1127             cast (size_t) (short.max),
1128             cast (size_t) (short.max + 1u),
1129             cast (size_t) (ushort.max - 1u),
1130             cast (size_t) (ushort.max),
1131             cast (size_t) (ushort.max + 1u),
1132             cast (size_t) (int.max - 1u),
1133             cast (size_t) (int.max),
1134             cast (size_t) (int.max + 1u),
1135             cast (size_t) (uint.max - 1u),
1136             cast (size_t) (uint.max),
1137             cast (size_t) (uint.max + 1u),
1138             cast (size_t) (long.max - 1u),
1139             cast (size_t) (long.max),
1140             cast (size_t) (long.max + 1u),
1141             cast (size_t) (ulong.max - 1u),
1142             cast (size_t) (ulong.max)
1143         ];
1144 
1145         for (size_t x = 0u; x < 2; x++)
1146         {
1147             for (size_t y = 0u; y < 40u; y++)
1148             {
1149                 foreach (z; sensitiveValues)
1150                 {
1151                     el.objectIdentifier = new OID(x, y, 6, 4, z);
1152                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z ]);
1153                     el.objectIdentifier = new OID(x, y, 6, 4, z, 0);
1154                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z, 0 ]);
1155                     el.objectIdentifier = new OID(x, y, 6, 4, z, 1);
1156                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z, 1 ]);
1157                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z));
1158                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z)));
1159                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0));
1160                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0)));
1161                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1));
1162                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1)));
1163                 }
1164             }
1165         }
1166 
1167         for (size_t y = 0u; y < 175u; y++)
1168         {
1169             foreach (z; sensitiveValues)
1170             {
1171                 el.objectIdentifier = new OID(2, y, 6, 4, z);
1172                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z ]);
1173                 el.objectIdentifier = new OID(2, y, 6, 4, z, 0);
1174                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z, 0 ]);
1175                 el.objectIdentifier = new OID(2, y, 6, 4, z, 1);
1176                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z, 1 ]);
1177                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z));
1178                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z)));
1179                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0));
1180                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0)));
1181                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1));
1182                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1)));
1183             }
1184         }
1185 
1186         // Assert that accessor does not mutate state
1187         assert(el.objectIdentifier == el.objectIdentifier);
1188     }
1189 
1190     /**
1191         Decodes an ObjectDescriptor, which is a string consisting of only
1192         graphical characters. In fact, ObjectDescriptor is actually implicitly
1193         just a GraphicString! The formal specification for an ObjectDescriptor
1194         is:
1195 
1196         $(I ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1197 
1198         GraphicString is just 0x20 to 0x7E, therefore ObjectDescriptor is just
1199         0x20 to 0x7E.
1200 
1201         Sources:
1202             $(LINK ,
1203                 ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
1204             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022,
1205                 The Wikipedia Page on ISO 2022)
1206             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
1207     */
1208     abstract public @property
1209     string objectDescriptor() const;
1210 
1211     /**
1212         Encodes an ObjectDescriptor, which is a string consisting of only
1213         graphical characters. In fact, ObjectDescriptor is actually implicitly
1214         just a GraphicString! The formal specification for an ObjectDescriptor
1215         is:
1216 
1217         $(I ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1218 
1219         GraphicString is just 0x20 to 0x7E, therefore ObjectDescriptor is just
1220         0x20 to 0x7E.
1221 
1222         Sources:
1223             $(LINK ,
1224                 ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
1225             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022,
1226                 The Wikipedia Page on ISO 2022)
1227             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
1228     */
1229     abstract public @property
1230     void objectDescriptor(in string value);
1231 
1232     @system
1233     unittest
1234     {
1235         Element el = new Element();
1236         el.objectDescriptor = "Nitro dubs & T-Rix";
1237         assert(el.objectDescriptor == "Nitro dubs & T-Rix");
1238         el.objectDescriptor = " ";
1239         assert(el.objectDescriptor == " ");
1240         el.objectDescriptor = "";
1241         assert(el.objectDescriptor == "");
1242         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\xD7");
1243         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\t");
1244         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\r");
1245         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\n");
1246         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\b");
1247         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\v");
1248         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\f");
1249         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\0");
1250 
1251         // Assert that accessor does not mutate state
1252         assert(el.objectDescriptor == el.objectDescriptor);
1253     }
1254 
1255     /**
1256         Decodes an $(MONO EXTERNAL), which is a constructed data type, defined in
1257         the $(LINK https://www.itu.int, International Telecommunications Union)'s
1258         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
1259 
1260         The specification defines $(MONO EXTERNAL) as:
1261 
1262         $(PRE
1263             EXTERNAL := [UNIVERSAL 8] IMPLICIT SEQUENCE {
1264                 identification CHOICE {
1265                     syntax OBJECT IDENTIFIER,
1266                     presentation-context-id INTEGER,
1267                     context-negotiation SEQUENCE {
1268                         presentation-context-id INTEGER,
1269                         transfer-syntax OBJECT IDENTIFIER } },
1270                 data-value-descriptor ObjectDescriptor OPTIONAL,
1271                 data-value OCTET STRING }
1272         )
1273 
1274         This assumes AUTOMATIC TAGS, so all of the identification choices
1275         will be context-specific and numbered from 0 to 2.
1276     */
1277     deprecated abstract public @property
1278     External external() const;
1279 
1280     /**
1281         Encodes an $(MONO EXTERNAL), which is a constructed data type, defined in
1282         the $(LINK https://www.itu.int, International Telecommunications Union)'s
1283         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
1284 
1285         The specification defines $(MONO EXTERNAL) as:
1286 
1287         $(PRE
1288             EXTERNAL := [UNIVERSAL 8] IMPLICIT SEQUENCE {
1289                 identification CHOICE {
1290                     syntax OBJECT IDENTIFIER,
1291                     presentation-context-id INTEGER,
1292                     context-negotiation SEQUENCE {
1293                         presentation-context-id INTEGER,
1294                         transfer-syntax OBJECT IDENTIFIER } },
1295                 data-value-descriptor ObjectDescriptor OPTIONAL,
1296                 data-value OCTET STRING }
1297         )
1298 
1299         This assumes AUTOMATIC TAGS, so all of the identification choices
1300         will be context-specific and numbered from 0 to 2.
1301     */
1302     deprecated abstract public @property
1303     void external(in External value);
1304 
1305     // Test of all pre-1994 External encoding choices
1306     @system
1307     unittest
1308     {
1309         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
1310         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
1311 
1312         External input = External();
1313         External output;
1314         input.identification = id;
1315         input.dataValueDescriptor = "boop";
1316         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
1317 
1318         Element el = new Element();
1319 
1320         // single-ASN1-type
1321         input.encoding = ASN1ExternalEncodingChoice.singleASN1Type;
1322         el.external = input;
1323         output = el.external;
1324         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1325         assert(output.dataValueDescriptor == "boop");
1326         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1327 
1328         // octet-aligned
1329         input.encoding = ASN1ExternalEncodingChoice.octetAligned;
1330         el.external = input;
1331         output = el.external;
1332         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1333         assert(output.dataValueDescriptor == "boop");
1334         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1335 
1336         // arbitrary
1337         input.encoding = ASN1ExternalEncodingChoice.arbitrary;
1338         el.external = input;
1339         output = el.external;
1340         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1341         assert(output.dataValueDescriptor == "boop");
1342         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1343     }
1344 
1345     /// Encodes a floating-point number
1346     abstract public @property
1347     T realNumber(T)() const if (isFloatingPoint!T);
1348 
1349     /// Encodes a floating-point number
1350     abstract public @property
1351     void realNumber(T)(in T value) if (isFloatingPoint!T);
1352 
1353     @system
1354     unittest
1355     {
1356         for (int i = -100; i < 100; i++)
1357         {
1358             // Alternating negative and positive floating point numbers exploring extreme values
1359             immutable float f = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1360             immutable double d = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1361             immutable real r = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1362             Element elf = new Element();
1363             Element eld = new Element();
1364             Element elr = new Element();
1365             elf.realNumber!float = f;
1366             eld.realNumber!double = d;
1367             elr.realNumber!real = r;
1368             assert(approxEqual(elf.realNumber!float, f));
1369             assert(approxEqual(elf.realNumber!double, f));
1370             assert(approxEqual(elf.realNumber!real, f));
1371             assert(approxEqual(eld.realNumber!float, d));
1372             assert(approxEqual(eld.realNumber!double, d));
1373             assert(approxEqual(eld.realNumber!real, d));
1374             assert(approxEqual(elr.realNumber!float, d));
1375             assert(approxEqual(elr.realNumber!double, d));
1376             assert(approxEqual(elr.realNumber!real, d));
1377         }
1378     }
1379 
1380     // Test a few edge cases
1381     @system
1382     unittest
1383     {
1384         immutable float[] tests = [
1385             0.0,
1386             -0.0,
1387             (10.0 / 3.0), // Non-terminating decimal
1388             (-10.0 / 3.0), // Negative non-terminating decimal
1389             1.0,
1390             -1.0
1391         ];
1392         Element el = new Element();
1393 
1394         foreach (test; tests)
1395         {
1396             el.realNumber!float = cast(float) test;
1397             assert(approxEqual(el.realNumber!float, cast(float) test));
1398             el.realNumber!double = cast(double) test;
1399             assert(approxEqual(el.realNumber!double, cast(double) test));
1400             el.realNumber!real = test;
1401             assert(approxEqual(el.realNumber!real, test));
1402         }
1403     }
1404 
1405     // Test both positive and negative infinity for each floating-point type
1406     @system
1407     unittest
1408     {
1409         Element el = new Element();
1410 
1411         // float.infinity
1412         el.realNumber!float = float.infinity;
1413         assert(el.realNumber!float  == float.infinity);
1414         assert(el.realNumber!double == float.infinity);
1415         assert(el.realNumber!real   == float.infinity);
1416 
1417         el.realNumber!double = float.infinity;
1418         assert(el.realNumber!float  == float.infinity);
1419         assert(el.realNumber!double == float.infinity);
1420         assert(el.realNumber!real   == float.infinity);
1421 
1422         el.realNumber!real = float.infinity;
1423         assert(el.realNumber!float  == float.infinity);
1424         assert(el.realNumber!double == float.infinity);
1425         assert(el.realNumber!real   == float.infinity);
1426 
1427         el.realNumber!float = -float.infinity;
1428         assert(el.realNumber!float  == -float.infinity);
1429         assert(el.realNumber!double == -float.infinity);
1430         assert(el.realNumber!real   == -float.infinity);
1431 
1432         el.realNumber!double = -float.infinity;
1433         assert(el.realNumber!float  == -float.infinity);
1434         assert(el.realNumber!double == -float.infinity);
1435         assert(el.realNumber!real   == -float.infinity);
1436 
1437         el.realNumber!real = -float.infinity;
1438         assert(el.realNumber!float  == -float.infinity);
1439         assert(el.realNumber!double == -float.infinity);
1440         assert(el.realNumber!real   == -float.infinity);
1441 
1442         // double.infinity
1443         el.realNumber!float = double.infinity;
1444         assert(el.realNumber!float  == double.infinity);
1445         assert(el.realNumber!double == double.infinity);
1446         assert(el.realNumber!real   == double.infinity);
1447 
1448         el.realNumber!double = double.infinity;
1449         assert(el.realNumber!float  == double.infinity);
1450         assert(el.realNumber!double == double.infinity);
1451         assert(el.realNumber!real   == double.infinity);
1452 
1453         el.realNumber!real = double.infinity;
1454         assert(el.realNumber!float  == double.infinity);
1455         assert(el.realNumber!double == double.infinity);
1456         assert(el.realNumber!real   == double.infinity);
1457 
1458         el.realNumber!float = -double.infinity;
1459         assert(el.realNumber!float  == -double.infinity);
1460         assert(el.realNumber!double == -double.infinity);
1461         assert(el.realNumber!real   == -double.infinity);
1462 
1463         el.realNumber!double = -double.infinity;
1464         assert(el.realNumber!float  == -double.infinity);
1465         assert(el.realNumber!double == -double.infinity);
1466         assert(el.realNumber!real   == -double.infinity);
1467 
1468         el.realNumber!real = -double.infinity;
1469         assert(el.realNumber!float  == -double.infinity);
1470         assert(el.realNumber!double == -double.infinity);
1471         assert(el.realNumber!real   == -double.infinity);
1472 
1473         // real.infinity
1474         el.realNumber!float = real.infinity;
1475         assert(el.realNumber!float  == real.infinity);
1476         assert(el.realNumber!double == real.infinity);
1477         assert(el.realNumber!real   == real.infinity);
1478 
1479         el.realNumber!double = real.infinity;
1480         assert(el.realNumber!float  == real.infinity);
1481         assert(el.realNumber!double == real.infinity);
1482         assert(el.realNumber!real   == real.infinity);
1483 
1484         el.realNumber!real = real.infinity;
1485         assert(el.realNumber!float  == real.infinity);
1486         assert(el.realNumber!double == real.infinity);
1487         assert(el.realNumber!real   == real.infinity);
1488 
1489         el.realNumber!float = -real.infinity;
1490         assert(el.realNumber!float  == -real.infinity);
1491         assert(el.realNumber!double == -real.infinity);
1492         assert(el.realNumber!real   == -real.infinity);
1493 
1494         el.realNumber!double = -real.infinity;
1495         assert(el.realNumber!float  == -real.infinity);
1496         assert(el.realNumber!double == -real.infinity);
1497         assert(el.realNumber!real   == -real.infinity);
1498 
1499         el.realNumber!real = -real.infinity;
1500         assert(el.realNumber!float  == -real.infinity);
1501         assert(el.realNumber!double == -real.infinity);
1502         assert(el.realNumber!real   == -real.infinity);
1503     }
1504 
1505     // Test NaN
1506     @system
1507     unittest
1508     {
1509         Element el = new Element();
1510 
1511         el.realNumber!float = float.nan;
1512         assert(el.realNumber!float.isNaN);
1513         assert(el.realNumber!double.isNaN);
1514         assert(el.realNumber!real.isNaN);
1515 
1516         el.realNumber!double = float.nan;
1517         assert(el.realNumber!float.isNaN);
1518         assert(el.realNumber!double.isNaN);
1519         assert(el.realNumber!real.isNaN);
1520 
1521         el.realNumber!real = float.nan;
1522         assert(el.realNumber!float.isNaN);
1523         assert(el.realNumber!double.isNaN);
1524         assert(el.realNumber!real.isNaN);
1525 
1526         el.realNumber!float = double.nan;
1527         assert(el.realNumber!float.isNaN);
1528         assert(el.realNumber!double.isNaN);
1529         assert(el.realNumber!real.isNaN);
1530 
1531         el.realNumber!double = double.nan;
1532         assert(el.realNumber!float.isNaN);
1533         assert(el.realNumber!double.isNaN);
1534         assert(el.realNumber!real.isNaN);
1535 
1536         el.realNumber!real = double.nan;
1537         assert(el.realNumber!float.isNaN);
1538         assert(el.realNumber!double.isNaN);
1539         assert(el.realNumber!real.isNaN);
1540 
1541         el.realNumber!float = real.nan;
1542         assert(el.realNumber!float.isNaN);
1543         assert(el.realNumber!double.isNaN);
1544         assert(el.realNumber!real.isNaN);
1545 
1546         el.realNumber!double = real.nan;
1547         assert(el.realNumber!float.isNaN);
1548         assert(el.realNumber!double.isNaN);
1549         assert(el.realNumber!real.isNaN);
1550 
1551         el.realNumber!real = real.nan;
1552         assert(el.realNumber!float.isNaN);
1553         assert(el.realNumber!double.isNaN);
1554         assert(el.realNumber!real.isNaN);
1555     }
1556 
1557     // Tests of maximum and minimums of integral types
1558     @system
1559     unittest
1560     {
1561         immutable float[] tests = cast(float[]) [
1562             byte.max,
1563             byte.min,
1564             ubyte.max,
1565             ubyte.min,
1566             short.max,
1567             short.min,
1568             ushort.max,
1569             ushort.min,
1570             int.max,
1571             int.min,
1572             uint.max,
1573             uint.min,
1574             long.max,
1575             long.min,
1576             ulong.max,
1577             ulong.min
1578         ];
1579 
1580         Element el = new Element();
1581 
1582         foreach (test; tests)
1583         {
1584             el.realNumber!float = cast(float) test;
1585             assert(approxEqual(el.realNumber!float, cast(float) test));
1586             el.realNumber!double = cast(double) test;
1587             assert(approxEqual(el.realNumber!double, cast(double) test));
1588             el.realNumber!real = test;
1589             assert(approxEqual(el.realNumber!real, test));
1590         }
1591     }
1592 
1593     // Tests of maximum and minimums of floating-point types
1594     @system
1595     unittest
1596     {
1597         Element el = new Element();
1598 
1599         // Maximums and minimums of floats
1600         el.realNumber!float = float.max;
1601         assert(approxEqual(el.realNumber!float, float.max));
1602         el.realNumber!double = cast(double) float.max;
1603         assert(approxEqual(el.realNumber!double, float.max));
1604         el.realNumber!real = cast(real) float.max;
1605         assert(approxEqual(el.realNumber!real, float.max));
1606 
1607         el.realNumber!float = float.max_10_exp;
1608         assert(approxEqual(el.realNumber!float, float.max_10_exp));
1609         el.realNumber!double = cast(double) float.max_10_exp;
1610         assert(approxEqual(el.realNumber!double, float.max_10_exp));
1611         el.realNumber!real = cast(real) float.max_10_exp;
1612         assert(approxEqual(el.realNumber!real, float.max_10_exp));
1613 
1614         el.realNumber!float = float.max_exp;
1615         assert(approxEqual(el.realNumber!float, float.max_exp));
1616         el.realNumber!double = cast(double) float.max_exp;
1617         assert(approxEqual(el.realNumber!double, float.max_exp));
1618         el.realNumber!real = cast(real) float.max_exp;
1619         assert(approxEqual(el.realNumber!real, float.max_exp));
1620 
1621         el.realNumber!float = float.min_10_exp;
1622         assert(approxEqual(el.realNumber!float, float.min_10_exp));
1623         el.realNumber!double = cast(double) float.min_10_exp;
1624         assert(approxEqual(el.realNumber!double, float.min_10_exp));
1625         el.realNumber!real = cast(real) float.min_10_exp;
1626         assert(approxEqual(el.realNumber!real, float.min_10_exp));
1627 
1628         el.realNumber!float = float.min_exp;
1629         assert(approxEqual(el.realNumber!float, float.min_exp));
1630         el.realNumber!double = cast(double) float.min_exp;
1631         assert(approxEqual(el.realNumber!double, float.min_exp));
1632         el.realNumber!real = cast(real) float.min_exp;
1633         assert(approxEqual(el.realNumber!real, float.min_exp));
1634 
1635         el.realNumber!float = float.min_normal;
1636         assert(approxEqual(el.realNumber!float, float.min_normal));
1637         el.realNumber!double = cast(double) float.min_normal;
1638         assert(approxEqual(el.realNumber!double, float.min_normal));
1639         el.realNumber!real = cast(real) float.min_normal;
1640         assert(approxEqual(el.realNumber!real, float.min_normal));
1641 
1642         // Maximums and minimums of doubles
1643         el.realNumber!double = cast(double) double.max;
1644         assert(approxEqual(el.realNumber!double, double.max));
1645         el.realNumber!real = cast(real) double.max;
1646         assert(approxEqual(el.realNumber!real, double.max));
1647 
1648         el.realNumber!double = cast(double) double.max_10_exp;
1649         assert(approxEqual(el.realNumber!double, double.max_10_exp));
1650         el.realNumber!real = cast(real) double.max_10_exp;
1651         assert(approxEqual(el.realNumber!real, double.max_10_exp));
1652 
1653         el.realNumber!double = cast(double) double.max_exp;
1654         assert(approxEqual(el.realNumber!double, double.max_exp));
1655         el.realNumber!real = cast(real) double.max_exp;
1656         assert(approxEqual(el.realNumber!real, double.max_exp));
1657 
1658         el.realNumber!double = cast(double) double.min_10_exp;
1659         assert(approxEqual(el.realNumber!double, double.min_10_exp));
1660         el.realNumber!real = cast(real) double.min_10_exp;
1661         assert(approxEqual(el.realNumber!real, double.min_10_exp));
1662 
1663         el.realNumber!double = cast(double) double.min_exp;
1664         assert(approxEqual(el.realNumber!double, double.min_exp));
1665         el.realNumber!real = cast(real) double.min_exp;
1666         assert(approxEqual(el.realNumber!real, double.min_exp));
1667 
1668         el.realNumber!double = cast(double) double.min_normal;
1669         assert(approxEqual(el.realNumber!double, double.min_normal));
1670         el.realNumber!real = cast(real) double.min_normal;
1671         assert(approxEqual(el.realNumber!real, double.min_normal));
1672 
1673         // Maximums and minimums of reals
1674         el.realNumber!real = cast(real) real.max;
1675         assert(approxEqual(el.realNumber!real, real.max));
1676 
1677         el.realNumber!real = cast(real) real.max_10_exp;
1678         assert(approxEqual(el.realNumber!real, real.max_10_exp));
1679 
1680         el.realNumber!real = cast(real) real.max_exp;
1681         assert(approxEqual(el.realNumber!real, real.max_exp));
1682 
1683         el.realNumber!real = cast(real) real.min_10_exp;
1684         assert(approxEqual(el.realNumber!real, real.min_10_exp));
1685 
1686         el.realNumber!real = cast(real) real.min_exp;
1687         assert(approxEqual(el.realNumber!real, real.min_exp));
1688 
1689         el.realNumber!real = cast(real) real.min_normal;
1690         assert(approxEqual(el.realNumber!real, real.min_normal));
1691     }
1692 
1693     // Test with all of the math constants, to make sure there are no edge cases.
1694     @system
1695     unittest
1696     {
1697         import std.math :
1698             E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, LN10, LN2, LOG2,
1699             LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2, sqrt;
1700 
1701         immutable real[] tests = [
1702             E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, LN10, LN2, LOG2,
1703             LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2,
1704             (SQRT2 / 2.0), // SQRT_2_OVER_2
1705             ((1.0 + sqrt(5.0)) / 2.0), // GOLDEN_RATIO
1706             0.57721,      // EULER_MASCHERONI_CONSTANT
1707             0.2614972128, // MEISSEL_MERTENS_CONSTANT
1708             0.2801694990, // BERNSTEINS_CONSTANT
1709             0.3036630028, // GAUSS_KUZMIN_WIRSING_CONSTANT
1710             0.3532363718, // HAFNER_SARNAK_MCCURLEY_CONSTANT
1711             0.5671432904, // OMEGA_CONSTANT
1712             0.6243299885, // GOLOMB_DICKMAN_CONSTANT
1713             0.6434105462, // CAHENS_CONSTANT
1714             0.6601618158, // TWIN_PRIME_CONSTANT
1715             0.6627434193, // LAPLACE_LIMIT
1716             0.70258,      // LANDAU_RAMANUJAN_CONSTANT
1717             0.8093940205, // ALLADI_GRINSTEAD_CONSTANT
1718             0.87058838,   // BRUNS_CONSTANT_FOR_PRIME_QUADRUPLETS
1719             0.9159655941, // CATALANS_CONSTANT
1720             1.0986858055, // LENGYELS_CONSTANT
1721             1.13198824,   // VISWANATHS_CONSTANT
1722             1.2020569,    // APERYS_CONSTANT
1723             1.30357,      // CONWAYS_CONSTANT
1724             1.3063778838, // MILLS_CONSTANT
1725             1.3247179572, // PLASTIC_CONSTANT
1726             1.4513692348, // RAMANUJAN_SOLDNER_CONSTANT
1727             1.4560749485, // BACKHOUSES_CONSTANT
1728             1.4670780794, // PORTERS_CONSTANT
1729             1.5396007178, // LIEBS_SQUARE_ICE_CONSTANT
1730             1.6066951524, // ERDOS_BORWEIN_CONSTANT
1731             1.7052111401, // NIVENS_CONSTANT
1732             1.9021605831, // BRUNS_CONSTANT_FOR_TWIN_PRIMES
1733             2.2955871493, // UNIVERSAL_PARABOLIC_CONSTANT
1734             2.5029078750, // FEIGENBAUM_CONSTANT_ALPHA
1735             2.5849817595, // SIERPINSKIS_CONSTANT
1736             2.6854520010, // KHINCHINS_CONSTANT
1737             2.8077702420, // FRANSEN_ROBINSON_CONSTANT
1738             3.2758229187, // LEVYS_CONSTANT
1739             3.3598856662, // RECIPROCAL_FIBONACCI_CONSTANT
1740             4.6692016091, // FEIGENBAUM_CONSTANT_DELTA
1741             1.2824271291  // GLAISHER_KINKELIN_CONSTANT
1742         ];
1743 
1744         Element el = new Element();
1745 
1746         foreach (test; tests)
1747         {
1748             el.realNumber!float = cast(float) test;
1749             assert(approxEqual(el.realNumber!float, cast(float) test));
1750             el.realNumber!double = cast(double) test;
1751             assert(approxEqual(el.realNumber!double, cast(double) test));
1752             el.realNumber!real = test;
1753             assert(approxEqual(el.realNumber!real, test));
1754         }
1755     }
1756 
1757     /// Encodes an integer that represents an ENUMERATED value
1758     abstract public @property
1759     T enumerated(T)() const if (isIntegral!T && isSigned!T);
1760 
1761     /// Decodes an integer that represents an ENUMERATED value
1762     abstract public @property
1763     void enumerated(T)(in T value) if (isIntegral!T && isSigned!T);
1764 
1765     // Test all 255 signed 8-bit integers
1766     @system
1767     unittest
1768     {
1769         Element el = new Element();
1770         for (byte i = byte.min; i < byte.max; i++)
1771         {
1772             el.enumerated!byte = i;
1773             assert(el.enumerated!byte == i);
1774             el.enumerated!short = i;
1775             assert(el.enumerated!short == i);
1776             el.enumerated!int = i;
1777             assert(el.enumerated!int == i);
1778             el.enumerated!long = i;
1779             assert(el.enumerated!long == i);
1780         }
1781     }
1782 
1783     // Test all 65536 signed 16-bit integers
1784     @system
1785     unittest
1786     {
1787         Element el = new Element();
1788         for (short i = short.min; i < short.max; i++)
1789         {
1790             el.enumerated!short = i;
1791             assert(el.enumerated!short == i);
1792             el.enumerated!int = i;
1793             assert(el.enumerated!int == i);
1794             el.enumerated!long = i;
1795             assert(el.enumerated!long == i);
1796         }
1797     }
1798 
1799     // Test a few signed 32-bit integers
1800     @system
1801     unittest
1802     {
1803         Element el = new Element();
1804         for (int i = int.min; i < int.max-15_485_863; i += 15_485_863) // 15,485,863 is the millionth prime
1805         {
1806             ubyte[] ub;
1807             ub.length = int.sizeof;
1808             *cast(int *)&ub[0] = i;
1809             version (LittleEndian) reverse(ub);
1810             el.enumerated!int = i;
1811             assert(el.enumerated!int == i);
1812             el.enumerated!long = i;
1813             assert(el.enumerated!long == i);
1814         }
1815     }
1816 
1817     // Test a few signed 64-bit integers
1818     @system
1819     unittest
1820     {
1821         Element el = new Element();
1822         for (long i = long.min; i < long.max-965_211_250_482_432_409; i += 965_211_250_482_432_409) // 50 millionth prime^2
1823         {
1824             el.enumerated!long = i;
1825             assert(el.enumerated!long == i);
1826         }
1827     }
1828 
1829     @system
1830     unittest
1831     {
1832         Element el = new Element();
1833 
1834         // Tests for zero
1835         el.enumerated!byte = 0;
1836         assert(el.enumerated!byte == 0);
1837         assert(el.enumerated!short == 0);
1838         assert(el.enumerated!int == 0);
1839         assert(el.enumerated!long == 0L);
1840 
1841         el.enumerated!short = 0;
1842         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1843         assert(el.enumerated!short == 0);
1844         assert(el.enumerated!int == 0);
1845         assert(el.enumerated!long == 0L);
1846 
1847         el.enumerated!int = 0;
1848         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1849         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1850         assert(el.enumerated!int == 0);
1851         assert(el.enumerated!long == 0L);
1852 
1853         el.enumerated!long = 0L;
1854         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1855         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1856         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1857         assert(el.enumerated!long == 0L);
1858 
1859         // Tests for small positives
1860         el.enumerated!byte = 3;
1861         assert(el.enumerated!byte == 3);
1862         assert(el.enumerated!short == 3);
1863         assert(el.enumerated!int == 3);
1864         assert(el.enumerated!long == 3L);
1865 
1866         el.enumerated!short = 5;
1867         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1868         assert(el.enumerated!short == 5);
1869         assert(el.enumerated!int == 5);
1870         assert(el.enumerated!long == 5L);
1871 
1872         el.enumerated!int = 7;
1873         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1874         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1875         assert(el.enumerated!int == 7);
1876         assert(el.enumerated!long == 7L);
1877 
1878         el.enumerated!long = 9L;
1879         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1880         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1881         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1882         assert(el.enumerated!long == 9L);
1883 
1884         // Tests for small negatives
1885         el.enumerated!byte = -3;
1886         assert(el.enumerated!byte == -3);
1887         assert(el.enumerated!short == -3);
1888         assert(el.enumerated!int == -3);
1889         assert(el.enumerated!long == -3L);
1890 
1891         el.enumerated!short = -5;
1892         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1893         assert(el.enumerated!short == -5);
1894         assert(el.enumerated!int == -5);
1895         assert(el.enumerated!long == -5L);
1896 
1897         el.enumerated!int = -7;
1898         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1899         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1900         assert(el.enumerated!int == -7);
1901         assert(el.enumerated!long == -7L);
1902 
1903         el.enumerated!long = -9L;
1904         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1905         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1906         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1907         assert(el.enumerated!long == -9L);
1908 
1909         // Tests for large positives
1910         el.enumerated!short = 20000;
1911         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1912         assert(el.enumerated!short == 20000);
1913         assert(el.enumerated!int == 20000);
1914         assert(el.enumerated!long == 20000L);
1915 
1916         el.enumerated!int = 70000;
1917         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1918         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1919         assert(el.enumerated!int == 70000);
1920         assert(el.enumerated!long == 70000L);
1921 
1922         el.enumerated!long = 70000L;
1923         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1924         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1925         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1926         assert(el.enumerated!long == 70000L);
1927 
1928         // Tests for large negatives
1929         el.enumerated!short = -20000;
1930         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1931         assert(el.enumerated!short == -20000);
1932         assert(el.enumerated!int == -20000);
1933         assert(el.enumerated!long == -20000L);
1934 
1935         el.enumerated!int = -70000;
1936         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1937         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1938         assert(el.enumerated!int == -70000);
1939         assert(el.enumerated!long == -70000L);
1940 
1941         el.enumerated!long = -70000L;
1942         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1943         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1944         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1945         assert(el.enumerated!long == -70000L);
1946 
1947         // Tests for maximum values
1948         el.enumerated!byte = byte.max;
1949         assert(el.enumerated!byte == byte.max);
1950         assert(el.enumerated!short == byte.max);
1951         assert(el.enumerated!int == byte.max);
1952         assert(el.enumerated!long == byte.max);
1953 
1954         el.enumerated!short = short.max;
1955         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1956         assert(el.enumerated!short == short.max);
1957         assert(el.enumerated!int == short.max);
1958         assert(el.enumerated!long == short.max);
1959 
1960         el.enumerated!int = int.max;
1961         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1962         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1963         assert(el.enumerated!int == int.max);
1964         assert(el.enumerated!long == int.max);
1965 
1966         el.enumerated!long = long.max;
1967         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1968         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1969         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1970         assert(el.enumerated!long == long.max);
1971 
1972         // Tests for minimum values
1973         el.enumerated!byte = byte.min;
1974         assert(el.enumerated!byte == byte.min);
1975         assert(el.enumerated!short == byte.min);
1976         assert(el.enumerated!int == byte.min);
1977         assert(el.enumerated!long == byte.min);
1978 
1979         el.enumerated!short = short.min;
1980         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1981         assert(el.enumerated!short == short.min);
1982         assert(el.enumerated!int == short.min);
1983         assert(el.enumerated!long == short.min);
1984 
1985         el.enumerated!int = int.min;
1986         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1987         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1988         assert(el.enumerated!int == int.min);
1989         assert(el.enumerated!long == int.min);
1990 
1991         el.enumerated!long = long.min;
1992         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1993         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1994         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1995         assert(el.enumerated!long == long.min);
1996 
1997         // Assert that accessor does not mutate state
1998         assert(el.enumerated!long == el.enumerated!long);
1999     }
2000 
2001     ///
2002     public alias embeddedPDV = embeddedPresentationDataValue;
2003     /**
2004         Decodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
2005         the $(LINK https://www.itu.int, International Telecommunications Union)'s
2006         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2007 
2008         The specification defines $(MONO EmbeddedPDV) as:
2009 
2010         $(PRE
2011             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2012                 identification CHOICE {
2013                     syntaxes SEQUENCE {
2014                         abstract OBJECT IDENTIFIER,
2015                         transfer OBJECT IDENTIFIER },
2016                     syntax OBJECT IDENTIFIER,
2017                     presentation-context-id INTEGER,
2018                     context-negotiation SEQUENCE {
2019                         presentation-context-id INTEGER,
2020                         transfer-syntax OBJECT IDENTIFIER },
2021                     transfer-syntax OBJECT IDENTIFIER,
2022                     fixed NULL },
2023                 data-value-descriptor ObjectDescriptor OPTIONAL,
2024                 data-value OCTET STRING }
2025             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
2026         )
2027 
2028         This assumes AUTOMATIC TAGS, so all of the identification choices
2029         will be context-specific and numbered from 0 to 5.
2030     */
2031     abstract public @property
2032     EmbeddedPDV embeddedPresentationDataValue() const;
2033 
2034     /**
2035         Encodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
2036         the $(LINK https://www.itu.int, International Telecommunications Union)'s
2037         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2038 
2039         The specification defines $(MONO EmbeddedPDV) as:
2040 
2041         $(PRE
2042             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2043                 identification CHOICE {
2044                     syntaxes SEQUENCE {
2045                         abstract OBJECT IDENTIFIER,
2046                         transfer OBJECT IDENTIFIER },
2047                     syntax OBJECT IDENTIFIER,
2048                     presentation-context-id INTEGER,
2049                     context-negotiation SEQUENCE {
2050                         presentation-context-id INTEGER,
2051                         transfer-syntax OBJECT IDENTIFIER },
2052                     transfer-syntax OBJECT IDENTIFIER,
2053                     fixed NULL },
2054                 data-value-descriptor ObjectDescriptor OPTIONAL,
2055                 data-value OCTET STRING }
2056             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
2057         )
2058 
2059         This assumes AUTOMATIC TAGS, so all of the identification choices
2060         will be context-specific and numbered from 0 to 5.
2061     */
2062     abstract public @property
2063     void embeddedPresentationDataValue(in EmbeddedPDV value);
2064 
2065     @system
2066     unittest
2067     {
2068         ASN1Syntaxes syn = ASN1Syntaxes();
2069         syn.abstractSyntax = new OID(1, 3, 6, 4, 1, 256, 7);
2070         syn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 8);
2071 
2072         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2073         id.syntaxes = syn;
2074 
2075         EmbeddedPDV input = EmbeddedPDV();
2076         input.identification = id;
2077         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2078 
2079         Element el = new Element();
2080         el.embeddedPDV = input;
2081         EmbeddedPDV output = el.embeddedPDV;
2082         assert(output.identification.syntaxes.abstractSyntax == new OID(1, 3, 6, 4, 1, 256, 7));
2083         assert(output.identification.syntaxes.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 8));
2084         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2085 
2086         // Assert that accessor does not mutate state
2087         assert(el.embeddedPDV == el.embeddedPDV);
2088     }
2089 
2090     @system
2091     unittest
2092     {
2093         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2094         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
2095 
2096         EmbeddedPDV input = EmbeddedPDV();
2097         input.identification = id;
2098         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2099 
2100         Element el = new Element();
2101         el.embeddedPDV = input;
2102         EmbeddedPDV output = el.embeddedPDV;
2103         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
2104         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2105     }
2106 
2107     @system
2108     unittest
2109     {
2110         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2111         id.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
2112 
2113         EmbeddedPDV input = EmbeddedPDV();
2114         input.identification = id;
2115         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2116 
2117         Element el = new Element();
2118         el.embeddedPDV = input;
2119         EmbeddedPDV output = el.embeddedPDV;
2120         assert(output.identification.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 39));
2121         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2122     }
2123 
2124     @system
2125     unittest
2126     {
2127         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2128         id.fixed = true;
2129 
2130         EmbeddedPDV input = EmbeddedPDV();
2131         input.identification = id;
2132         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2133 
2134         Element el = new Element();
2135         el.embeddedPDV = input;
2136         EmbeddedPDV output = el.embeddedPDV;
2137         assert(output.identification.fixed == true);
2138         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2139     }
2140 
2141     ///
2142     public alias utf8String = unicodeTransformationFormat8String;
2143     /// Decodes a UTF-8 String
2144     abstract public @property
2145     string unicodeTransformationFormat8String() const;
2146 
2147     /// Encodes a UTF-8 String
2148     abstract public @property
2149     void unicodeTransformationFormat8String(in string value);
2150 
2151     @system
2152     unittest
2153     {
2154         Element el = new Element();
2155         el.utf8String = "";
2156         assert(el.utf8String == "");
2157         el.utf8String = "henlo borthers";
2158         assert(el.utf8String == "henlo borthers");
2159 
2160         // Assert that accessor does not mutate state
2161         assert(el.utf8String == el.utf8String);
2162     }
2163 
2164     // Test that mutating the value does not mutate an external reference.
2165     @system
2166     unittest
2167     {
2168         string test = "HENLO";
2169         Element el = new Element();
2170         el.utf8String = test;
2171         el.value[4] = 0x88u;
2172         assert(test[4] == 'O');
2173     }
2174 
2175     ///
2176     public alias roid = relativeObjectIdentifier;
2177     ///
2178     public alias relativeOID = relativeObjectIdentifier;
2179     /// Decodes a portion of an Object Identifier
2180     abstract public @property
2181     OIDNode[] relativeObjectIdentifier() const;
2182 
2183     /// Encodes a porition of an Object Identifier
2184     abstract public @property
2185     void relativeObjectIdentifier(in OIDNode[] value);
2186 
2187     @system
2188     unittest
2189     {
2190         Element el = new Element();
2191         OIDNode[] input = [ OIDNode(3), OIDNode(5), OIDNode(7), OIDNode(9) ];
2192         el.roid = input;
2193         OIDNode[] output = el.roid;
2194 
2195         assert(input.length == output.length);
2196         for (ptrdiff_t i = 0; i < input.length; i++)
2197         {
2198             assert(input[i] == output[i]);
2199         }
2200 
2201         // Assert that accessor does not mutate state
2202         assert(el.relativeObjectIdentifier == el.relativeObjectIdentifier);
2203     }
2204 
2205     @system
2206     unittest
2207     {
2208         size_t[] sensitiveValues = [
2209             0u,
2210             1u,
2211             2u,
2212             3u,
2213             7u,
2214             8u,
2215             cast (size_t) (byte.max - 1u),
2216             cast (size_t) (byte.max),
2217             cast (size_t) (byte.max + 1u),
2218             cast (size_t) (ubyte.max - 1u),
2219             cast (size_t) (ubyte.max),
2220             cast (size_t) (ubyte.max + 1u),
2221             cast (size_t) (short.max - 1u),
2222             cast (size_t) (short.max),
2223             cast (size_t) (short.max + 1u),
2224             cast (size_t) (ushort.max - 1u),
2225             cast (size_t) (ushort.max),
2226             cast (size_t) (ushort.max + 1u),
2227             cast (size_t) (int.max - 1u),
2228             cast (size_t) (int.max),
2229             cast (size_t) (int.max + 1u),
2230             cast (size_t) (uint.max - 1u),
2231             cast (size_t) (uint.max),
2232             cast (size_t) (uint.max + 1u),
2233             cast (size_t) (long.max - 1u),
2234             cast (size_t) (long.max),
2235             cast (size_t) (long.max + 1u),
2236             cast (size_t) (ulong.max - 1u),
2237             cast (size_t) (ulong.max)
2238         ];
2239 
2240         Element el = new Element();
2241         foreach (immutable size_t number; sensitiveValues)
2242         {
2243             import std.stdio : writefln, writeln;
2244             el.roid = [ OIDNode(number) ];
2245             assert(el.roid == [ OIDNode(number) ]);
2246         }
2247     }
2248 
2249     /**
2250         Decodes an array of elements.
2251 
2252         Credits:
2253             Thanks to StackOverflow user
2254             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2255             for teaching me how to create the abstract method that uses the
2256             child class as a template.
2257     */
2258     abstract public @property
2259     Element[] sequence() const;
2260 
2261     /**
2262         Encodes an array of elements.
2263 
2264         Credits:
2265             Thanks to StackOverflow user
2266             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2267             for teaching me how to create the abstract method that uses the
2268             child class as a template.
2269     */
2270     abstract public @property
2271     void sequence(in Element[] value);
2272 
2273     /**
2274         Decodes an array of elements.
2275 
2276         Credits:
2277             Thanks to StackOverflow user
2278             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2279             for teaching me how to create the abstract method that uses the
2280             child class as a template.
2281     */
2282     abstract public @property
2283     Element[] set() const;
2284 
2285     /**
2286         Encodes an array of elements.
2287 
2288         Credits:
2289             Thanks to StackOverflow user
2290             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2291             for teaching me how to create the abstract method that uses the
2292             child class as a template.
2293     */
2294     abstract public @property
2295     void set(in Element[] value);
2296 
2297     /**
2298         Decodes a string, where the characters of the string are limited to
2299         0 - 9 and space.
2300     */
2301     abstract public @property
2302     string numericString() const;
2303 
2304     /**
2305         Encodes a string, where the characters of the string are limited to
2306         0 - 9 and space.
2307     */
2308     abstract public @property
2309     void numericString(in string value);
2310 
2311     @system
2312     unittest
2313     {
2314         Element el = new Element();
2315         el.numericString = "";
2316         assert(el.numericString == "");
2317         el.numericString = "1234567890";
2318         assert(el.numericString == "1234567890");
2319         el.numericString = " ";
2320         assert(el.numericString == " ");
2321         assertThrown!ASN1ValueCharactersException(el.numericString = "hey hey");
2322         assertThrown!ASN1ValueCharactersException(el.numericString = "12345676789A");
2323 
2324         // Assert that accessor does not mutate state
2325         assert(el.numericString == el.numericString);
2326     }
2327 
2328     // Test that mutating the value does not mutate an external reference.
2329     @system
2330     unittest
2331     {
2332         string test = "12345";
2333         Element el = new Element();
2334         el.numericString = test;
2335         el.value[4] = 0x88u;
2336         assert(test[4] == '5');
2337     }
2338 
2339     /**
2340         Decodes a string that will only contain characters a-z, A-Z, 0-9,
2341         space, apostrophe, parentheses, comma, minus, plus, period,
2342         forward slash, colon, equals, and question mark.
2343     */
2344     abstract public @property
2345     string printableString() const;
2346 
2347     /**
2348         Encodes a string that will only contain characters a-z, A-Z, 0-9,
2349         space, apostrophe, parentheses, comma, minus, plus, period,
2350         forward slash, colon, equals, and question mark.
2351     */
2352     abstract public @property
2353     void printableString(in string value);
2354 
2355     @system
2356     unittest
2357     {
2358         Element el = new Element();
2359         el.printableString = "";
2360         assert(el.printableString == "");
2361         el.printableString = "1234567890 asdfjkl";
2362         assert(el.printableString == "1234567890 asdfjkl");
2363         el.printableString = " ";
2364         assert(el.printableString == " ");
2365         assertThrown!ASN1ValueCharactersException(el.printableString = "\t");
2366         assertThrown!ASN1ValueCharactersException(el.printableString = "\n");
2367         assertThrown!ASN1ValueCharactersException(el.printableString = "\0");
2368         assertThrown!ASN1ValueCharactersException(el.printableString = "\v");
2369         assertThrown!ASN1ValueCharactersException(el.printableString = "\b");
2370         assertThrown!ASN1ValueCharactersException(el.printableString = "\r");
2371         assertThrown!ASN1ValueCharactersException(el.printableString = "\x13");
2372 
2373         // Assert that accessor does not mutate state
2374         assert(el.printableString == el.printableString);
2375     }
2376 
2377     // Test that mutating the value does not mutate an external reference.
2378     @system
2379     unittest
2380     {
2381         string test = "HENLO";
2382         Element el = new Element();
2383         el.printableString = test;
2384         el.value[4] = 0x88u;
2385         assert(test[4] == 'O');
2386     }
2387 
2388     ///
2389     public alias t61String = teletexString;
2390     /// Decodes bytes representing the T.61 Character Set
2391     abstract public @property
2392     ubyte[] teletexString() const;
2393 
2394     /// Encodes bytes representing the T.61 Character Set
2395     abstract public @property
2396     void teletexString(in ubyte[] value);
2397 
2398     @system
2399     unittest
2400     {
2401         Element el = new Element();
2402         el.teletexString = [];
2403         assert(el.teletexString == []);
2404         el.teletexString = [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ];
2405         assert(el.teletexString == [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ]);
2406 
2407         // Assert that accessor does not mutate state
2408         assert(el.teletexString == el.teletexString);
2409     }
2410 
2411     // Test that mutating the value does not mutate an external reference.
2412     @system
2413     unittest
2414     {
2415         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
2416         Element el = new Element();
2417         el.teletexString = test;
2418         el.value[4] = 0x88u;
2419         assert(test[4] == 0x6Au);
2420     }
2421 
2422     // Test that mutating a large value does not mutate an external reference.
2423     @system
2424     unittest
2425     {
2426         ubyte[] test;
2427         test.length = 10000u;
2428         Element el = new Element();
2429         el.teletexString = test;
2430         el.value[4] = 0x88u;
2431         assert(test[4] == 0x00u);
2432     }
2433 
2434     abstract public @property
2435     ubyte[] videotexString() const;
2436 
2437     abstract public @property
2438     void videotexString(in ubyte[] value);
2439 
2440     @system
2441     unittest
2442     {
2443         Element el = new Element();
2444         el.videotexString = [];
2445         assert(el.videotexString == []);
2446         el.videotexString = [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ];
2447         assert(el.videotexString == [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ]);
2448 
2449         // Assert that accessor does not mutate state
2450         assert(el.videotexString == el.videotexString);
2451     }
2452 
2453     // Test that mutating the value does not mutate an external reference.
2454     @system
2455     unittest
2456     {
2457         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
2458         Element el = new Element();
2459         el.videotexString = test;
2460         el.value[4] = 0x88u;
2461         assert(test[4] == 0x6Au);
2462     }
2463 
2464     // Test that mutating a large value does not mutate an external reference.
2465     @system
2466     unittest
2467     {
2468         ubyte[] test;
2469         test.length = 10000u;
2470         Element el = new Element();
2471         el.videotexString = test;
2472         el.value[4] = 0x88u;
2473         assert(test[4] == 0x00u);
2474     }
2475 
2476     ///
2477     public alias ia5String = internationalAlphabetNumber5String;
2478     /// Decodes a string of ASCII characters
2479     abstract public @property
2480     string internationalAlphabetNumber5String() const;
2481 
2482     /// Encodes a string of ASCII characters
2483     abstract public @property
2484     void internationalAlphabetNumber5String(in string value);
2485 
2486     @system
2487     unittest
2488     {
2489         Element el = new Element();
2490         el.ia5String = "";
2491         assert(el.ia5String == "");
2492         el.ia5String = "Nitro dubs & T-Rix";
2493         assert(el.ia5String == "Nitro dubs & T-Rix");
2494         assertThrown!ASN1ValueCharactersException(el.ia5String = "Nitro dubs \xD7 T-Rix");
2495 
2496         // Assert that accessor does not mutate state
2497         assert(el.ia5String == el.ia5String);
2498     }
2499 
2500     // Test that mutating the value does not mutate an external reference.
2501     @system
2502     unittest
2503     {
2504         string test = "HENLO";
2505         Element el = new Element();
2506         el.ia5String = test;
2507         el.value[4] = 0x88u;
2508         assert(test[4] == 'O');
2509     }
2510 
2511     ///
2512     public alias utc = coordinatedUniversalTime;
2513     ///
2514     public alias utcTime = coordinatedUniversalTime;
2515     /// Decodes a DateTime
2516     abstract public @property
2517     DateTime coordinatedUniversalTime() const;
2518 
2519     /// Encodes a DateTime
2520     abstract public @property
2521     void coordinatedUniversalTime(in DateTime value);
2522 
2523     @system
2524     unittest
2525     {
2526         Element el = new Element();
2527         el.utcTime = DateTime(2017, 10, 3);
2528         assert(el.utcTime == DateTime(2017, 10, 3));
2529 
2530         // Assert that accessor does not mutate state
2531         assert(el.utcTime == el.utcTime);
2532     }
2533 
2534     /// Decodes a DateTime
2535     abstract public @property
2536     DateTime generalizedTime() const;
2537 
2538     /// Encodes a DateTime
2539     abstract public @property
2540     void generalizedTime(in DateTime value);
2541 
2542     @system
2543     unittest
2544     {
2545         Element el = new Element();
2546         el.generalizedTime = DateTime(2017, 10, 3);
2547         assert(el.generalizedTime == DateTime(2017, 10, 3));
2548 
2549         // Assert that accessor does not mutate state
2550         assert(el.generalizedTime == el.generalizedTime);
2551     }
2552 
2553     /**
2554         Decodes an ASCII string that contains only characters between and
2555         including 0x20 and 0x75.
2556 
2557         Sources:
2558             $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
2559             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022)
2560             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
2561 
2562     */
2563     deprecated
2564     abstract public @property
2565     string graphicString() const;
2566 
2567     /**
2568         Encodes an ASCII string that contains only characters between and
2569         including 0x20 and 0x75.
2570 
2571         Sources:
2572             $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
2573             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022)
2574             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
2575 
2576     */
2577     deprecated
2578     abstract public @property
2579     void graphicString(in string value);
2580 
2581     @system
2582     unittest
2583     {
2584         Element el = new Element();
2585         el.graphicString = "";
2586         assert(el.graphicString == "");
2587         el.graphicString = "Nitro dubs & T-Rix";
2588         assert(el.graphicString == "Nitro dubs & T-Rix");
2589         el.graphicString = " ";
2590         assert(el.graphicString == " ");
2591         assertThrown!ASN1ValueCharactersException(el.graphicString = "\xD7");
2592         assertThrown!ASN1ValueCharactersException(el.graphicString = "\t");
2593         assertThrown!ASN1ValueCharactersException(el.graphicString = "\r");
2594         assertThrown!ASN1ValueCharactersException(el.graphicString = "\n");
2595         assertThrown!ASN1ValueCharactersException(el.graphicString = "\b");
2596         assertThrown!ASN1ValueCharactersException(el.graphicString = "\v");
2597         assertThrown!ASN1ValueCharactersException(el.graphicString = "\f");
2598         assertThrown!ASN1ValueCharactersException(el.graphicString = "\0");
2599 
2600         // Assert that accessor does not mutate state
2601         assert(el.graphicString == el.graphicString);
2602     }
2603 
2604     // Test that mutating the value does not mutate an external reference.
2605     @system
2606     unittest
2607     {
2608         string test = "HENLO";
2609         Element el = new Element();
2610         el.graphicString = test;
2611         el.value[4] = 0x88u;
2612         assert(test[4] == 'O');
2613     }
2614 
2615     ///
2616     public alias iso646String = visibleString;
2617     /**
2618         Decodes a string that only contains characters between and including
2619         0x20 and 0x7E. (Honestly, I don't know how this differs from
2620         GraphicalString.)
2621     */
2622     abstract public @property
2623     string visibleString() const;
2624 
2625     /**
2626         Encodes a string that only contains characters between and including
2627         0x20 and 0x7E. (Honestly, I don't know how this differs from
2628         GraphicalString.)
2629     */
2630     abstract public @property
2631     void visibleString(in string value);
2632 
2633     @system
2634     unittest
2635     {
2636         Element el = new Element();
2637         el.visibleString = "";
2638         assert(el.visibleString == "");
2639         el.visibleString = "hey hey";
2640         assert(el.visibleString == "hey hey");
2641         el.visibleString = " ";
2642         assert(el.visibleString == " ");
2643         assertThrown!ASN1ValueCharactersException(el.visibleString = "\xD7");
2644         assertThrown!ASN1ValueCharactersException(el.visibleString = "\t");
2645         assertThrown!ASN1ValueCharactersException(el.visibleString = "\r");
2646         assertThrown!ASN1ValueCharactersException(el.visibleString = "\n");
2647         assertThrown!ASN1ValueCharactersException(el.visibleString = "\b");
2648         assertThrown!ASN1ValueCharactersException(el.visibleString = "\v");
2649         assertThrown!ASN1ValueCharactersException(el.visibleString = "\f");
2650         assertThrown!ASN1ValueCharactersException(el.visibleString = "\0");
2651 
2652         // Assert that accessor does not mutate state
2653         assert(el.visibleString == el.visibleString);
2654     }
2655 
2656     // Test that mutating the value does not mutate an external reference.
2657     @system
2658     unittest
2659     {
2660         string test = "HENLO";
2661         Element el = new Element();
2662         el.visibleString = test;
2663         el.value[4] = 0x88u;
2664         assert(test[4] == 'O');
2665     }
2666 
2667     /// Decodes a string containing only ASCII characters.
2668     deprecated
2669     abstract public @property
2670     string generalString();
2671 
2672     /// Encodes a string containing only ASCII characters.
2673     deprecated
2674     abstract public @property
2675     void generalString(in string value);
2676 
2677     @system
2678     unittest
2679     {
2680         Element el = new Element();
2681         el.generalString = "";
2682         assert(el.generalString == "");
2683         el.generalString = "foin-ass sweatpants from BUCCI \0\n\t\b\v\r\f";
2684         assert(el.generalString == "foin-ass sweatpants from BUCCI \0\n\t\b\v\r\f");
2685         assertThrown!ASN1ValueCharactersException(el.generalString = "\xF5");
2686 
2687         // Assert that accessor does not mutate state
2688         assert(el.generalString == el.generalString);
2689     }
2690 
2691     // Test that mutating the value does not mutate an external reference.
2692     @system
2693     unittest
2694     {
2695         string test = "HENLO";
2696         Element el = new Element();
2697         el.generalString = test;
2698         el.value[4] = 0x88u;
2699         assert(test[4] == 'O');
2700     }
2701 
2702     /// Decodes a string of UTF-32 characters
2703     abstract public @property
2704     dstring universalString() const;
2705 
2706     /// Encodes a string of UTF-32 characters
2707     abstract public @property
2708     void universalString(in dstring value);
2709 
2710     @system
2711     unittest
2712     {
2713         Element el = new Element();
2714         el.universalString = ""d;
2715         assert(el.universalString == ""d);
2716         el.universalString = "abcd"d;
2717         assert(el.universalString == "abcd"d);
2718 
2719         // Assert that accessor does not mutate state
2720         assert(el.universalString == el.universalString);
2721     }
2722 
2723     // Test that mutating the value does not mutate an external reference.
2724     @system
2725     unittest
2726     {
2727         dstring test = "HENLO"d;
2728         Element el = new Element();
2729         el.universalString = test;
2730         el.value[4] = 0x88u;
2731         assert(test[4] == 'O');
2732     }
2733 
2734     /**
2735         Decodes a $(MONO CHARACTER STRING), which is a constructed data type, defined
2736         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
2737         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2738 
2739         The specification defines $(MONO CHARACTER STRING) as:
2740 
2741         $(PRE
2742             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
2743                 identification CHOICE {
2744                     syntaxes SEQUENCE {
2745                         abstract OBJECT IDENTIFIER,
2746                         transfer OBJECT IDENTIFIER },
2747                     syntax OBJECT IDENTIFIER,
2748                     presentation-context-id INTEGER,
2749                     context-negotiation SEQUENCE {
2750                         presentation-context-id INTEGER,
2751                         transfer-syntax OBJECT IDENTIFIER },
2752                     transfer-syntax OBJECT IDENTIFIER,
2753                     fixed NULL },
2754                 string-value OCTET STRING }
2755         )
2756 
2757         This assumes AUTOMATIC TAGS, so all of the identification choices
2758         will be context-specific and numbered from 0 to 5.
2759     */
2760     abstract public @property
2761     CharacterString characterString() const;
2762 
2763     /**
2764         Encodes a $(MONO CHARACTER STRING), which is a constructed data type, defined
2765         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
2766         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2767 
2768         The specification defines $(MONO CHARACTER STRING) as:
2769 
2770         $(PRE
2771             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
2772                 identification CHOICE {
2773                     syntaxes SEQUENCE {
2774                         abstract OBJECT IDENTIFIER,
2775                         transfer OBJECT IDENTIFIER },
2776                     syntax OBJECT IDENTIFIER,
2777                     presentation-context-id INTEGER,
2778                     context-negotiation SEQUENCE {
2779                         presentation-context-id INTEGER,
2780                         transfer-syntax OBJECT IDENTIFIER },
2781                     transfer-syntax OBJECT IDENTIFIER,
2782                     fixed NULL },
2783                 string-value OCTET STRING }
2784         )
2785 
2786         This assumes AUTOMATIC TAGS, so all of the identification choices
2787         will be context-specific and numbered from 0 to 5.
2788     */
2789     abstract public @property
2790     void characterString(in CharacterString value);
2791 
2792     @system
2793     unittest
2794     {
2795         ASN1Syntaxes syn = ASN1Syntaxes();
2796         syn.abstractSyntax = new OID(1, 3, 6, 4, 1, 256, 7);
2797         syn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 8);
2798 
2799         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2800         id.syntaxes = syn;
2801 
2802         CharacterString input = CharacterString();
2803         input.identification = id;
2804         input.stringValue = [ 'H', 'E', 'L', 'N', 'O' ];
2805 
2806         Element el = new Element();
2807         el.characterString = input;
2808 
2809         CharacterString output = el.characterString;
2810         assert(output.identification.syntaxes.abstractSyntax == new OID(1, 3, 6, 4, 1, 256, 7));
2811         assert(output.identification.syntaxes.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 8));
2812         assert(output.stringValue == [ 'H', 'E', 'L', 'N', 'O' ]);
2813     }
2814 
2815     @system
2816     unittest
2817     {
2818         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2819         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
2820 
2821         CharacterString input = CharacterString();
2822         input.identification = id;
2823         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2824 
2825         Element el = new Element();
2826         el.characterString = input;
2827         CharacterString output = el.characterString;
2828         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
2829         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2830     }
2831 
2832     @system
2833     unittest
2834     {
2835         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2836         id.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
2837 
2838         CharacterString input = CharacterString();
2839         input.identification = id;
2840         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2841 
2842         Element el = new Element();
2843         el.characterString = input;
2844         CharacterString output = el.characterString;
2845         assert(output.identification.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 39));
2846         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2847     }
2848 
2849     @system
2850     unittest
2851     {
2852         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2853         id.fixed = true;
2854 
2855         CharacterString input = CharacterString();
2856         input.identification = id;
2857         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2858 
2859         Element el = new Element();
2860         el.characterString = input;
2861         CharacterString output = el.characterString;
2862         assert(output.identification.fixed == true);
2863         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2864 
2865         // Assert that accessor does not mutate state
2866         assert(el.characterString == el.characterString);
2867     }
2868 
2869     ///
2870     public alias bmpString = basicMultilingualPlaneString;
2871     /// Decodes a string of UTF-16 characters
2872     abstract public @property
2873     wstring basicMultilingualPlaneString() const;
2874 
2875     /// Encodes a string of UTF-16 characters
2876     abstract public @property
2877     void basicMultilingualPlaneString(in wstring value);
2878 
2879     @system
2880     unittest
2881     {
2882         Element el = new Element();
2883         el.bmpString = ""w;
2884         assert(el.bmpString == ""w);
2885         el.bmpString = "abcd"w;
2886         assert(el.bmpString == "abcd"w);
2887 
2888         // Assert that accessor does not mutate state
2889         assert(el.bmpString == el.bmpString);
2890     }
2891 
2892     // Test that mutating the value does not mutate an external reference.
2893     @system
2894     unittest
2895     {
2896         wstring test = "HENLO"w;
2897         Element el = new Element();
2898         el.bmpString = test;
2899         el.value[4] = 0x88u;
2900         assert(test[4] == 'O');
2901     }
2902 
2903 }