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