Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

x509_cert/
certificate.rs

1//! Certificate types
2
3use crate::{AlgorithmIdentifier, SubjectPublicKeyInfo};
4use crate::{ext, name::Name, serial_number::SerialNumber, time::Validity};
5use alloc::vec::Vec;
6use const_oid::AssociatedOid;
7use core::{cmp::Ordering, fmt::Debug};
8use der::{Decode, Enumerated, ErrorKind, Sequence, Tag, ValueOrd, asn1::BitString};
9
10#[cfg(feature = "pem")]
11use der::{
12    DecodePem,
13    pem::{self, PemLabel},
14};
15
16#[cfg(feature = "digest")]
17use {
18    der::Encode,
19    digest::{Digest, Output},
20    spki::DigestWriter,
21};
22
23use crate::time::Time;
24
25/// [`Profile`] allows the consumer of this crate to customize the behavior when parsing
26/// certificates.
27/// By default, parsing will be made in a rfc5280-compliant manner.
28pub trait Profile: PartialEq + Debug + Eq + Ord + Clone + Copy + Default + 'static {
29    /// Checks to run when parsing serial numbers
30    fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
31        // See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
32        // since some X.509 implementations interpret the limit of 20 bytes to refer
33        // to the pre-encoded value.
34        if serial.inner.len() > SerialNumber::<Self>::MAX_DECODE_LEN {
35            Err(Tag::Integer.value_error().into())
36        } else {
37            Ok(())
38        }
39    }
40
41    /// Adjustments to the time to run while serializing validity.
42    /// See [RFC 5280 Section 4.1.2.5]:
43    /// ```text
44    /// CAs conforming to this profile MUST always encode certificate
45    /// validity dates through the year 2049 as UTCTime; certificate validity
46    /// dates in 2050 or later MUST be encoded as GeneralizedTime.
47    /// ```
48    ///
49    /// [RFC 5280 Section 4.1.2.5]: https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.5
50    fn time_encoding(mut time: Time) -> der::Result<Time> {
51        time.rfc5280_adjust_utc_time()?;
52        Ok(time)
53    }
54}
55
56#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
57#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
58/// Parse and serialize certificates in rfc5280-compliant manner
59pub struct Rfc5280;
60
61impl Profile for Rfc5280 {}
62
63#[cfg(feature = "hazmat")]
64#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
65#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
66/// Parse raw x509 certificate and disable all the checks and modification to the underlying data.
67pub struct Raw;
68
69#[cfg(feature = "hazmat")]
70impl Profile for Raw {
71    fn check_serial_number(_serial: &SerialNumber<Self>) -> der::Result<()> {
72        Ok(())
73    }
74    fn time_encoding(time: Time) -> der::Result<Time> {
75        Ok(time)
76    }
77}
78
79/// Certificate `Version` as defined in [RFC 5280 Section 4.1].
80///
81/// ```text
82/// Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
83/// ```
84///
85/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
86#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
87#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)]
88#[asn1(type = "INTEGER")]
89#[repr(u8)]
90#[derive(Default)]
91pub enum Version {
92    /// Version 1 (default)
93    #[default]
94    V1 = 0,
95
96    /// Version 2
97    V2 = 1,
98
99    /// Version 3
100    V3 = 2,
101}
102
103impl ValueOrd for Version {
104    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
105        (*self as u8).value_cmp(&(*other as u8))
106    }
107}
108
109/// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1]
110pub type TbsCertificate = TbsCertificateInner<Rfc5280>;
111
112/// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1]
113///
114/// ASN.1 structure containing the names of the subject and issuer, a public
115/// key associated with the subject, a validity period, and other associated
116/// information.
117///
118/// ```text
119/// TBSCertificate  ::=  SEQUENCE  {
120///     version         [0]  EXPLICIT Version DEFAULT v1,
121///     serialNumber         CertificateSerialNumber,
122///     signature            AlgorithmIdentifier,
123///     issuer               Name,
124///     validity             Validity,
125///     subject              Name,
126///     subjectPublicKeyInfo SubjectPublicKeyInfo,
127///     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
128///                          -- If present, version MUST be v2 or v3
129///     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
130///                          -- If present, version MUST be v2 or v3
131///     extensions      [3]  Extensions OPTIONAL
132///                          -- If present, version MUST be v3 --
133/// }
134/// ```
135///
136/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
137#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
138#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
139#[allow(missing_docs)]
140pub struct TbsCertificateInner<P: Profile = Rfc5280> {
141    /// The certificate version.
142    ///
143    /// Note that this value defaults to Version 1 per the RFC. However,
144    /// fields such as `issuer_unique_id`, `subject_unique_id` and `extensions`
145    /// require later versions. Care should be taken in order to ensure
146    /// standards compliance.
147    #[asn1(context_specific = "0", default = "Default::default")]
148    pub(crate) version: Version,
149
150    pub(crate) serial_number: SerialNumber<P>,
151    pub(crate) signature: AlgorithmIdentifier,
152    pub(crate) issuer: Name,
153    pub(crate) validity: Validity<P>,
154    pub(crate) subject: Name,
155    pub(crate) subject_public_key_info: SubjectPublicKeyInfo,
156
157    #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
158    pub(crate) issuer_unique_id: Option<BitString>,
159
160    #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")]
161    pub(crate) subject_unique_id: Option<BitString>,
162
163    #[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
164    pub(crate) extensions: Option<ext::Extensions>,
165}
166
167impl<P: Profile> TbsCertificateInner<P> {
168    /// [`Version`] of this certificate (v1/v2/v3).
169    pub fn version(&self) -> Version {
170        self.version
171    }
172
173    /// Serial number of this certificate.
174    ///
175    /// X.509 serial numbers are used to uniquely identify certificates issued by a given
176    /// Certificate Authority (CA) identified in the `issuer` field.
177    pub fn serial_number(&self) -> &SerialNumber<P> {
178        &self.serial_number
179    }
180
181    /// Identifies the signature algorithm that this `TBSCertificate` should be signed with.
182    ///
183    /// In a signed certificate, matches [`CertificateInner::signature_algorithm`].
184    pub fn signature(&self) -> &AlgorithmIdentifier {
185        &self.signature
186    }
187
188    /// Certificate issuer: [`Name`] of the Certificate Authority (CA) which issued this
189    /// certificate.
190    pub fn issuer(&self) -> &Name {
191        &self.issuer
192    }
193
194    /// Validity period for this certificate: time range in which a certificate is considered valid,
195    /// after which it expires.
196    pub fn validity(&self) -> &Validity<P> {
197        &self.validity
198    }
199
200    /// Subject of this certificate: entity that the certificate is intended to represent or
201    /// authenticate, e.g. an individual, a device, or an organization.
202    pub fn subject(&self) -> &Name {
203        &self.subject
204    }
205
206    /// Subject Public Key Info (SPKI): public key information about this certificate including
207    /// algorithm identifier and key data.
208    pub fn subject_public_key_info(&self) -> &SubjectPublicKeyInfo {
209        &self.subject_public_key_info
210    }
211
212    /// Issuer unique ID: unique identifier representing the issuing CA, as defined by the
213    /// issuing CA.
214    ///
215    /// (NOTE: added in X.509 v2)
216    pub fn issuer_unique_id(&self) -> &Option<BitString> {
217        &self.issuer_unique_id
218    }
219
220    /// Subject unique ID: unique identifier representing the certificate subject, as defined by the
221    /// issuing CA.
222    ///
223    /// (NOTE: added in X.509 v2)
224    pub fn subject_unique_id(&self) -> &Option<BitString> {
225        &self.subject_unique_id
226    }
227
228    /// Certificate extensions.
229    ///
230    /// Additional fields in a digital certificate that provide extra information beyond the
231    /// standard fields. These extensions enhance the functionality and flexibility of certificates,
232    /// allowing them to convey more specific details about the certificate's usage and constraints.
233    ///
234    /// (NOTE: added in X.509 v3)
235    pub fn extensions(&self) -> Option<&ext::Extensions> {
236        self.extensions.as_ref()
237    }
238
239    /// Decodes a single extension.
240    ///
241    /// Returns `Ok(None)` if the extension is not present.
242    ///
243    /// Otherwise, returns the extension, and indicates if the extension was marked critical in the
244    /// boolean.
245    ///
246    /// ```
247    /// # #[cfg(feature = "pem")]
248    /// # fn pemonly() {
249    /// # const CERT_PEM: &str = include_str!("../tests/examples/amazon.pem");
250    /// use x509_cert::{der::DecodePem, ext::pkix::BasicConstraints, Certificate};
251    /// let certificate = Certificate::from_pem(CERT_PEM.as_bytes()).expect("parse certificate");
252    ///
253    /// let (critical, constraints) = certificate.tbs_certificate().get_extension::<BasicConstraints>()
254    ///     .expect("Failed to parse extension")
255    ///     .expect("Basic constraints expected");
256    /// # let _ = constraints;
257    /// # }
258    /// ```
259    ///
260    /// # Errors
261    ///
262    /// Returns an error if multiple of these extensions are present.
263    ///
264    /// Returns a decoding error if decoding failed.
265    pub fn get_extension<'a, T: Decode<'a> + AssociatedOid>(
266        &'a self,
267    ) -> Result<Option<(bool, T)>, <T as Decode<'a>>::Error> {
268        let mut iter = self.filter_extensions::<T>().peekable();
269        match iter.next() {
270            None => Ok(None),
271            Some(item) => match iter.peek() {
272                Some(..) => Err(der::Error::from(ErrorKind::Failed).into()),
273                None => Ok(Some(item?)),
274            },
275        }
276    }
277
278    /// Filters extensions by an associated OID
279    ///
280    /// Returns a filtered iterator over all the extensions with the OID.
281    ///
282    /// ```
283    /// # #[cfg(feature = "pem")]
284    /// # fn pemonly() {
285    /// # const CERT_PEM: &str = include_str!("../tests/examples/amazon.pem");
286    /// use x509_cert::{der::DecodePem, ext::pkix::BasicConstraints, Certificate};
287    /// let certificate = Certificate::from_pem(CERT_PEM.as_bytes()).expect("parse certificate");
288    ///
289    /// let mut extensions_found = certificate.tbs_certificate().filter_extensions::<BasicConstraints>();
290    /// while let Some(Ok((critical, extension))) = extensions_found.next() {
291    ///     println!("Found (critical={critical}): {extension:?}");
292    /// }
293    /// # }
294    /// ```
295    ///
296    /// # Safety
297    ///
298    /// According to [RFC 5290 section 4.2], extensions should not appear more than once.
299    /// A better alternative is to use [`TbsCertificateInner::get_extension`] instead.
300    ///
301    /// [RFC 5290 section 4.2]: https://www.rfc-editor.org/rfc/rfc5280#section-4.2
302    pub fn filter_extensions<'a, T: Decode<'a> + AssociatedOid>(
303        &'a self,
304    ) -> impl 'a + Iterator<Item = Result<(bool, T), <T as Decode<'a>>::Error>> {
305        self.extensions
306            .as_deref()
307            .unwrap_or(&[])
308            .iter()
309            .filter(|e| e.extn_id == T::OID)
310            .map(|e| Ok((e.critical, T::from_der(e.extn_value.as_bytes())?)))
311    }
312}
313
314/// X.509 certificates are defined in [RFC 5280 Section 4.1].
315///
316/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
317pub type Certificate = CertificateInner<Rfc5280>;
318
319/// X.509 certificates are defined in [RFC 5280 Section 4.1].
320///
321/// ```text
322/// Certificate  ::=  SEQUENCE  {
323///     tbsCertificate       TBSCertificate,
324///     signatureAlgorithm   AlgorithmIdentifier,
325///     signature            BIT STRING
326/// }
327/// ```
328///
329/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
330#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
331#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
332#[allow(missing_docs)]
333pub struct CertificateInner<P: Profile = Rfc5280> {
334    pub(crate) tbs_certificate: TbsCertificateInner<P>,
335    pub(crate) signature_algorithm: AlgorithmIdentifier,
336    pub(crate) signature: BitString,
337}
338
339impl<P: Profile> CertificateInner<P> {
340    /// Get the [`TbsCertificateInner`] (i.e. the part the signature is computed over).
341    pub fn tbs_certificate(&self) -> &TbsCertificateInner<P> {
342        &self.tbs_certificate
343    }
344
345    /// Signature algorithm used to sign the serialization of [`CertificateInner::tbs_certificate`].
346    pub fn signature_algorithm(&self) -> &AlgorithmIdentifier {
347        &self.signature_algorithm
348    }
349
350    /// Signature over the DER serialization of [`CertificateInner::tbs_certificate`] using the
351    /// algorithm identified in [`CertificateInner::signature_algorithm`].
352    pub fn signature(&self) -> &BitString {
353        &self.signature
354    }
355}
356
357#[cfg(feature = "pem")]
358impl<P: Profile> PemLabel for CertificateInner<P> {
359    const PEM_LABEL: &'static str = "CERTIFICATE";
360}
361
362/// `PkiPath` as defined by X.509 and referenced by [RFC 6066].
363///
364/// This contains a series of certificates in validation order from the
365/// top-most certificate to the bottom-most certificate. This means that
366/// the first certificate signs the second certificate and so on.
367///
368/// ```text
369/// PkiPath ::= SEQUENCE OF Certificate
370/// ```
371///
372/// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-10.1
373pub type PkiPath = Vec<Certificate>;
374
375#[cfg(feature = "pem")]
376impl<P: Profile> CertificateInner<P> {
377    /// Parse a chain of pem-encoded certificates from a slice.
378    ///
379    /// Returns the list of certificates.
380    pub fn load_pem_chain(mut input: &[u8]) -> Result<Vec<Self>, der::Error> {
381        fn find_boundary<T>(haystack: &[T], needle: &[T]) -> Option<usize>
382        where
383            for<'a> &'a [T]: PartialEq,
384        {
385            haystack
386                .windows(needle.len())
387                .position(|window| window == needle)
388        }
389
390        let mut certs = Vec::new();
391        let mut position: usize = 0;
392
393        let end_boundary = &b"-----END CERTIFICATE-----"[..];
394
395        // Strip the trailing whitespaces
396        loop {
397            if input.is_empty() {
398                break;
399            }
400            let last_pos = input.len() - 1;
401
402            match input.get(last_pos) {
403                Some(b'\r') | Some(b'\n') => {
404                    input = &input[..last_pos];
405                }
406                _ => break,
407            }
408        }
409
410        while position + 1 < input.len() {
411            let rest = &input[position..];
412            let end_pos = find_boundary(rest, end_boundary)
413                .ok_or(pem::Error::PostEncapsulationBoundary)?
414                + end_boundary.len();
415
416            let cert_buf = &rest[..end_pos];
417            let cert = Self::from_pem(cert_buf)?;
418            certs.push(cert);
419
420            position += end_pos;
421        }
422
423        Ok(certs)
424    }
425}
426
427#[cfg(feature = "digest")]
428impl<P> CertificateInner<P>
429where
430    P: Profile,
431{
432    /// Return the hash of the DER serialization of this certificate
433    pub fn hash<D>(&self) -> der::Result<Output<D>>
434    where
435        D: Digest,
436    {
437        let mut digest = D::new();
438
439        self.encode(&mut DigestWriter(&mut digest))?;
440
441        Ok(digest.finalize())
442    }
443}