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