1 module hunt.security.util.DerValue; 2 3 import hunt.security.util.DerInputBuffer; 4 import hunt.security.util.DerOutputStream; 5 // import hunt.security.util.DerInputStream; 6 7 import hunt.stream.Common; 8 import hunt.Exceptions; 9 10 import std.conv; 11 import std.bigint; 12 import std.bitmanip; 13 import std.datetime; 14 15 16 /** 17 * Represents a single DER-encoded value. DER encoding rules are a subset 18 * of the "Basic" Encoding Rules (BER), but they only support a single way 19 * ("Definite" encoding) to encode any given value. 20 * 21 * <P>All DER-encoded data are triples <em>{type, length, data}</em>. This 22 * class represents such tagged values as they have been read (or constructed), 23 * and provides structured access to the encoded data. 24 * 25 * <P>At this time, this class supports only a subset of the types of DER 26 * data encodings which are defined. That subset is sufficient for parsing 27 * most X.509 certificates, and working with selected additional formats 28 * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data). 29 * 30 * A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3 31 * and RFC 3280, section 4.1.2.4., we assume that this kind of string will 32 * contain ISO-8859-1 characters only. 33 * 34 * 35 * @author David Brownell 36 * @author Amit Kapoor 37 * @author Hemma Prafullchandra 38 */ 39 class DerValue { 40 /** The tag class types */ 41 enum byte TAG_UNIVERSAL = cast(byte)0x000; 42 enum byte TAG_APPLICATION = cast(byte)0x040; 43 enum byte TAG_CONTEXT = cast(byte)0x080; 44 enum byte TAG_PRIVATE = cast(byte)0x0c0; 45 46 /** The DER tag of the value; one of the tag_ constants. */ 47 byte tag; 48 49 protected DerInputBuffer buffer; 50 51 /** 52 * The DER-encoded data of the value, never null 53 */ 54 // DerInputStream data; 55 56 private int _length; 57 58 /* 59 * The type starts at the first byte of the encoding, and 60 * is one of these tag_* values. That may be all the type 61 * data that is needed. 62 */ 63 64 /* 65 * These tags are the "universal" tags ... they mean the same 66 * in all contexts. (Mask with 0x1f -- five bits.) 67 */ 68 69 /** Tag value indicating an ASN.1 "BOOLEAN" value. */ 70 enum byte tag_Boolean = 0x01; 71 72 /** Tag value indicating an ASN.1 "INTEGER" value. */ 73 enum byte tag_Integer = 0x02; 74 75 /** Tag value indicating an ASN.1 "BIT STRING" value. */ 76 enum byte tag_BitString = 0x03; 77 78 /** Tag value indicating an ASN.1 "OCTET STRING" value. */ 79 enum byte tag_OctetString = 0x04; 80 81 /** Tag value indicating an ASN.1 "NULL" value. */ 82 enum byte tag_Null = 0x05; 83 84 /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */ 85 enum byte tag_ObjectId = 0x06; 86 87 /** Tag value including an ASN.1 "ENUMERATED" value */ 88 enum byte tag_Enumerated = 0x0A; 89 90 /** Tag value indicating an ASN.1 "UTF8String" value. */ 91 enum byte tag_UTF8String = 0x0C; 92 93 /** Tag value including a "printable" string */ 94 enum byte tag_PrintableString = 0x13; 95 96 /** Tag value including a "teletype" string */ 97 enum byte tag_T61String = 0x14; 98 99 /** Tag value including an ASCII string */ 100 enum byte tag_IA5String = 0x16; 101 102 /** Tag value indicating an ASN.1 "UTCTime" value. */ 103 enum byte tag_UtcTime = 0x17; 104 105 /** Tag value indicating an ASN.1 "GeneralizedTime" value. */ 106 enum byte tag_GeneralizedTime = 0x18; 107 108 /** Tag value indicating an ASN.1 "GenerallString" value. */ 109 enum byte tag_GeneralString = 0x1B; 110 111 /** Tag value indicating an ASN.1 "UniversalString" value. */ 112 enum byte tag_UniversalString = 0x1C; 113 114 /** Tag value indicating an ASN.1 "BMPString" value. */ 115 enum byte tag_BMPString = 0x1E; 116 117 // CONSTRUCTED seq/set 118 119 /** 120 * Tag value indicating an ASN.1 121 * "SEQUENCE" (zero to N elements, order is significant). 122 */ 123 enum byte tag_Sequence = 0x30; 124 125 /** 126 * Tag value indicating an ASN.1 127 * "SEQUENCE OF" (one to N elements, order is significant). 128 */ 129 enum byte tag_SequenceOf = 0x30; 130 131 /** 132 * Tag value indicating an ASN.1 133 * "SET" (zero to N members, order does not matter). 134 */ 135 enum byte tag_Set = 0x31; 136 137 /** 138 * Tag value indicating an ASN.1 139 * "SET OF" (one to N members, order does not matter). 140 */ 141 enum byte tag_SetOf = 0x31; 142 143 /* 144 * These values are the high order bits for the other kinds of tags. 145 */ 146 147 /** 148 * Returns true if the tag class is UNIVERSAL. 149 */ 150 bool isUniversal() { return ((tag & 0x0c0) == 0x000); } 151 152 /** 153 * Returns true if the tag class is APPLICATION. 154 */ 155 bool isApplication() { return ((tag & 0x0c0) == 0x040); } 156 157 /** 158 * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag. 159 * This is associated with the ASN.1 "DEFINED BY" syntax. 160 */ 161 bool isContextSpecific() { return ((tag & 0x0c0) == 0x080); } 162 163 /** 164 * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag. 165 */ 166 bool isContextSpecific(byte cntxtTag) { 167 if (!isContextSpecific()) { 168 return false; 169 } 170 return ((tag & 0x01f) == cntxtTag); 171 } 172 173 bool isPrivate() { return ((tag & 0x0c0) == 0x0c0); } 174 175 /** Returns true iff the CONSTRUCTED bit is set in the type tag. */ 176 bool isConstructed() { return ((tag & 0x020) == 0x020); } 177 178 /** 179 * Returns true iff the CONSTRUCTED TAG matches the passed tag. 180 */ 181 bool isConstructed(byte constructedTag) { 182 if (!isConstructed()) { 183 return false; 184 } 185 return ((tag & 0x01f) == constructedTag); 186 } 187 188 /** 189 * Creates a PrintableString or UTF8string DER value from a string 190 */ 191 this(string value) { 192 bool isPrintableString = true; 193 for (size_t i = 0; i < value.length; i++) { 194 if (!isPrintableStringChar(value[i])) { 195 isPrintableString = false; 196 break; 197 } 198 } 199 200 // data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value); 201 } 202 203 /** 204 * Creates a string type DER value from a string object 205 * @param stringTag the tag for the DER value to create 206 * @param value the string object to use for the DER value 207 */ 208 this(byte stringTag, string value) { 209 // data = init(stringTag, value); 210 } 211 212 // Creates a DerValue from a tag and some DER-encoded data w/ additional 213 // arg to control whether DER checks are enforced. 214 this(byte tag, byte[] data, bool allowBER) { 215 this.tag = tag; 216 // buffer = new DerInputBuffer(data.clone(), allowBER); 217 _length = cast(int)data.length; 218 // this.data = new DerInputStream(buffer); 219 // this.data.mark(int.max); 220 } 221 222 /** 223 * Creates a DerValue from a tag and some DER-encoded data. 224 * 225 * @param tag the DER type tag 226 * @param data the DER-encoded data 227 */ 228 this(byte tag, byte[] data) { 229 this(tag, data, true); 230 } 231 232 /* 233 * package private 234 */ 235 // this(DerInputBuffer ib) { 236 237 // // XXX must also parse BER-encoded constructed 238 // // values such as sequences, sets... 239 // tag = cast(byte)ib.read(); 240 // byte lenByte = cast(byte)ib.read(); 241 // length = DerInputStream.getLength(lenByte, ib); 242 // if (length == -1) { // indefinite length encoding found 243 // DerInputBuffer inbuf = ib.dup(); 244 // int readLen = inbuf.available(); 245 // int offset = 2; // for tag and length bytes 246 // byte[] indefData = new byte[readLen + offset]; 247 // indefData[0] = tag; 248 // indefData[1] = lenByte; 249 // DataInputStream dis = new DataInputStream(inbuf); 250 // dis.readFully(indefData, offset, readLen); 251 // dis.close(); 252 // DerIndefLenConverter derIn = new DerIndefLenConverter(); 253 // inbuf = new DerInputBuffer(derIn.convert(indefData), ib.allowBER); 254 // if (tag != inbuf.read()) 255 // throw new IOException 256 // ("Indefinite length encoding not supported"); 257 // length = DerInputStream.getLength(inbuf); 258 // buffer = inbuf.dup(); 259 // buffer.truncate(length); 260 // data = new DerInputStream(buffer); 261 // // indefinite form is encoded by sending a length field with a 262 // // length of 0. - i.e. [1000|0000]. 263 // // the object is ended by sending two zero bytes. 264 // ib.skip(length + offset); 265 // } else { 266 267 // buffer = ib.dup(); 268 // buffer.truncate(length); 269 // data = new DerInputStream(buffer); 270 271 // ib.skip(length); 272 // } 273 // } 274 275 // Get an ASN.1/DER encoded datum from a buffer w/ additional 276 // arg to control whether DER checks are enforced. 277 this(byte[] buf, bool allowBER) { 278 implementationMissing(false); 279 // data = init(true, new ByteArrayInputStream(buf), allowBER); 280 } 281 282 /** 283 * Get an ASN.1/DER encoded datum from a buffer. The 284 * entire buffer must hold exactly one datum, including 285 * its tag and length. 286 * 287 * @param buf buffer holding a single DER-encoded datum. 288 */ 289 this(byte[] buf) { 290 this(buf, true); 291 } 292 293 // Get an ASN.1/DER encoded datum from part of a buffer w/ additional 294 // arg to control whether DER checks are enforced. 295 this(byte[] buf, int offset, int len, bool allowBER) 296 { 297 298 implementationMissing(); 299 // data = init(true, new ByteArrayInputStream(buf, offset, len), allowBER); 300 } 301 302 /** 303 * Get an ASN.1/DER encoded datum from part of a buffer. 304 * That part of the buffer must hold exactly one datum, including 305 * its tag and length. 306 * 307 * @param buf the buffer 308 * @param offset start point of the single DER-encoded dataum 309 * @param length how many bytes are in the encoded datum 310 */ 311 this(byte[] buf, int offset, int len) { 312 this(buf, offset, len, true); 313 } 314 315 // Get an ASN1/DER encoded datum from an input stream w/ additional 316 // arg to control whether DER checks are enforced. 317 this(InputStream inputStream, bool allowBER) { 318 // data = init(false, inputStream, allowBER); 319 implementationMissing(); 320 } 321 322 /** 323 * Get an ASN1/DER encoded datum from an input stream. The 324 * stream may have additional data following the encoded datum. 325 * In case of indefinite length encoded datum, the input stream 326 * must hold only one datum. 327 * 328 * @param in the input stream holding a single DER datum, 329 * which may be followed by additional data 330 */ 331 this(InputStream inputStream) { 332 this(inputStream, true); 333 } 334 335 // private DerInputStream init(byte stringTag, string value) 336 // { 337 // string enc = null; 338 339 // tag = stringTag; 340 341 // switch (stringTag) { 342 // case tag_PrintableString: 343 // case tag_IA5String: 344 // case tag_GeneralString: 345 // enc = "ASCII"; 346 // break; 347 // case tag_T61String: 348 // enc = "ISO-8859-1"; 349 // break; 350 // case tag_BMPString: 351 // enc = "UnicodeBigUnmarked"; 352 // break; 353 // case tag_UTF8String: 354 // enc = "UTF8"; 355 // break; 356 // // TBD: Need encoder for UniversalString before it can 357 // // be handled. 358 // default: 359 // throw new IllegalArgumentException("Unsupported DER string type"); 360 // } 361 362 // byte[] buf = value.getBytes(enc); 363 // length = buf.length; 364 // buffer = new DerInputBuffer(buf, true); 365 // DerInputStream result = new DerInputStream(buffer); 366 // result.mark(int.max); 367 // return result; 368 // } 369 370 // /* 371 // * helper routine 372 // */ 373 // private DerInputStream init(bool fullyBuffered, InputStream inputStream, 374 // bool allowBER) { 375 376 // tag = cast(byte)inputStream.read(); 377 // byte lenByte = cast(byte)inputStream.read(); 378 // length = DerInputStream.getLength(lenByte, inputStream); 379 // if (length == -1) { // indefinite length encoding found 380 // int readLen = inputStream.available(); 381 // int offset = 2; // for tag and length bytes 382 // byte[] indefData = new byte[readLen + offset]; 383 // indefData[0] = tag; 384 // indefData[1] = lenByte; 385 // DataInputStream dis = new DataInputStream(inputStream); 386 // dis.readFully(indefData, offset, readLen); 387 // dis.close(); 388 // DerIndefLenConverter derIn = new DerIndefLenConverter(); 389 // inputStream = new ByteArrayInputStream(derIn.convert(indefData)); 390 // if (tag != inputStream.read()) 391 // throw new IOException 392 // ("Indefinite length encoding not supported"); 393 // length = DerInputStream.getLength(inputStream); 394 // } 395 396 // if (fullyBuffered && inputStream.available() != length) 397 // throw new IOException("extra data given to DerValue constructor"); 398 399 // byte[] bytes = IOUtils.readFully(inputStream, length, true); 400 401 // buffer = new DerInputBuffer(bytes, allowBER); 402 // return new DerInputStream(buffer); 403 // } 404 405 // /** 406 // * Encode an ASN1/DER encoded datum onto a DER output stream. 407 // */ 408 // void encode(DerOutputStream ot) 409 // { 410 // ot.write(tag); 411 // ot.putLength(length); 412 // // XXX yeech, excess copies ... DerInputBuffer.write(OutStream) 413 // if (length > 0) { 414 // byte[] value = new byte[length]; 415 // // always synchronized on data 416 // synchronized (data) { 417 // buffer.reset(); 418 // if (buffer.read(value) != length) { 419 // throw new IOException("short DER value read (encode)"); 420 // } 421 // ot.write(value); 422 // } 423 // } 424 // } 425 426 // final DerInputStream getData() { 427 // return data; 428 // } 429 430 final byte getTag() { 431 return tag; 432 } 433 434 /** 435 * Returns an ASN.1 BOOLEAN 436 * 437 * @return the bool held in this DER value 438 */ 439 bool getBoolean() { 440 if (tag != tag_Boolean) { 441 throw new IOException("DerValue.getBoolean, not a BOOLEAN " ~ tag); 442 } 443 if (length != 1) { 444 throw new IOException("DerValue.getBoolean, invalid length " 445 ~ length.to!string()); 446 } 447 // if (buffer.read() != 0) { 448 // return true; 449 // } 450 implementationMissing(); 451 return false; 452 } 453 454 /** 455 * Returns an ASN.1 OBJECT IDENTIFIER. 456 * 457 * @return the OID held in this DER value 458 */ 459 // ObjectIdentifier getOID() { 460 // if (tag != tag_ObjectId) 461 // throw new IOException("DerValue.getOID, not an OID " ~ tag); 462 // return new ObjectIdentifier(buffer); 463 // } 464 465 private byte[] append(byte[] a, byte[] b) { 466 if (a is null) 467 return b; 468 469 // byte[] ret = new byte[a.length + b.length]; 470 // ret = a ~ b; 471 // System.arraycopy(a, 0, ret, 0, a.length); 472 // System.arraycopy(b, 0, ret, a.length, b.length); 473 474 return a ~ b; 475 } 476 477 /** 478 * Returns an ASN.1 OCTET STRING 479 * 480 * @return the octet string held in this DER value 481 */ 482 byte[] getOctetString() { 483 byte[] bytes; 484 485 if (tag != tag_OctetString && !isConstructed(tag_OctetString)) { 486 throw new IOException( 487 "DerValue.getOctetString, not an Octet string: " ~ tag); 488 } 489 bytes = new byte[length]; 490 // Note: do not tempt to call buffer.read(bytes) at all. There's a 491 // known bug that it returns -1 instead of 0. 492 if (length == 0) { 493 return bytes; 494 } 495 // if (buffer.read(bytes) != length) 496 // throw new IOException("short read on DerValue buffer"); 497 // if (isConstructed()) { 498 // DerInputStream inputStream = new DerInputStream(bytes, 0, bytes.length, 499 // buffer.allowBER); 500 // bytes = null; 501 // while (inputStream.available() != 0) { 502 // bytes = append(bytes, inputStream.getOctetString()); 503 // } 504 // } 505 implementationMissing(); 506 return bytes; 507 } 508 509 /** 510 * Returns an ASN.1 INTEGER value as an integer. 511 * 512 * @return the integer held in this DER value. 513 */ 514 int getInteger() { 515 if (tag != tag_Integer) { 516 throw new IOException("DerValue.getInteger, not an int " ~ tag); 517 } 518 implementationMissing(); 519 return 0 ; //buffer.getInteger(data.available()); 520 } 521 522 /** 523 * Returns an ASN.1 INTEGER value as a BigInt. 524 * 525 * @return the integer held in this DER value as a BigInt. 526 */ 527 BigInt getBigInteger() { 528 if (tag != tag_Integer) 529 throw new IOException("DerValue.getBigInteger, not an int " ~ tag); 530 // return buffer.getBigInteger(data.available(), false); 531 implementationMissing(); 532 return BigInt(0); 533 } 534 535 /** 536 * Returns an ASN.1 INTEGER value as a positive BigInt. 537 * This is just to deal with implementations that incorrectly encode 538 * some values as negative. 539 * 540 * @return the integer held in this DER value as a BigInt. 541 */ 542 BigInt getPositiveBigInteger() { 543 if (tag != tag_Integer) 544 throw new IOException("DerValue.getBigInteger, not an int " ~ tag); 545 // return buffer.getBigInteger(data.available(), true); 546 implementationMissing(); 547 return BigInt(0); 548 } 549 550 /** 551 * Returns an ASN.1 ENUMERATED value. 552 * 553 * @return the integer held in this DER value. 554 */ 555 // int getEnumerated() { 556 // if (tag != tag_Enumerated) { 557 // throw new IOException("DerValue.getEnumerated, incorrect tag: " 558 // + tag); 559 // } 560 // return buffer.getInteger(data.available()); 561 // } 562 563 /** 564 * Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned. 565 * 566 * @return the bit string held in this value 567 */ 568 // byte[] getBitString() { 569 // if (tag != tag_BitString) 570 // throw new IOException( 571 // "DerValue.getBitString, not a bit string " ~ tag); 572 573 // return buffer.getBitString(); 574 // } 575 576 /** 577 * Returns an ASN.1 BIT STRING value that need not be byte-aligned. 578 * 579 * @return a BitArray representing the bit string held in this value 580 */ 581 BitArray getUnalignedBitString() { 582 // if (tag != tag_BitString) 583 // throw new IOException( 584 // "DerValue.getBitString, not a bit string " ~ tag); 585 586 // return buffer.getUnalignedBitString(); 587 implementationMissing(); 588 return BitArray.init; 589 } 590 591 /** 592 * Returns the name component as a Java string, regardless of its 593 * encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8). 594 */ 595 // TBD: Need encoder for UniversalString before it can be handled. 596 string getAsString() { 597 if (tag == tag_UTF8String) 598 return getUTF8String(); 599 else if (tag == tag_PrintableString) 600 return getPrintableString(); 601 else if (tag == tag_T61String) 602 return getT61String(); 603 else if (tag == tag_IA5String) 604 return getIA5String(); 605 /* 606 else if (tag == tag_UniversalString) 607 return getUniversalString(); 608 */ 609 else if (tag == tag_BMPString) 610 return getBMPString(); 611 else if (tag == tag_GeneralString) 612 return getGeneralString(); 613 else 614 return null; 615 } 616 617 /** 618 * Returns an ASN.1 BIT STRING value, with the tag assumed implicit 619 * based on the parameter. The bit string must be byte-aligned. 620 * 621 * @params tagImplicit if true, the tag is assumed implicit. 622 * @return the bit string held in this value 623 */ 624 // byte[] getBitString(bool tagImplicit) { 625 // if (!tagImplicit) { 626 // if (tag != tag_BitString) 627 // throw new IOException("DerValue.getBitString, not a bit string " 628 // + tag); 629 // } 630 // return buffer.getBitString(); 631 // } 632 633 /** 634 * Returns an ASN.1 BIT STRING value, with the tag assumed implicit 635 * based on the parameter. The bit string need not be byte-aligned. 636 * 637 * @params tagImplicit if true, the tag is assumed implicit. 638 * @return the bit string held in this value 639 */ 640 BitArray getUnalignedBitString(bool tagImplicit) 641 { 642 if (!tagImplicit) { 643 if (tag != tag_BitString) 644 throw new IOException("DerValue.getBitString, not a bit string " 645 ~ tag.to!string()); 646 } 647 return buffer.getUnalignedBitString(); 648 } 649 650 /** 651 * Helper routine to return all the bytes contained in the 652 * DerInputStream associated with this object. 653 */ 654 byte[] getDataBytes() { 655 byte[] retVal = new byte[length]; 656 // synchronized (data) { 657 // data.reset(); 658 // data.getBytes(retVal); 659 // } 660 implementationMissing(); 661 return retVal; 662 } 663 664 /** 665 * Returns an ASN.1 STRING value 666 * 667 * @return the printable string held in this value 668 */ 669 string getPrintableString() 670 { 671 if (tag != tag_PrintableString) 672 throw new IOException( 673 "DerValue.getPrintableString, not a string " ~ tag); 674 675 return cast(string)getDataBytes(); 676 } 677 678 /** 679 * Returns an ASN.1 T61 (Teletype) STRING value 680 * 681 * @return the teletype string held in this value 682 */ 683 string getT61String() { 684 if (tag != tag_T61String) 685 throw new IOException( 686 "DerValue.getT61String, not T61 " ~ tag); 687 688 return cast(string)getDataBytes(); 689 } 690 691 /** 692 * Returns an ASN.1 IA5 (ASCII) STRING value 693 * 694 * @return the ASCII string held in this value 695 */ 696 string getIA5String() { 697 if (tag != tag_IA5String) 698 throw new IOException( 699 "DerValue.getIA5String, not IA5 " ~ tag); 700 701 return cast(string)getDataBytes(); 702 } 703 704 /** 705 * Returns the ASN.1 BMP (Unicode) STRING value as a Java string. 706 * 707 * @return a string corresponding to the encoded BMPString held in 708 * this value 709 */ 710 string getBMPString() { 711 if (tag != tag_BMPString) 712 throw new IOException( 713 "DerValue.getBMPString, not BMP " ~ tag); 714 715 // BMPString is the same as Unicode in big endian, unmarked 716 // format. 717 return cast(string)getDataBytes(); 718 } 719 720 /** 721 * Returns the ASN.1 UTF-8 STRING value as a Java string. 722 * 723 * @return a string corresponding to the encoded UTF8String held in 724 * this value 725 */ 726 string getUTF8String() { 727 if (tag != tag_UTF8String) 728 throw new IOException( 729 "DerValue.getUTF8String, not UTF-8 " ~ tag); 730 731 return cast(string)getDataBytes(); 732 } 733 734 /** 735 * Returns the ASN.1 GENERAL STRING value as a Java string. 736 * 737 * @return a string corresponding to the encoded GeneralString held in 738 * this value 739 */ 740 string getGeneralString() { 741 if (tag != tag_GeneralString) 742 throw new IOException( 743 "DerValue.getGeneralString, not GeneralString " ~ tag); 744 745 return cast(string)getDataBytes(); 746 } 747 748 /** 749 * Returns a Date if the DerValue is UtcTime. 750 * 751 * @return the Date held in this DER value 752 */ 753 // Date getUTCTime() { 754 // if (tag != tag_UtcTime) { 755 // throw new IOException("DerValue.getUTCTime, not a UtcTime: " ~ tag); 756 // } 757 // return buffer.getUTCTime(data.available()); 758 // } 759 760 /** 761 * Returns a Date if the DerValue is GeneralizedTime. 762 * 763 * @return the Date held in this DER value 764 */ 765 // Date getGeneralizedTime() { 766 // if (tag != tag_GeneralizedTime) { 767 // throw new IOException( 768 // "DerValue.getGeneralizedTime, not a GeneralizedTime: " ~ tag); 769 // } 770 // return buffer.getGeneralizedTime(data.available()); 771 // } 772 773 /** 774 * Returns true iff the other object is a DER value which 775 * is bitwise equal to this one. 776 * 777 * @param other the object being compared with this one 778 */ 779 override 780 bool opEquals(Object other) { 781 if (typeid(other) == typeid(DerValue)) 782 return opEquals(cast(DerValue)other); 783 else 784 return false; 785 } 786 787 /** 788 * Bitwise equality comparison. DER encoded values have a single 789 * encoding, so that bitwise equality of the encoded values is an 790 * efficient way to establish equivalence of the unencoded values. 791 * 792 * @param other the object being compared with this one 793 */ 794 bool opEquals(DerValue other) { 795 if (this is other) { 796 return true; 797 } 798 if (tag != other.tag) { 799 return false; 800 } 801 implementationMissing(); 802 return false; 803 // if (data == other.data) { 804 // return true; 805 // } 806 807 // // make sure the order of lock is always consistent to avoid a deadlock 808 // return hashOf(this.data) 809 // > hashOf(other.data) ? 810 // doEquals(this, other): 811 // doEquals(other, this); 812 } 813 814 /** 815 * Helper for method equals() 816 */ 817 private static bool doEquals(DerValue d1, DerValue d2) { 818 // synchronized (d1.data) { 819 // synchronized (d2.data) { 820 // d1.data.reset(); 821 // d2.data.reset(); 822 // return d1.buffer.equals(d2.buffer); 823 // } 824 // } 825 implementationMissing(); 826 return false; 827 } 828 829 /** 830 * Returns a printable representation of the value. 831 * 832 * @return printable representation of the value 833 */ 834 override string toString() { 835 try { 836 837 string str = getAsString(); 838 if (str !is null) 839 return "\"" ~ str ~ "\""; 840 if (tag == tag_Null) 841 return "[DerValue, null]"; 842 // if (tag == tag_ObjectId) 843 // return "OID." ~ getOID(); 844 845 // integers 846 else 847 return "[DerValue, tag = " ~ tag 848 ~ ", length = " ~ _length.to!string() ~ "]"; 849 } catch (IOException e) { 850 throw new IllegalArgumentException("misformatted DER value"); 851 } 852 } 853 854 /** 855 * Returns a DER-encoded value, such that if it's passed to the 856 * DerValue constructor, a value equivalent to "this" is returned. 857 * 858 * @return DER-encoded value, including tag and length. 859 */ 860 byte[] toByteArray() { 861 DerOutputStream ot = new DerOutputStream(); 862 implementationMissing(); 863 // encode(ot); 864 // data.reset(); 865 return ot.toByteArray(); 866 } 867 868 /** 869 * For "set" and "sequence" types, this function may be used 870 * to return a DER stream of the members of the set or sequence. 871 * This operation is not supported for primitive types such as 872 * integers or bit strings. 873 */ 874 // DerInputStream toDerInputStream() { 875 // if (tag == tag_Sequence || tag == tag_Set) 876 // return new DerInputStream(buffer); 877 // throw new IOException("toDerInputStream rejects tag type " ~ tag); 878 // } 879 880 /** 881 * Get the length of the encoded value. 882 */ 883 int length() { 884 return _length; 885 } 886 887 /** 888 * Determine if a character is one of the permissible characters for 889 * PrintableString: 890 * A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses, 891 * plus sign, comma, hyphen, period, slash, colon, equals sign, 892 * and question mark. 893 * 894 * Characters that are *not* allowed in PrintableString include 895 * exclamation point, quotation mark, number sign, dollar sign, 896 * percent sign, ampersand, asterisk, semicolon, less than sign, 897 * greater than sign, at sign, left and right square brackets, 898 * backslash, circumflex (94), underscore, back quote (96), 899 * left and right curly brackets, vertical line, tilde, 900 * and the control codes (0-31 and 127). 901 * 902 * This list is based on X.680 (the ASN.1 spec). 903 */ 904 static bool isPrintableStringChar(char ch) { 905 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || 906 (ch >= '0' && ch <= '9')) { 907 return true; 908 } else { 909 switch (ch) { 910 case ' ': /* space */ 911 case '\'': /* apostrophe */ 912 case '(': /* left paren */ 913 case ')': /* right paren */ 914 case '+': /* plus */ 915 case ',': /* comma */ 916 case '-': /* hyphen */ 917 case '.': /* period */ 918 case '/': /* slash */ 919 case ':': /* colon */ 920 case '=': /* equals */ 921 case '?': /* question mark */ 922 return true; 923 default: 924 return false; 925 } 926 } 927 } 928 929 /** 930 * Create the tag of the attribute. 931 * 932 * @params class the tag class type, one of UNIVERSAL, CONTEXT, 933 * APPLICATION or PRIVATE 934 * @params form if true, the value is constructed, otherwise it 935 * is primitive. 936 * @params val the tag value 937 */ 938 static byte createTag(byte tagClass, bool form, byte val) { 939 byte tag = cast(byte)(tagClass | val); 940 if (form) { 941 tag |= cast(byte)0x20; 942 } 943 return (tag); 944 } 945 946 /** 947 * Set the tag of the attribute. Commonly used to reset the 948 * tag value used for IMPLICIT encodings. 949 * 950 * @params tag the tag value 951 */ 952 void resetTag(byte tag) { 953 this.tag = tag; 954 } 955 956 /** 957 * Returns a hashcode for this DerValue. 958 * 959 * @return a hashcode for this DerValue. 960 */ 961 override size_t toHash() @trusted nothrow { 962 try 963 { 964 string s = toString(); 965 return hashOf(toString()); 966 } 967 catch(Exception) 968 { 969 return super.toHash(); 970 } 971 } 972 }