x509_cert/
certificate.rs

1//! Certificate types
2
3use crate::{name::Name, serial_number::SerialNumber, time::Validity};
4use alloc::vec::Vec;
5use const_oid::AssociatedOid;
6use core::{cmp::Ordering, fmt::Debug};
7use der::asn1::BitString;
8use der::{Decode, Enumerated, Error, ErrorKind, Sequence, Tag, ValueOrd};
9use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned};
10
11#[cfg(feature = "pem")]
12use der::{
13    pem::{self, PemLabel},
14    DecodePem,
15};
16
17/// [`Profile`] allows the consumer of this crate to customize the behavior when parsing
18/// certificates.
19/// By default, parsing will be made in a rfc5280-compliant manner.
20pub trait Profile: PartialEq + Debug + Eq + Clone {
21    /// Checks to run when parsing serial numbers
22    fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
23        // See the note in `SerialNumber::new`: we permit lengths of 21 bytes here,
24        // since some X.509 implementations interpret the limit of 20 bytes to refer
25        // to the pre-encoded value.
26        if serial.inner.len() > SerialNumber::<Self>::MAX_DECODE_LEN {
27            Err(Tag::Integer.value_error())
28        } else {
29            Ok(())
30        }
31    }
32}
33
34#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
35#[derive(Debug, PartialEq, Eq, Clone)]
36/// Parse certificates with rfc5280-compliant checks
37pub struct Rfc5280;
38
39impl Profile for Rfc5280 {}
40
41#[cfg(feature = "hazmat")]
42#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
43#[derive(Debug, PartialEq, Eq, Clone)]
44/// Parse raw x509 certificate and disable all the checks
45pub struct Raw;
46
47#[cfg(feature = "hazmat")]
48impl Profile for Raw {
49    fn check_serial_number(_serial: &SerialNumber<Self>) -> der::Result<()> {
50        Ok(())
51    }
52}
53
54/// Certificate `Version` as defined in [RFC 5280 Section 4.1].
55///
56/// ```text
57/// Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
58/// ```
59///
60/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
61#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
62#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)]
63#[asn1(type = "INTEGER")]
64#[repr(u8)]
65pub enum Version {
66    /// Version 1 (default)
67    V1 = 0,
68
69    /// Version 2
70    V2 = 1,
71
72    /// Version 3
73    V3 = 2,
74}
75
76impl ValueOrd for Version {
77    fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
78        (*self as u8).value_cmp(&(*other as u8))
79    }
80}
81
82impl Default for Version {
83    fn default() -> Self {
84        Self::V1
85    }
86}
87
88/// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1]
89pub type TbsCertificate = TbsCertificateInner<Rfc5280>;
90
91/// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1]
92///
93/// ASN.1 structure containing the names of the subject and issuer, a public
94/// key associated with the subject, a validity period, and other associated
95/// information.
96///
97/// ```text
98/// TBSCertificate  ::=  SEQUENCE  {
99///     version         [0]  EXPLICIT Version DEFAULT v1,
100///     serialNumber         CertificateSerialNumber,
101///     signature            AlgorithmIdentifier,
102///     issuer               Name,
103///     validity             Validity,
104///     subject              Name,
105///     subjectPublicKeyInfo SubjectPublicKeyInfo,
106///     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
107///                          -- If present, version MUST be v2 or v3
108///     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
109///                          -- If present, version MUST be v2 or v3
110///     extensions      [3]  Extensions OPTIONAL
111///                          -- If present, version MUST be v3 --
112/// }
113/// ```
114///
115/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
118#[allow(missing_docs)]
119pub struct TbsCertificateInner<P: Profile = Rfc5280> {
120    /// The certificate version
121    ///
122    /// Note that this value defaults to Version 1 per the RFC. However,
123    /// fields such as `issuer_unique_id`, `subject_unique_id` and `extensions`
124    /// require later versions. Care should be taken in order to ensure
125    /// standards compliance.
126    #[asn1(context_specific = "0", default = "Default::default")]
127    pub version: Version,
128
129    pub serial_number: SerialNumber<P>,
130    pub signature: AlgorithmIdentifierOwned,
131    pub issuer: Name,
132    pub validity: Validity,
133    pub subject: Name,
134    pub subject_public_key_info: SubjectPublicKeyInfoOwned,
135
136    #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
137    pub issuer_unique_id: Option<BitString>,
138
139    #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")]
140    pub subject_unique_id: Option<BitString>,
141
142    #[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
143    pub extensions: Option<crate::ext::Extensions>,
144}
145
146impl<P: Profile> TbsCertificateInner<P> {
147    /// Decodes a single extension
148    ///
149    /// Returns an error if multiple of these extensions is present. Returns
150    /// `Ok(None)` if the extension is not present. Returns a decoding error
151    /// if decoding failed. Otherwise returns the extension.
152    pub fn get<'a, T: Decode<'a> + AssociatedOid>(&'a self) -> Result<Option<(bool, T)>, Error> {
153        let mut iter = self.filter::<T>().peekable();
154        match iter.next() {
155            None => Ok(None),
156            Some(item) => match iter.peek() {
157                Some(..) => Err(ErrorKind::Failed.into()),
158                None => Ok(Some(item?)),
159            },
160        }
161    }
162
163    /// Filters extensions by an associated OID
164    ///
165    /// Returns a filtered iterator over all the extensions with the OID.
166    pub fn filter<'a, T: Decode<'a> + AssociatedOid>(
167        &'a self,
168    ) -> impl 'a + Iterator<Item = Result<(bool, T), Error>> {
169        self.extensions
170            .as_deref()
171            .unwrap_or(&[])
172            .iter()
173            .filter(|e| e.extn_id == T::OID)
174            .map(|e| Ok((e.critical, T::from_der(e.extn_value.as_bytes())?)))
175    }
176}
177
178/// X.509 certificates are defined in [RFC 5280 Section 4.1].
179///
180/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
181pub type Certificate = CertificateInner<Rfc5280>;
182
183/// X.509 certificates are defined in [RFC 5280 Section 4.1].
184///
185/// ```text
186/// Certificate  ::=  SEQUENCE  {
187///     tbsCertificate       TBSCertificate,
188///     signatureAlgorithm   AlgorithmIdentifier,
189///     signature            BIT STRING
190/// }
191/// ```
192///
193/// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1
194#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
195#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
196#[allow(missing_docs)]
197pub struct CertificateInner<P: Profile = Rfc5280> {
198    pub tbs_certificate: TbsCertificateInner<P>,
199    pub signature_algorithm: AlgorithmIdentifierOwned,
200    pub signature: BitString,
201}
202
203#[cfg(feature = "pem")]
204impl<P: Profile> PemLabel for CertificateInner<P> {
205    const PEM_LABEL: &'static str = "CERTIFICATE";
206}
207
208/// `PkiPath` as defined by X.509 and referenced by [RFC 6066].
209///
210/// This contains a series of certificates in validation order from the
211/// top-most certificate to the bottom-most certificate. This means that
212/// the first certificate signs the second certificate and so on.
213///
214/// ```text
215/// PkiPath ::= SEQUENCE OF Certificate
216/// ```
217///
218/// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-10.1
219pub type PkiPath = Vec<Certificate>;
220
221#[cfg(feature = "pem")]
222impl<P: Profile> CertificateInner<P> {
223    /// Parse a chain of pem-encoded certificates from a slice.
224    ///
225    /// Returns the list of certificates.
226    pub fn load_pem_chain(mut input: &[u8]) -> Result<Vec<Self>, Error> {
227        fn find_boundary<T>(haystack: &[T], needle: &[T]) -> Option<usize>
228        where
229            for<'a> &'a [T]: PartialEq,
230        {
231            haystack
232                .windows(needle.len())
233                .position(|window| window == needle)
234        }
235
236        let mut certs = Vec::new();
237        let mut position: usize = 0;
238
239        let end_boundary = &b"-----END CERTIFICATE-----"[..];
240
241        // Strip the trailing whitespaces
242        loop {
243            if input.is_empty() {
244                break;
245            }
246            let last_pos = input.len() - 1;
247
248            match input.get(last_pos) {
249                Some(b'\r') | Some(b'\n') => {
250                    input = &input[..last_pos];
251                }
252                _ => break,
253            }
254        }
255
256        while position < input.len() - 1 {
257            let rest = &input[position..];
258            let end_pos = find_boundary(rest, end_boundary)
259                .ok_or(pem::Error::PostEncapsulationBoundary)?
260                + end_boundary.len();
261
262            let cert_buf = &rest[..end_pos];
263            let cert = Self::from_pem(cert_buf)?;
264            certs.push(cert);
265
266            position += end_pos;
267        }
268
269        Ok(certs)
270    }
271}