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 }