1use 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
17pub trait Profile: PartialEq + Debug + Eq + Clone {
21 fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
23 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)]
36pub 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)]
44pub 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#[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 V1 = 0,
68
69 V2 = 1,
71
72 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
88pub type TbsCertificate = TbsCertificateInner<Rfc5280>;
90
91#[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 #[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 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 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
178pub type Certificate = CertificateInner<Rfc5280>;
182
183#[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
208pub type PkiPath = Vec<Certificate>;
220
221#[cfg(feature = "pem")]
222impl<P: Profile> CertificateInner<P> {
223 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 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}