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 }