1 /**
2 * X.509 CRL
3 * 
4 * Copyright:
5 * (C) 1999-2007 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_crl;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_X509_CERTIFICATES):
15 
16 import botan.cert.x509.x509_obj;
17 import botan.cert.x509.crl_ent;
18 import botan.cert.x509.x509_ext;
19 import botan.cert.x509.x509cert;
20 import botan.asn1.x509_dn;
21 import botan.asn1.ber_dec;
22 import botan.utils.parsing;
23 import botan.math.bigint.bigint;
24 import botan.asn1.oids;
25 import botan.asn1.asn1_time;
26 import botan.utils.types;
27 import memutils.utils;
28 import std.datetime;
29 
30 alias X509CRL = RefCounted!X509CRLImpl;
31 
32 /**
33 * This class represents X.509 Certificate Revocation Lists (CRLs).
34 */
35 final class X509CRLImpl : X509Object
36 {
37 public:
38     /**
39     * This class represents CRL related errors.
40     */
41     class X509CRLError : Exception
42     {
43         this(in string error) {
44             super("X509CRL: " ~ error);
45         }
46     }
47 
48     /**
49     * Check if this particular certificate is listed in the CRL
50     */
51     bool isRevoked(in X509Certificate cert) const
52     {
53         /*
54         If the cert wasn't issued by the CRL issuer, it's possible the cert
55         is revoked, but not by this CRL. Maybe throw new an exception instead?
56         */
57         if (cert.issuerDn() != issuerDn())
58             return false;
59         
60         Vector!ubyte crl_akid = authorityKeyId();
61         const Vector!ubyte cert_akid = cert.authorityKeyId();
62         
63         if (!crl_akid.empty && !cert_akid.empty)
64             if (crl_akid != cert_akid)
65                 return false;
66         
67         const Vector!ubyte cert_serial = cert.serialNumber();
68         
69         bool is_revoked = false;
70         
71         foreach (const revoked; m_revoked[])
72         {
73             if (cert_serial == revoked.serialNumber())
74             {
75                 if (revoked.reasonCode() == REMOVE_FROM_CRL)
76                     is_revoked = false;
77                 else
78                     is_revoked = true;
79             }
80         }
81         
82         return is_revoked;
83     }
84 
85 
86     /**
87     * Get the entries of this CRL in the form of a vector.
88     * Returns: vector containing the entries of this CRL.
89     */
90     ref const(Vector!CRLEntry) getRevoked() const
91     {
92         return m_revoked;
93     }
94 
95     /**
96     * Get the issuer DN of this CRL.
97     * Returns: CRLs issuer DN
98     */
99     X509DN issuerDn() const
100     {
101         return createDn(m_info);
102     }
103 
104 
105     /**
106     * Get the AuthorityKeyIdentifier of this CRL.
107     * Returns: this CRLs AuthorityKeyIdentifier
108     */
109     Vector!ubyte authorityKeyId() const
110     {
111         return m_info.get1Memvec("X509v3.AuthorityKeyIdentifier");
112     }
113 
114     /**
115     * Get the serial number of this CRL.
116     * Returns: CRLs serial number
117     */
118     uint crlNumber() const
119     {
120         return m_info.get1Uint("X509v3.CRLNumber");
121     }
122 
123     /**
124     * Get the CRL's thisUpdate value.
125     * Returns: CRLs thisUpdate
126     */
127     const(X509Time) thisUpdate() const
128     {
129         return X509Time(m_info.get1("X509.CRL.start"));
130     }
131 
132     /**
133     * Get the CRL's nextUpdate value.
134     * Returns: CRLs nextdUpdate
135     */
136     const(X509Time) nextUpdate() const
137     {
138         return X509Time(m_info.get1("X509.CRL.end"));
139     }
140 
141     /**
142     * Construct a CRL from a data source.
143     *
144     * Params:
145     *  input = the data source providing the DER or PEM encoded CRL.
146     *  throw_on_unknown_critical_ = should we throw new an exception
147     * if an unknown CRL extension marked as critical is encountered.
148     */
149     this(DataSource input, bool throw_on_unknown_critical_ = false)
150     {
151         m_throw_on_unknown_critical = throw_on_unknown_critical_;
152         super(input, "X509 CRL/CRL");
153         doDecode();
154     }
155 
156     /**
157     * Construct a CRL from a file containing the DER or PEM encoded CRL.
158     *
159     * Params:
160     *  filename = the name of the CRL file
161     *  throw_on_unknown_critical_ = should we throw new an exception
162     * if an unknown CRL extension marked as critical is encountered.
163     */
164     this(in string filename,
165          bool throw_on_unknown_critical_ = false)
166     {
167         m_throw_on_unknown_critical = throw_on_unknown_critical_;
168         super(filename, "CRL/X509 CRL");
169         doDecode();
170     }
171 
172     /**
173     * Construct a CRL from a binary vector
174     * Params:
175     *  vec = the binary (DER) representation of the CRL
176     *  throw_on_unknown_critical_ = should we throw new an exception
177     * if an unknown CRL extension marked as critical is encountered.
178     */
179     this(const ref Vector!ubyte vec, bool throw_on_unknown_critical_ = false)
180     {
181         m_throw_on_unknown_critical = throw_on_unknown_critical_;
182         super(vec, "CRL/X509 CRL");
183         doDecode();
184     }
185 
186 protected:
187 
188     /*
189     * Decode the TBSCertList data
190     */
191     override void forceDecode()
192     {
193         logTrace("Starting Decode CRL");
194         BERDecoder tbs_crl = BERDecoder(m_tbs_bits);
195         
196         size_t _version;
197         tbs_crl.decodeOptional(_version, ASN1Tag.INTEGER, ASN1Tag.UNIVERSAL);
198         
199         if (_version != 0 && _version != 1)
200             throw new X509CRLError("Unknown X.509 CRL version " ~ to!string(_version+1));
201         
202         auto sig_algo_inner = AlgorithmIdentifier();
203         tbs_crl.decode(sig_algo_inner);
204         
205         logTrace("Sig algo inner: ", OIDS.lookup(sig_algo_inner.oid));
206         if (m_sig_algo != sig_algo_inner)
207             throw new X509CRLError("Algorithm identifier mismatch");
208         
209         X509DN dn_issuer = X509DN();
210         tbs_crl.decode(dn_issuer);
211         m_info.add(dn_issuer.contents());
212         
213         X509Time start = X509Time(Clock.currTime(UTC()));
214         X509Time end = X509Time(Clock.currTime(UTC()));
215         tbs_crl.decode(start).decode(end);
216         logTrace("CRL Start, ", start.readableString());
217         m_info.add("X509.CRL.start", start.readableString());
218         logTrace("CRL End");
219         logTrace("CRL Start, ", end.readableString());
220         m_info.add("X509.CRL.end", end.readableString());
221         
222         BERObject next = tbs_crl.getNextObject();
223         
224         logTrace("Next...");
225         if (next.type_tag == ASN1Tag.SEQUENCE && next.class_tag == ASN1Tag.CONSTRUCTED)
226         {
227             BERDecoder cert_list = BERDecoder(next.value);
228             
229             while (cert_list.moreItems())
230             {
231                 CRLEntry entry = CRLEntry(m_throw_on_unknown_critical);
232                 cert_list.decode(entry);
233                 m_revoked.pushBack(entry);
234             }
235             next = tbs_crl.getNextObject();
236         }
237         
238         if (next.type_tag == 0 &&
239             next.class_tag == (ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC))
240         {
241             BERDecoder crl_options = BERDecoder(next.value);
242             
243             X509Extensions extensions = X509Extensions(m_throw_on_unknown_critical);
244             
245             crl_options.decode(extensions).verifyEnd();
246             
247             extensions.contentsTo(m_info, m_info);
248             
249             next = tbs_crl.getNextObject();
250         }
251         
252         if (next.type_tag != ASN1Tag.NO_OBJECT)
253             throw new X509CRLError("Unknown tag in CRL");
254         
255         tbs_crl.verifyEnd();
256     }
257 
258 
259     bool m_throw_on_unknown_critical;
260     Vector!CRLEntry m_revoked;
261     DataStore m_info;
262 }