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