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

x509_cert/
ext.rs

1//! Standardized X.509 Certificate Extensions
2
3use const_oid::AssociatedOid;
4use der::{Sequence, ValueOrd, asn1::OctetString};
5use spki::ObjectIdentifier;
6
7pub mod pkix;
8
9/// Extension as defined in [RFC 5280 Section 4.1.2.9].
10///
11/// The ASN.1 definition for Extension objects is below. The extnValue type
12/// may be further parsed using a decoder corresponding to the extnID value.
13///
14/// ```text
15/// Extension  ::=  SEQUENCE  {
16///     extnID      OBJECT IDENTIFIER,
17///     critical    BOOLEAN DEFAULT FALSE,
18///     extnValue   OCTET STRING
19///                 -- contains the DER encoding of an ASN.1 value
20///                 -- corresponding to the extension type identified
21///                 -- by extnID
22/// }
23/// ```
24///
25/// [RFC 5280 Section 4.1.2.9]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.9
26#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
27#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
28#[allow(missing_docs)]
29pub struct Extension {
30    pub extn_id: ObjectIdentifier,
31
32    #[asn1(default = "Default::default")]
33    pub critical: bool,
34
35    pub extn_value: OctetString,
36}
37
38impl ToExtension for Extension {
39    type Error = der::Error;
40
41    fn to_extension(
42        self,
43        _subject: &crate::name::Name,
44        _extensions: &[Extension],
45    ) -> Result<Extension, Self::Error> {
46        Ok(self)
47    }
48}
49
50/// Extensions as defined in [RFC 5280 Section 4.1.2.9].
51///
52/// ```text
53/// Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
54/// ```
55///
56/// [RFC 5280 Section 4.1.2.9]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.9
57pub type Extensions = alloc::vec::Vec<Extension>;
58
59/// Trait for types that define their default criticality as an extension.
60///
61/// This is used for most der::Encode types that are used as extensions.
62pub trait Criticality {
63    /// Should the extension be marked critical
64    ///
65    /// This affects the behavior of a validator when using the generated certificate.
66    /// See [RFC 5280 Section 4.2]:
67    /// ```text
68    /// A certificate-using system MUST reject the certificate if it encounters
69    /// a critical extension it does not recognize or a critical extension
70    /// that contains information that it cannot process.  A non-critical
71    /// extension MAY be ignored if it is not recognized, but MUST be
72    /// processed if it is recognized.
73    /// ```
74    ///
75    /// [RFC 5280 Section 4.2]: https://www.rfc-editor.org/rfc/rfc5280#section-4.2
76    fn criticality(&self, subject: &crate::name::Name, extensions: &[Extension]) -> bool;
77}
78
79/// Trait to be implemented by extensions to allow them to be formatted as x509 v3 extensions by
80/// builder.
81///
82/// # Examples
83///
84/// ```
85/// use const_oid::{AssociatedOid, ObjectIdentifier};
86/// use x509_cert::{der::Sequence, ext, name};
87///
88/// /// This extension indicates the age of the captain at the time of signature
89/// #[derive(Clone, Debug, Eq, PartialEq, Sequence)]
90/// pub struct CaptainAge {
91///     pub age: u32,
92/// }
93///
94/// impl AssociatedOid for CaptainAge {
95/// # // https://datatracker.ietf.org/doc/html/rfc5612
96/// # // 32473 is the private OID reserved for documentation.
97///     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.32473.1");
98/// }
99///
100/// impl ext::Criticality for CaptainAge {
101///     fn criticality(&self, _subject: &name::Name, _extensions: &[ext::Extension]) -> bool {
102///         false
103///     }
104/// }
105/// ```
106pub trait ToExtension {
107    /// The error type returned when encoding the extension.
108    type Error;
109
110    /// Returns the Extension with the content encoded.
111    fn to_extension(
112        self,
113        subject: &crate::name::Name,
114        extensions: &[Extension],
115    ) -> Result<Extension, Self::Error>;
116}
117
118impl<T: Criticality + AssociatedOid + der::Encode> ToExtension for &T {
119    type Error = der::Error;
120
121    fn to_extension(
122        self,
123        subject: &crate::name::Name,
124        extensions: &[Extension],
125    ) -> Result<Extension, Self::Error> {
126        let criticality = self.criticality(subject, extensions);
127        (criticality, self).to_extension(subject, extensions)
128    }
129}
130
131impl<T: Criticality + der::Encode> ToExtension for (ObjectIdentifier, &T) {
132    type Error = der::Error;
133
134    fn to_extension(
135        self,
136        subject: &crate::name::Name,
137        extensions: &[Extension],
138    ) -> Result<Extension, Self::Error> {
139        let criticality = self.1.criticality(subject, extensions);
140        (self.0, criticality, self.1).to_extension(subject, extensions)
141    }
142}
143
144impl<T: AssociatedOid + der::Encode> ToExtension for (bool, &T) {
145    type Error = der::Error;
146
147    fn to_extension(
148        self,
149        subject: &crate::name::Name,
150        extensions: &[Extension],
151    ) -> Result<Extension, Self::Error> {
152        (T::OID, self.0, self.1).to_extension(subject, extensions)
153    }
154}
155
156impl<T: der::Encode> ToExtension for (ObjectIdentifier, bool, &T) {
157    type Error = der::Error;
158
159    fn to_extension(
160        self,
161        _subject: &crate::name::Name,
162        _extensions: &[Extension],
163    ) -> Result<Extension, Self::Error> {
164        Ok(Extension {
165            extn_id: self.0,
166            critical: self.1,
167            extn_value: OctetString::new(self.2.to_der()?)?,
168        })
169    }
170}