x509_parser/
revocation_list.rs

1use crate::error::{X509Error, X509Result};
2use crate::extensions::*;
3use crate::time::ASN1Time;
4use crate::utils::format_serial;
5use crate::x509::{
6    parse_serial, parse_signature_value, AlgorithmIdentifier, ReasonCode, X509Name, X509Version,
7};
8
9#[cfg(feature = "verify")]
10use crate::verify::verify_signature;
11#[cfg(feature = "verify")]
12use crate::x509::SubjectPublicKeyInfo;
13use asn1_rs::{BitString, FromDer};
14use der_parser::ber::Tag;
15use der_parser::der::*;
16use der_parser::num_bigint::BigUint;
17use der_parser::oid::Oid;
18use nom::combinator::{all_consuming, complete, map, opt};
19use nom::multi::many0;
20use nom::Offset;
21use oid_registry::*;
22use std::collections::HashMap;
23
24/// An X.509 v2 Certificate Revocation List (CRL).
25///
26/// X.509 v2 CRLs are defined in [RFC5280](https://tools.ietf.org/html/rfc5280).
27///
28/// # Example
29///
30/// To parse a CRL and print information about revoked certificates:
31///
32/// ```rust
33/// use x509_parser::prelude::FromDer;
34/// use x509_parser::revocation_list::CertificateRevocationList;
35///
36/// # static DER: &'static [u8] = include_bytes!("../assets/example.crl");
37/// #
38/// # fn main() {
39/// let res = CertificateRevocationList::from_der(DER);
40/// match res {
41///     Ok((_rem, crl)) => {
42///         for revoked in crl.iter_revoked_certificates() {
43///             println!("Revoked certificate serial: {}", revoked.raw_serial_as_string());
44///             println!("  Reason: {}", revoked.reason_code().unwrap_or_default().1);
45///         }
46///     },
47///     _ => panic!("CRL parsing failed: {:?}", res),
48/// }
49/// # }
50/// ```
51#[derive(Clone, Debug)]
52pub struct CertificateRevocationList<'a> {
53    pub tbs_cert_list: TbsCertList<'a>,
54    pub signature_algorithm: AlgorithmIdentifier<'a>,
55    pub signature_value: BitString<'a>,
56}
57
58impl<'a> CertificateRevocationList<'a> {
59    /// Get the version of the encoded certificate
60    pub fn version(&self) -> Option<X509Version> {
61        self.tbs_cert_list.version
62    }
63
64    /// Get the certificate issuer.
65    #[inline]
66    pub fn issuer(&self) -> &X509Name {
67        &self.tbs_cert_list.issuer
68    }
69
70    /// Get the date and time of the last (this) update.
71    #[inline]
72    pub fn last_update(&self) -> ASN1Time {
73        self.tbs_cert_list.this_update
74    }
75
76    /// Get the date and time of the next update, if present.
77    #[inline]
78    pub fn next_update(&self) -> Option<ASN1Time> {
79        self.tbs_cert_list.next_update
80    }
81
82    /// Return an iterator over the `RevokedCertificate` objects
83    pub fn iter_revoked_certificates(&self) -> impl Iterator<Item = &RevokedCertificate<'a>> {
84        self.tbs_cert_list.revoked_certificates.iter()
85    }
86
87    /// Get the CRL extensions.
88    #[inline]
89    pub fn extensions(&self) -> &[X509Extension] {
90        &self.tbs_cert_list.extensions
91    }
92
93    /// Get the CRL number, if present
94    ///
95    /// Note that the returned value is a `BigUint`, because of the following RFC specification:
96    /// <pre>
97    /// Given the requirements above, CRL numbers can be expected to contain long integers.  CRL
98    /// verifiers MUST be able to handle CRLNumber values up to 20 octets.  Conformant CRL issuers
99    /// MUST NOT use CRLNumber values longer than 20 octets.
100    /// </pre>
101    pub fn crl_number(&self) -> Option<&BigUint> {
102        self.extensions()
103            .iter()
104            .find(|&ext| ext.oid == OID_X509_EXT_CRL_NUMBER)
105            .and_then(|ext| match ext.parsed_extension {
106                ParsedExtension::CRLNumber(ref num) => Some(num),
107                _ => None,
108            })
109    }
110
111    /// Verify the cryptographic signature of this certificate revocation list
112    ///
113    /// `public_key` is the public key of the **signer**.
114    ///
115    /// Not all algorithms are supported, this function is limited to what `ring` supports.
116    #[cfg(feature = "verify")]
117    #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
118    pub fn verify_signature(&self, public_key: &SubjectPublicKeyInfo) -> Result<(), X509Error> {
119        verify_signature(
120            public_key,
121            &self.signature_algorithm,
122            &self.signature_value,
123            self.tbs_cert_list.raw,
124        )
125    }
126}
127
128/// <pre>
129/// CertificateList  ::=  SEQUENCE  {
130///      tbsCertList          TBSCertList,
131///      signatureAlgorithm   AlgorithmIdentifier,
132///      signatureValue       BIT STRING  }
133/// </pre>
134impl<'a> FromDer<'a, X509Error> for CertificateRevocationList<'a> {
135    fn from_der(i: &'a [u8]) -> X509Result<Self> {
136        parse_der_sequence_defined_g(|i, _| {
137            let (i, tbs_cert_list) = TbsCertList::from_der(i)?;
138            let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?;
139            let (i, signature_value) = parse_signature_value(i)?;
140            let crl = CertificateRevocationList {
141                tbs_cert_list,
142                signature_algorithm,
143                signature_value,
144            };
145            Ok((i, crl))
146        })(i)
147    }
148}
149
150/// The sequence TBSCertList contains information about the certificates that have
151/// been revoked by the CA that issued the CRL.
152///
153/// RFC5280 definition:
154///
155/// <pre>
156/// TBSCertList  ::=  SEQUENCE  {
157///         version                 Version OPTIONAL,
158///                                      -- if present, MUST be v2
159///         signature               AlgorithmIdentifier,
160///         issuer                  Name,
161///         thisUpdate              Time,
162///         nextUpdate              Time OPTIONAL,
163///         revokedCertificates     SEQUENCE OF SEQUENCE  {
164///             userCertificate         CertificateSerialNumber,
165///             revocationDate          Time,
166///             crlEntryExtensions      Extensions OPTIONAL
167///                                      -- if present, version MUST be v2
168///                                   } OPTIONAL,
169///         crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
170///                                      -- if present, version MUST be v2
171///                             }
172/// </pre>
173#[derive(Clone, Debug, PartialEq)]
174pub struct TbsCertList<'a> {
175    pub version: Option<X509Version>,
176    pub signature: AlgorithmIdentifier<'a>,
177    pub issuer: X509Name<'a>,
178    pub this_update: ASN1Time,
179    pub next_update: Option<ASN1Time>,
180    pub revoked_certificates: Vec<RevokedCertificate<'a>>,
181    extensions: Vec<X509Extension<'a>>,
182    pub(crate) raw: &'a [u8],
183}
184
185impl<'a> TbsCertList<'a> {
186    /// Returns the certificate extensions
187    #[inline]
188    pub fn extensions(&self) -> &[X509Extension] {
189        &self.extensions
190    }
191
192    /// Returns an iterator over the certificate extensions
193    #[inline]
194    pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
195        self.extensions.iter()
196    }
197
198    /// Searches for an extension with the given `Oid`.
199    ///
200    /// Note: if there are several extensions with the same `Oid`, the first one is returned.
201    pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
202        self.extensions.iter().find(|&ext| ext.oid == *oid)
203    }
204
205    /// Builds and returns a map of extensions.
206    ///
207    /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
208    pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
209        self.extensions
210            .iter()
211            .try_fold(HashMap::new(), |mut m, ext| {
212                if m.contains_key(&ext.oid) {
213                    return Err(X509Error::DuplicateExtensions);
214                }
215                m.insert(ext.oid.clone(), ext);
216                Ok(m)
217            })
218    }
219}
220
221impl<'a> AsRef<[u8]> for TbsCertList<'a> {
222    fn as_ref(&self) -> &[u8] {
223        self.raw
224    }
225}
226
227impl<'a> FromDer<'a, X509Error> for TbsCertList<'a> {
228    fn from_der(i: &'a [u8]) -> X509Result<Self> {
229        let start_i = i;
230        parse_der_sequence_defined_g(move |i, _| {
231            let (i, version) =
232                opt(map(parse_der_u32, X509Version))(i).or(Err(X509Error::InvalidVersion))?;
233            let (i, signature) = AlgorithmIdentifier::from_der(i)?;
234            let (i, issuer) = X509Name::from_der(i)?;
235            let (i, this_update) = ASN1Time::from_der(i)?;
236            let (i, next_update) = ASN1Time::from_der_opt(i)?;
237            let (i, revoked_certificates) = opt(complete(parse_revoked_certificates))(i)?;
238            let (i, extensions) = parse_extensions(i, Tag(0))?;
239            let len = start_i.offset(i);
240            let tbs = TbsCertList {
241                version,
242                signature,
243                issuer,
244                this_update,
245                next_update,
246                revoked_certificates: revoked_certificates.unwrap_or_default(),
247                extensions,
248                raw: &start_i[..len],
249            };
250            Ok((i, tbs))
251        })(i)
252    }
253}
254
255#[derive(Clone, Debug, PartialEq)]
256pub struct RevokedCertificate<'a> {
257    /// The Serial number of the revoked certificate
258    pub user_certificate: BigUint,
259    /// The date on which the revocation occurred is specified.
260    pub revocation_date: ASN1Time,
261    /// Additional information about revocation
262    extensions: Vec<X509Extension<'a>>,
263    pub(crate) raw_serial: &'a [u8],
264}
265
266impl<'a> RevokedCertificate<'a> {
267    /// Return the serial number of the revoked certificate
268    pub fn serial(&self) -> &BigUint {
269        &self.user_certificate
270    }
271
272    /// Get the CRL entry extensions.
273    #[inline]
274    pub fn extensions(&self) -> &[X509Extension] {
275        &self.extensions
276    }
277
278    /// Returns an iterator over the CRL entry extensions
279    #[inline]
280    pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> {
281        self.extensions.iter()
282    }
283
284    /// Searches for a CRL entry extension with the given `Oid`.
285    ///
286    /// Note: if there are several extensions with the same `Oid`, the first one is returned.
287    pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension> {
288        self.extensions.iter().find(|&ext| ext.oid == *oid)
289    }
290
291    /// Builds and returns a map of CRL entry extensions.
292    ///
293    /// If an extension is present twice, this will fail and return `DuplicateExtensions`.
294    pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, X509Error> {
295        self.extensions
296            .iter()
297            .try_fold(HashMap::new(), |mut m, ext| {
298                if m.contains_key(&ext.oid) {
299                    return Err(X509Error::DuplicateExtensions);
300                }
301                m.insert(ext.oid.clone(), ext);
302                Ok(m)
303            })
304    }
305
306    /// Get the raw bytes of the certificate serial number
307    pub fn raw_serial(&self) -> &[u8] {
308        self.raw_serial
309    }
310
311    /// Get a formatted string of the certificate serial number, separated by ':'
312    pub fn raw_serial_as_string(&self) -> String {
313        format_serial(self.raw_serial)
314    }
315
316    /// Get the code identifying the reason for the revocation, if present
317    pub fn reason_code(&self) -> Option<(bool, ReasonCode)> {
318        self.find_extension(&OID_X509_EXT_REASON_CODE)
319            .and_then(|ext| match ext.parsed_extension {
320                ParsedExtension::ReasonCode(code) => Some((ext.critical, code)),
321                _ => None,
322            })
323    }
324
325    /// Get the invalidity date, if present
326    ///
327    /// The invalidity date is the date on which it is known or suspected that the private
328    ///  key was compromised or that the certificate otherwise became invalid.
329    pub fn invalidity_date(&self) -> Option<(bool, ASN1Time)> {
330        self.find_extension(&OID_X509_EXT_INVALIDITY_DATE)
331            .and_then(|ext| match ext.parsed_extension {
332                ParsedExtension::InvalidityDate(date) => Some((ext.critical, date)),
333                _ => None,
334            })
335    }
336}
337
338// revokedCertificates     SEQUENCE OF SEQUENCE  {
339//     userCertificate         CertificateSerialNumber,
340//     revocationDate          Time,
341//     crlEntryExtensions      Extensions OPTIONAL
342//                                   -- if present, MUST be v2
343//                          }  OPTIONAL,
344impl<'a> FromDer<'a, X509Error> for RevokedCertificate<'a> {
345    fn from_der(i: &'a [u8]) -> X509Result<Self> {
346        parse_der_sequence_defined_g(|i, _| {
347            let (i, (raw_serial, user_certificate)) = parse_serial(i)?;
348            let (i, revocation_date) = ASN1Time::from_der(i)?;
349            let (i, extensions) = opt(complete(parse_extension_sequence))(i)?;
350            let revoked = RevokedCertificate {
351                user_certificate,
352                revocation_date,
353                extensions: extensions.unwrap_or_default(),
354                raw_serial,
355            };
356            Ok((i, revoked))
357        })(i)
358    }
359}
360
361fn parse_revoked_certificates(i: &[u8]) -> X509Result<Vec<RevokedCertificate>> {
362    parse_der_sequence_defined_g(|a, _| {
363        all_consuming(many0(complete(RevokedCertificate::from_der)))(a)
364    })(i)
365}