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 }