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