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