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}