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