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 static 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 }