1 /** 2 * X.509 Certificate Extensions 3 * 4 * Copyright: 5 * (C) 1999-2007,2012 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.cert.x509.x509_ext; 12 13 import botan.constants; 14 static if (BOTAN_HAS_X509_CERTIFICATES): 15 16 import botan.asn1.asn1_obj; 17 import botan.asn1.asn1_oid; 18 import botan.asn1.asn1_str; 19 import botan.utils.datastor.datastor; 20 import botan.cert.x509.crl_ent; 21 import botan.cert.x509.key_constraint; 22 import botan.hash.sha160; 23 import botan.asn1.der_enc; 24 import botan.asn1.ber_dec; 25 import botan.asn1.oids; 26 import botan.asn1.asn1_alt_name; 27 import botan.utils.charset; 28 import botan.utils.bit_ops; 29 import std.algorithm; 30 import botan.utils.types; 31 import botan.utils.mem_ops; 32 import memutils.dictionarylist; 33 34 /** 35 * X.509 Certificate Extension 36 */ 37 interface CertificateExtension 38 { 39 public: 40 /** 41 * Returns: OID representing this extension 42 */ 43 final OID oidOf() const 44 { 45 return OIDS.lookup(oidName()); 46 } 47 48 /** 49 * Make a copy of this extension 50 * Returns: copy of this 51 */ 52 abstract CertificateExtension copy() const; 53 54 /* 55 * Add the contents of this extension into the information 56 * for the subject and/or issuer, as necessary. 57 * 58 * Params: 59 * subject = the subject info 60 * issuer = the issuer info 61 */ 62 abstract void contentsTo(ref DataStore subject, 63 ref DataStore issuer) const; 64 65 /* 66 * Returns: specific OID name 67 */ 68 abstract string oidName() const; 69 70 protected: 71 abstract bool shouldEncode() const; 72 abstract Vector!ubyte encodeInner() const; 73 abstract void decodeInner(const ref Vector!ubyte); 74 } 75 76 alias X509Extensions = RefCounted!X509ExtensionsImpl; 77 78 /** 79 * X.509 Certificate Extension List 80 */ 81 final class X509ExtensionsImpl : ASN1Object 82 { 83 public: 84 85 override void encodeInto(ref DEREncoder to_object) const 86 { 87 foreach (const extension; m_extensions[]) 88 { 89 const CertificateExtension ext = extension.first; 90 const bool is_critical = extension.second; 91 92 const bool should_encode = ext.shouldEncode(); 93 94 if (should_encode) 95 { 96 to_object.startCons(ASN1Tag.SEQUENCE) 97 .encode(ext.oidOf()) 98 .encodeOptional(is_critical, false) 99 .encode(ext.encodeInner(), ASN1Tag.OCTET_STRING) 100 .endCons(); 101 } 102 } 103 } 104 105 override void decodeFrom(ref BERDecoder from_source) 106 { 107 CertificateExtension cext; 108 foreach (Pair!(CertificateExtension, bool) extension; m_extensions[]) { 109 cext = extension.first; 110 destroy(cext); 111 } 112 m_extensions.clear(); 113 114 BERDecoder sequence = from_source.startCons(ASN1Tag.SEQUENCE); 115 116 while (sequence.moreItems()) 117 { 118 OID oid = OID(); 119 Vector!ubyte value; 120 bool critical; 121 122 sequence.startCons(ASN1Tag.SEQUENCE) 123 .decode(oid) 124 .decodeOptional(critical, ASN1Tag.BOOLEAN, ASN1Tag.UNIVERSAL, false) 125 .decode(value, ASN1Tag.OCTET_STRING) 126 .verifyEnd() 127 .endCons(); 128 129 CertificateExtension ext = getExtension(oid); 130 131 if (!ext && critical && m_throw_on_unknown_critical) 132 throw new DecodingError("Encountered unknown X.509 extension marked " 133 ~ "as critical; OID = " ~ oid.toString()); 134 135 if (ext) 136 { 137 try 138 { 139 ext.decodeInner(value); 140 } 141 catch(Exception e) 142 { 143 throw new DecodingError("Exception while decoding extension " ~ 144 oid.toString() ~ ": " ~ e.msg); 145 } 146 147 m_extensions.pushBack(makePair(ext, critical)); 148 } 149 } 150 151 sequence.verifyEnd(); 152 } 153 154 void contentsTo(ref DataStore subject_info, 155 ref DataStore issuer_info) const 156 { 157 foreach (extension; m_extensions[]) 158 extension.first.contentsTo(subject_info, issuer_info); 159 } 160 161 void add(CertificateExtension extn, bool critical = false) 162 { 163 m_extensions.pushBack(makePair(extn, critical)); 164 } 165 166 X509ExtensionsImpl opAssign(in X509Extensions other) 167 { 168 CertificateExtension cext; 169 foreach (extension; m_extensions[]) { 170 cext = extension.first; 171 destroy(cext); 172 } 173 m_extensions.clear(); 174 175 foreach (extension; other.m_extensions[]) 176 m_extensions.pushBack(makePair(extension.first.copy(), extension.second)); 177 178 return this; 179 } 180 181 this(in X509Extensions ext) { 182 this = ext; 183 } 184 185 this(bool st = true) { m_throw_on_unknown_critical = st; } 186 187 ~this() 188 { 189 CertificateExtension cext; 190 foreach (extension; m_extensions[]) { 191 cext = extension.first; 192 destroy(cext); 193 } 194 } 195 196 override string toString() const { 197 import std.array :Appender; 198 Appender!string ret; 199 foreach (ext; m_extensions[]) 200 ret ~= ext.first.oidName() ~ "\n"; 201 return ret.data; 202 } 203 204 private: 205 206 /* 207 * List of X.509 Certificate Extensions 208 */ 209 CertificateExtension getExtension(in OID oid) 210 { 211 enum string X509_EXTENSION(string NAME, string TYPE) = 212 `if (OIDS.nameOf(oid, "` ~ NAME ~ `")) return new ` ~ TYPE ~ `();`; 213 214 mixin( X509_EXTENSION!("X509v3.KeyUsage", "KeyUsage") ); 215 mixin( X509_EXTENSION!("X509v3.BasicConstraints", "BasicConstraints") ); 216 mixin( X509_EXTENSION!("X509v3.SubjectKeyIdentifier", "SubjectKeyID") ); 217 mixin( X509_EXTENSION!("X509v3.AuthorityKeyIdentifier", "AuthorityKeyID") ); 218 mixin( X509_EXTENSION!("X509v3.ExtendedKeyUsage", "ExtendedKeyUsage") ); 219 mixin( X509_EXTENSION!("X509v3.IssuerAlternativeName", "IssuerAlternativeName") ); 220 mixin( X509_EXTENSION!("X509v3.SubjectAlternativeName", "SubjectAlternativeName") ); 221 mixin( X509_EXTENSION!("X509v3.CertificatePolicies", "CertificatePolicies") ); 222 mixin( X509_EXTENSION!("X509v3.CRLDistributionPoints", "CRLDistributionPoints") ); 223 mixin( X509_EXTENSION!("PKIX.AuthorityInformationAccess", "AuthorityInformationAccess") ); 224 mixin( X509_EXTENSION!("X509v3.CRLNumber", "CRLNumber") ); 225 mixin( X509_EXTENSION!("X509v3.ReasonCode", "CRLReasonCode") ); 226 227 return null; 228 } 229 230 231 Vector!( Pair!(CertificateExtension, bool) ) m_extensions; 232 bool m_throw_on_unknown_critical; 233 } 234 235 __gshared immutable size_t NO_CERT_PATH_LIMIT = 0xFFFFFFF0; 236 237 /** 238 * Basic Constraints Extension 239 */ 240 final class BasicConstraints : CertificateExtension 241 { 242 public: 243 override BasicConstraints copy() const 244 { return new BasicConstraints(m_is_ca, m_path_limit); } 245 246 this(bool ca = false, size_t limit = 0) 247 { 248 m_is_ca = ca; 249 m_path_limit = limit; 250 } 251 252 bool getIsCa() const { return m_is_ca; } 253 /* 254 * Checked accessor for the path_limit member 255 */ 256 size_t getPathLimit() const 257 { 258 if (!m_is_ca) 259 throw new InvalidState("Basic_Constraints::get_path_limit: Not a CA"); 260 return m_path_limit; 261 } 262 263 protected: 264 override bool shouldEncode() const { return true; } 265 266 string oidName() const { return "X509v3.BasicConstraints"; } 267 268 /* 269 * Encode the extension 270 */ 271 Vector!ubyte encodeInner() const 272 { 273 auto der_enc = DEREncoder() 274 .startCons(ASN1Tag.SEQUENCE) 275 .encodeIf (m_is_ca, 276 DEREncoder() 277 .encode(m_is_ca) 278 .encodeOptional(m_path_limit, NO_CERT_PATH_LIMIT) 279 ) 280 .endCons(); 281 return der_enc.getContentsUnlocked(); 282 } 283 284 /* 285 * Decode the extension 286 */ 287 void decodeInner(const ref Vector!ubyte input) 288 { 289 BERDecoder(input) 290 .startCons(ASN1Tag.SEQUENCE) 291 .decodeOptional(m_is_ca, ASN1Tag.BOOLEAN, ASN1Tag.UNIVERSAL, false) 292 .decodeOptional(m_path_limit, ASN1Tag.INTEGER, ASN1Tag.UNIVERSAL, NO_CERT_PATH_LIMIT) 293 .verifyEnd() 294 .endCons(); 295 296 if (m_is_ca == false) 297 m_path_limit = 0; 298 } 299 300 /* 301 * Return a textual representation 302 */ 303 void contentsTo(ref DataStore subject, ref DataStore) const 304 { 305 subject.add("X509v3.BasicConstraints.is_ca", (m_is_ca ? 1 : 0)); 306 subject.add("X509v3.BasicConstraints.path_constraint", m_path_limit); 307 } 308 309 bool m_is_ca; 310 size_t m_path_limit; 311 } 312 313 /** 314 * Key Usage Constraints Extension 315 */ 316 final class KeyUsage : CertificateExtension 317 { 318 public: 319 override KeyUsage copy() const { return new KeyUsage(m_constraints); } 320 321 this(KeyConstraints c = KeyConstraints.NO_CONSTRAINTS) { m_constraints = c; } 322 323 KeyConstraints getConstraints() const { return m_constraints; } 324 protected: 325 string oidName() const { return "X509v3.KeyUsage"; } 326 327 bool shouldEncode() const { return (m_constraints != KeyConstraints.NO_CONSTRAINTS); } 328 329 /* 330 * Encode the extension 331 */ 332 Vector!ubyte encodeInner() const 333 { 334 if (m_constraints == KeyConstraints.NO_CONSTRAINTS) 335 throw new EncodingError("Cannot encode zero usage constraints"); 336 337 const size_t unused_bits = lowBit(m_constraints) - 1; 338 339 Vector!ubyte der; 340 der.pushBack(ASN1Tag.BIT_STRING); 341 der.pushBack(2 + ((unused_bits < 8) ? 1 : 0)); 342 der.pushBack(unused_bits % 8); 343 der.pushBack((m_constraints >> 8) & 0xFF); 344 if (m_constraints & 0xFF) 345 der.pushBack(m_constraints & 0xFF); 346 347 return der; 348 } 349 350 /* 351 * Decode the extension 352 */ 353 void decodeInner(const ref Vector!ubyte input) 354 { 355 BERDecoder ber = BERDecoder(input); 356 357 BERObject obj = ber.getNextObject(); 358 359 if (obj.type_tag != ASN1Tag.BIT_STRING || obj.class_tag != ASN1Tag.UNIVERSAL) 360 throw new BERBadTag("Bad tag for usage constraint", 361 obj.type_tag, obj.class_tag); 362 363 if (obj.value.length != 2 && obj.value.length != 3) 364 throw new BERDecodingError("Bad size for BITSTRING in usage constraint"); 365 366 if (obj.value[0] >= 8) 367 throw new BERDecodingError("Invalid unused bits in usage constraint"); 368 369 obj.value[obj.value.length-1] &= (0xFF << obj.value[0]); 370 371 KeyConstraints usage; 372 foreach (size_t i; 1 .. obj.value.length) 373 usage |= cast(KeyConstraints) (obj.value[i] << 8); 374 375 m_constraints = usage; 376 } 377 378 /* 379 * Return a textual representation 380 */ 381 void contentsTo(ref DataStore subject, ref DataStore) const 382 { 383 subject.add("X509v3.KeyUsage", m_constraints); 384 } 385 386 KeyConstraints m_constraints; 387 } 388 389 /** 390 * Subject Key Identifier Extension 391 */ 392 final class SubjectKeyID : CertificateExtension 393 { 394 public: 395 override SubjectKeyID copy() const { return new SubjectKeyID(m_key_id); } 396 397 this() {} 398 this()(auto const ref Vector!ubyte pub_key) 399 { 400 auto hash = scoped!SHA160(); 401 m_key_id = unlock(hash.process(pub_key)); 402 } 403 404 405 ref const(Vector!ubyte) getKeyId() const { return m_key_id; } 406 protected: 407 string oidName() const { return "X509v3.SubjectKeyIdentifier"; } 408 409 bool shouldEncode() const { return (m_key_id.length > 0); } 410 411 /* 412 * Encode the extension 413 */ 414 Vector!ubyte encodeInner() const 415 { 416 return DEREncoder().encode(m_key_id, ASN1Tag.OCTET_STRING).getContentsUnlocked(); 417 } 418 419 /* 420 * Decode the extension 421 */ 422 void decodeInner(const ref Vector!ubyte input) 423 { 424 BERDecoder(input).decode(m_key_id, ASN1Tag.OCTET_STRING).verifyEnd(); 425 } 426 427 /* 428 * Return a textual representation 429 */ 430 void contentsTo(ref DataStore subject, ref DataStore) const 431 { 432 subject.add("X509v3.SubjectKeyIdentifier", m_key_id); 433 } 434 435 Vector!ubyte m_key_id; 436 } 437 438 /** 439 * Authority Key Identifier Extension 440 */ 441 class AuthorityKeyID : CertificateExtension 442 { 443 public: 444 override AuthorityKeyID copy() const { return new AuthorityKeyID(m_key_id); } 445 446 this() {} 447 this()(auto const ref Vector!ubyte k) { m_key_id = k.dup(); } 448 449 ref const(Vector!ubyte) getKeyId() const { return m_key_id; } 450 protected: 451 string oidName() const { return "X509v3.AuthorityKeyIdentifier"; } 452 453 bool shouldEncode() const { return (m_key_id.length > 0); } 454 455 /* 456 * Encode the extension 457 */ 458 Vector!ubyte encodeInner() const 459 { 460 return DEREncoder() 461 .startCons(ASN1Tag.SEQUENCE) 462 .encode(m_key_id, ASN1Tag.OCTET_STRING, (cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC) 463 .endCons() 464 .getContentsUnlocked(); 465 } 466 467 /* 468 * Decode the extension 469 */ 470 void decodeInner(const ref Vector!ubyte input) 471 { 472 BERDecoder(input) 473 .startCons(ASN1Tag.SEQUENCE) 474 .decodeOptionalString(m_key_id, ASN1Tag.OCTET_STRING, 0); 475 } 476 477 /* 478 * Return a textual representation 479 */ 480 void contentsTo(ref DataStore, ref DataStore issuer) const 481 { 482 if (m_key_id.length) 483 issuer.add("X509v3.AuthorityKeyIdentifier", m_key_id); 484 } 485 486 487 Vector!ubyte m_key_id; 488 } 489 490 /** 491 * Alternative Name Extension Base Class 492 */ 493 abstract class AlternativeNameExt : CertificateExtension 494 { 495 public: 496 const(AlternativeName) getAltName() const { return m_alt_name; } 497 498 protected: 499 500 this(AlternativeName alt_name = AlternativeName.init, string oid_name_str = null) 501 { 502 m_alt_name = alt_name; 503 m_oid_name_str = oid_name_str; 504 } 505 506 string oidName() const { return m_oid_name_str; } 507 508 bool shouldEncode() const { return m_alt_name.hasItems(); } 509 510 /* 511 * Encode the extension 512 */ 513 Vector!ubyte encodeInner() const 514 { 515 return DEREncoder().encode(m_alt_name).getContentsUnlocked(); 516 } 517 518 /* 519 * Decode the extension 520 */ 521 void decodeInner(const ref Vector!ubyte input) 522 { 523 BERDecoder(input).decode(m_alt_name); 524 } 525 526 /* 527 * Return a textual representation 528 */ 529 void contentsTo(ref DataStore subject_info, 530 ref DataStore issuer_info) const 531 { 532 DictionaryListRef!(string, string) contents = getAltName().contents(); 533 534 if (m_oid_name_str == "X509v3.SubjectAlternativeName") 535 subject_info.add(contents); 536 else if (m_oid_name_str == "X509v3.IssuerAlternativeName") 537 issuer_info.add(contents); 538 else 539 throw new InternalError("In AlternativeName, unknown type " ~ m_oid_name_str); 540 } 541 542 string m_oid_name_str; 543 AlternativeName m_alt_name; 544 } 545 546 547 548 549 /** 550 * Subject Alternative Name Extension 551 */ 552 final class SubjectAlternativeName : AlternativeNameExt, CertificateExtension 553 { 554 public: 555 556 override void contentsTo(ref DataStore subject, ref DataStore issuer) const { 557 super.contentsTo(subject, issuer); 558 } 559 560 override string oidName() const { 561 return super.oidName(); 562 } 563 564 override bool shouldEncode() const { 565 return super.shouldEncode(); 566 } 567 568 override Vector!ubyte encodeInner() const 569 { 570 return super.encodeInner(); 571 } 572 573 override void decodeInner(const ref Vector!ubyte input) { 574 super.decodeInner(input); 575 } 576 577 override SubjectAlternativeName copy() const 578 { return new SubjectAlternativeName(cast(AlternativeName) getAltName()); } 579 580 this(AlternativeName name = AlternativeName()) { 581 super(name, "X509v3.SubjectAlternativeName"); 582 } 583 } 584 585 /** 586 * Issuer Alternative Name Extension 587 */ 588 final class IssuerAlternativeName : AlternativeNameExt, CertificateExtension 589 { 590 public: 591 override void contentsTo(ref DataStore subject, ref DataStore issuer) const { 592 super.contentsTo(subject, issuer); 593 } 594 595 override string oidName() const { 596 return super.oidName(); 597 } 598 599 override bool shouldEncode() const { 600 return super.shouldEncode(); 601 } 602 603 override Vector!ubyte encodeInner() const 604 { 605 return super.encodeInner(); 606 } 607 608 override void decodeInner(const ref Vector!ubyte input) { 609 super.decodeInner(input); 610 } 611 612 override IssuerAlternativeName copy() const 613 { return new IssuerAlternativeName(cast(AlternativeName)getAltName()); } 614 615 this(AlternativeName name = AlternativeName()) { 616 super(name, "X509v3.IssuerAlternativeName"); 617 } 618 } 619 620 /** 621 * Extended Key Usage Extension 622 */ 623 final class ExtendedKeyUsage : CertificateExtension 624 { 625 public: 626 override ExtendedKeyUsage copy() const { return new ExtendedKeyUsage(m_oids.dup); } 627 628 this() {} 629 630 this()(auto const ref Vector!OID o) 631 { 632 m_oids = o.dup; 633 } 634 635 ref const(Vector!OID) getOids() const { return m_oids; } 636 protected: 637 string oidName() const { return "X509v3.ExtendedKeyUsage"; } 638 639 bool shouldEncode() const { return (m_oids.length > 0); } 640 /* 641 * Encode the extension 642 */ 643 Vector!ubyte encodeInner() const 644 { 645 return DEREncoder() 646 .startCons(ASN1Tag.SEQUENCE) 647 .encodeList(m_oids) 648 .endCons() 649 .getContentsUnlocked(); 650 } 651 652 /* 653 * Decode the extension 654 */ 655 void decodeInner(const ref Vector!ubyte input) 656 { 657 BERDecoder(input).decodeList(m_oids); 658 } 659 660 /* 661 * Return a textual representation 662 */ 663 void contentsTo(ref DataStore subject, ref DataStore) const 664 { 665 foreach (oid; m_oids[]) 666 subject.add("X509v3.ExtendedKeyUsage", oid.toString()); 667 } 668 669 Vector!OID m_oids; 670 } 671 672 /** 673 * Certificate Policies Extension 674 */ 675 final class CertificatePolicies : CertificateExtension 676 { 677 public: 678 override CertificatePolicies copy() const 679 { return new CertificatePolicies(m_oids); } 680 681 this() {} 682 this()(auto const ref Vector!OID o) { m_oids = o.dup(); } 683 684 ref const(Vector!OID) getOids() const { return m_oids; } 685 protected: 686 string oidName() const { return "X509v3.CertificatePolicies"; } 687 688 bool shouldEncode() const { return (m_oids.length > 0); } 689 690 /* 691 * Encode the extension 692 */ 693 Vector!ubyte encodeInner() const 694 { 695 Vector!( PolicyInformation ) policies; 696 697 foreach (oid; m_oids[]) 698 policies.pushBack(PolicyInformation(oid)); 699 700 return DEREncoder() 701 .startCons(ASN1Tag.SEQUENCE) 702 .encodeList(policies) 703 .endCons() 704 .getContentsUnlocked(); 705 } 706 /* 707 * Decode the extension 708 */ 709 void decodeInner(const ref Vector!ubyte input) 710 { 711 Vector!( PolicyInformation ) policies; 712 //logTrace("Decode list of policies"); 713 BERDecoder(input).decodeList(policies); 714 715 m_oids.clear(); 716 foreach (policy; policies[]) 717 m_oids.pushBack(policy.oid); 718 } 719 720 /* 721 * Return a textual representation 722 */ 723 void contentsTo(ref DataStore info, ref DataStore) const 724 { 725 foreach (oid; m_oids[]) 726 info.add("X509v3.CertificatePolicies", oid.toString()); 727 } 728 729 Vector!OID m_oids; 730 } 731 732 final class AuthorityInformationAccess : CertificateExtension 733 { 734 public: 735 override AuthorityInformationAccess copy() const 736 { return new AuthorityInformationAccess(m_ocsp_responder); } 737 738 this() {} 739 740 this(in string ocsp) { m_ocsp_responder = ocsp; } 741 742 protected: 743 string oidName() const { return "PKIX.AuthorityInformationAccess"; } 744 745 bool shouldEncode() const { return (m_ocsp_responder != ""); } 746 747 Vector!ubyte encodeInner() const 748 { 749 ASN1String url = ASN1String(m_ocsp_responder, ASN1Tag.IA5_STRING); 750 751 return DEREncoder() 752 .startCons(ASN1Tag.SEQUENCE) 753 .startCons(ASN1Tag.SEQUENCE) 754 .encode(OIDS.lookup("PKIX.OCSP")) 755 .addObject((cast(ASN1Tag)6), ASN1Tag.CONTEXT_SPECIFIC, url.iso8859()) 756 .endCons() 757 .endCons().getContentsUnlocked(); 758 } 759 760 void decodeInner(const ref Vector!ubyte input) 761 { 762 BERDecoder ber = BERDecoder(input).startCons(ASN1Tag.SEQUENCE); 763 764 while (ber.moreItems()) 765 { 766 OID oid = OID(); 767 768 BERDecoder info = ber.startCons(ASN1Tag.SEQUENCE); 769 770 info.decode(oid); 771 772 if (oid == OIDS.lookup("PKIX.OCSP")) 773 { 774 BERObject name = info.getNextObject(); 775 776 if (name.type_tag == 6 && name.class_tag == ASN1Tag.CONTEXT_SPECIFIC) 777 { 778 m_ocsp_responder = transcode(name.toString(), 779 LATIN1_CHARSET, 780 LOCAL_CHARSET); 781 } 782 783 } 784 } 785 } 786 787 788 789 void contentsTo(ref DataStore subject, ref DataStore) const 790 { 791 if (m_ocsp_responder != "") 792 subject.add("OCSP.responder", m_ocsp_responder); 793 } 794 795 string m_ocsp_responder; 796 } 797 798 799 /** 800 * CRL Number Extension 801 */ 802 final class CRLNumber : CertificateExtension 803 { 804 public: 805 /* 806 * Copy a CRL_Number extension 807 */ 808 override CRLNumber copy() const 809 { 810 if (!m_has_value) 811 throw new InvalidState("CRL_Number::copy: Not set"); 812 return new CRLNumber(m_crl_number); 813 } 814 815 816 this() { m_has_value = false; m_crl_number = 0; } 817 this(size_t n) { m_has_value = true; m_crl_number = n; } 818 819 /* 820 * Checked accessor for the crl_number member 821 */ 822 size_t getCrlNumber() const 823 { 824 if (!m_has_value) 825 throw new InvalidState("CRL_Number::get_crl_number: Not set"); 826 return m_crl_number; 827 } 828 829 protected: 830 string oidName() const { return "X509v3.CRLNumber"; } 831 832 bool shouldEncode() const { return m_has_value; } 833 /* 834 * Encode the extension 835 */ 836 Vector!ubyte encodeInner() const 837 { 838 return DEREncoder().encode(m_crl_number).getContentsUnlocked(); 839 } 840 /* 841 * Decode the extension 842 */ 843 void decodeInner(const ref Vector!ubyte input) 844 { 845 BERDecoder(input).decode(m_crl_number); 846 } 847 848 /* 849 * Return a textual representation 850 */ 851 void contentsTo(ref DataStore info, ref DataStore) const 852 { 853 info.add("X509v3.CRLNumber", m_crl_number); 854 } 855 856 bool m_has_value; 857 size_t m_crl_number; 858 } 859 860 /** 861 * CRL Entry Reason Code Extension 862 */ 863 final class CRLReasonCode : CertificateExtension 864 { 865 public: 866 override CRLReasonCode copy() const { return new CRLReasonCode(m_reason); } 867 868 this(CRLCode r = UNSPECIFIED) { m_reason = r; } 869 870 CRLCode getReason() const { return m_reason; } 871 872 protected: 873 override string oidName() const { return "X509v3.ReasonCode"; } 874 875 override bool shouldEncode() const { return (m_reason != UNSPECIFIED); } 876 /* 877 * Encode the extension 878 */ 879 override Vector!ubyte encodeInner() const 880 { 881 return DEREncoder() 882 .encode(cast(size_t)(m_reason), ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL) 883 .getContentsUnlocked(); 884 } 885 886 /* 887 * Decode the extension 888 */ 889 override void decodeInner(const ref Vector!ubyte input) 890 { 891 size_t reason_code = 0; 892 BERDecoder(input).decode(reason_code, ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL); 893 m_reason = cast(CRLCode)(reason_code); 894 } 895 896 /* 897 * Return a textual representation 898 */ 899 override void contentsTo(ref DataStore info, ref DataStore) const 900 { 901 info.add("X509v3.CRLReasonCode", m_reason); 902 } 903 904 CRLCode m_reason; 905 } 906 907 908 /** 909 * CRL Distribution Points Extension 910 */ 911 final class CRLDistributionPoints : CertificateExtension 912 { 913 public: 914 alias DistributionPoint = RefCounted!DistributionPointImpl; 915 final class DistributionPointImpl : ASN1Object 916 { 917 public: 918 override void encodeInto(ref DEREncoder) const 919 { 920 throw new Exception("CRLDistributionPoints encoding not implemented"); 921 } 922 923 override void decodeFrom(ref BERDecoder ber) 924 { 925 ber.startCons(ASN1Tag.SEQUENCE) 926 .startCons((cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC) 927 .decodeOptionalImplicit(m_point, (cast(ASN1Tag) 0), 928 (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED), 929 ASN1Tag.SEQUENCE, ASN1Tag.CONSTRUCTED) 930 .endCons().endCons(); 931 } 932 933 934 const(AlternativeName) point() const { return m_point; } 935 private: 936 AlternativeName m_point; 937 } 938 939 override CRLDistributionPoints copy() const 940 { return new CRLDistributionPoints(m_distribution_points); } 941 942 this() {} 943 944 this()(auto const ref Vector!( DistributionPoint ) points) { m_distribution_points = points.dup; } 945 946 ref const(Vector!( DistributionPoint )) distributionPoints() const 947 { return m_distribution_points; } 948 949 protected: 950 string oidName() const { return "X509v3.CRLDistributionPoints"; } 951 952 bool shouldEncode() const { return !m_distribution_points.empty; } 953 954 Vector!ubyte encodeInner() const 955 { 956 throw new Exception("CRLDistributionPoints encoding not implemented"); 957 } 958 959 void decodeInner(const ref Vector!ubyte buf) 960 { 961 BERDecoder(buf).decodeList(m_distribution_points).verifyEnd(); 962 } 963 964 965 void contentsTo(ref DataStore info, ref DataStore) const 966 { 967 foreach (distribution_point; m_distribution_points[]) 968 { 969 auto point = distribution_point.point().contents(); 970 971 point.getValuesAt("URI", (in string val) { 972 info.add("CRL.DistributionPoint", val); 973 }); 974 } 975 } 976 977 Vector!( DistributionPoint ) m_distribution_points; 978 } 979 980 981 alias PolicyInformation = RefCounted!PolicyInformationImpl; 982 983 /* 984 * A policy specifier 985 */ 986 final class PolicyInformationImpl : ASN1Object 987 { 988 public: 989 OID oid; 990 991 this() {} 992 this(OID oid_) { oid = oid_; } 993 994 override void encodeInto(ref DEREncoder codec) const 995 { 996 codec.startCons(ASN1Tag.SEQUENCE) 997 .encode(oid) 998 .endCons(); 999 } 1000 1001 override void decodeFrom(ref BERDecoder codec) 1002 { 1003 oid = OID(); 1004 codec.startCons(ASN1Tag.SEQUENCE) 1005 .decode(oid) 1006 .discardRemaining() 1007 .endCons(); 1008 } 1009 }