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 }