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}