#![no_std]
#![deny(
exceeding_bitshifts,
invalid_type_param_default,
missing_fragment_specifier,
no_mangle_const_items,
overflowing_literals,
patterns_in_fns_without_body,
pub_use_of_private_extern_crate,
unknown_crate_types,
const_err,
order_dependent_trait_objects,
illegal_floating_point_literal_pattern,
improper_ctypes,
late_bound_lifetime_arguments,
non_camel_case_types,
non_shorthand_field_patterns,
non_snake_case,
non_upper_case_globals,
no_mangle_generic_items,
path_statements,
private_in_public,
stable_features,
type_alias_bounds,
tyvar_behind_raw_pointer,
unused,
unused_allocation,
unused_comparisons,
unused_mut,
unreachable_pub,
anonymous_parameters,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
clippy::all
)]
#![forbid(
mutable_transmutes,
unconditional_recursion,
unsafe_code,
intra_doc_link_resolution_failure,
safe_packed_borrows,
while_true,
elided_lifetimes_in_paths,
bare_trait_objects
)]
mod das;
mod sequence;
mod time;
use ring::io::der;
mod spki;
pub use das::DataAlgorithmSignature;
pub use sequence::{ExtensionIterator, SequenceIterator};
pub use spki::SubjectPublicKeyInfo;
#[cfg(feature = "rustls")]
pub use r::SignatureScheme;
#[cfg(not(feature = "rustls"))]
#[non_exhaustive]
#[allow(non_camel_case_types)]
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub enum SignatureScheme {
RSA_PKCS1_SHA256,
RSA_PKCS1_SHA384,
RSA_PKCS1_SHA512,
ECDSA_NISTP256_SHA256,
ECDSA_NISTP384_SHA384,
ED25519,
RSA_PSS_SHA256,
RSA_PSS_SHA384,
RSA_PSS_SHA512,
ED448,
}
#[cfg(not(feature = "webpki"))]
#[non_exhaustive]
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub enum Error {
UnsupportedCertVersion,
UnsupportedSignatureAlgorithm,
UnsupportedSignatureAlgorithmForPublicKey,
InvalidSignatureForPublicKey,
SignatureAlgorithmMismatch,
BadDER,
BadDERTime,
CertNotValidYet,
CertExpired,
InvalidCertValidity,
}
#[cfg(feature = "webpki")]
pub use w::Error;
#[derive(Debug)]
pub struct X509Certificate<'a> {
das: DataAlgorithmSignature<'a>,
serial: &'a [u8],
issuer: &'a [u8],
not_before: u64,
not_after: u64,
subject: &'a [u8],
subject_public_key_info: SubjectPublicKeyInfo<'a>,
extensions: ExtensionIterator<'a>,
}
impl<'a> X509Certificate<'a> {
pub fn das(&self) -> DataAlgorithmSignature<'a> { self.das }
pub fn serial(&self) -> &'a [u8] { self.serial }
pub fn issuer(&self) -> &'a [u8] { self.issuer }
pub fn not_before(&self) -> u64 { self.not_before }
pub fn not_after(&self) -> u64 { self.not_after }
pub fn subject(&self) -> &'a [u8] { self.subject }
pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfo<'a> {
self.subject_public_key_info
}
pub fn extensions(&self) -> ExtensionIterator<'a> { self.extensions }
pub fn verify_signature_against_scheme(
&self, time: u64, scheme: SignatureScheme, message: &[u8], signature: &[u8],
) -> Result<(), Error> {
if time < self.not_before {
return Err(Error::CertNotValidYet);
} else if time > self.not_after {
return Err(Error::CertExpired);
}
self.subject_public_key_info
.get_public_key_tls(scheme)?
.verify(message, signature)
.map_err(|_| Error::InvalidSignatureForPublicKey)
}
pub fn tbs_certificate(&self) -> &[u8] { self.das.data() }
pub fn signature_algorithm_id(&self) -> &[u8] { self.das.algorithm() }
pub fn signature(&self) -> &[u8] { self.das.signature() }
pub fn verify_signature_of_certificate(
&self, time: u64, cert: &X509Certificate<'_>,
) -> Result<(), Error> {
self.verify_signature_against_algorithmid(
time,
cert.signature_algorithm_id(),
cert.tbs_certificate(),
cert.signature(),
)
}
pub fn verify_signature_against_algorithmid(
&self, time: u64, algorithm_id: &[u8], message: &[u8], signature: &[u8],
) -> Result<(), Error> {
if time < self.not_before {
return Err(Error::CertNotValidYet);
} else if time > self.not_after {
return Err(Error::CertExpired);
}
self.subject_public_key_info
.get_public_key_x509(algorithm_id)?
.verify(message, signature)
.map_err(|_| Error::InvalidSignatureForPublicKey)
}
}
pub fn parse_certificate<'a>(certificate: &'a [u8]) -> Result<X509Certificate<'a>, Error> {
use core::convert::TryFrom as _;
let das = DataAlgorithmSignature::try_from(certificate)?;
untrusted::Input::from(&*das.inner()).read_all(Error::BadDER, |input| {
if input.read_bytes(5).map_err(|_| Error::BadDER)?
!= untrusted::Input::from(&[160, 3, 2, 1, 2])
{
return Err(Error::UnsupportedCertVersion);
}
let serial = der::positive_integer(input)
.map_err(|_| Error::BadDER)?
.big_endian_without_leading_zero();
if das::read_sequence(input)?.as_slice_less_safe() != das.algorithm() {
return Err(Error::SignatureAlgorithmMismatch);
}
let issuer = das::read_sequence(input)?.as_slice_less_safe();
let (not_before, not_after) =
der::nested(input, der::Tag::Sequence, Error::BadDER, |input| {
Ok((time::read_time(input)?, time::read_time(input)?))
})?;
if not_before > not_after {
return Err(Error::InvalidCertValidity);
}
let subject = das::read_sequence(input)?.as_slice_less_safe();
let subject_public_key_info = SubjectPublicKeyInfo::read(input)?;
let extensions = if !input.at_end() {
let tag = der::Tag::ContextSpecificConstructed3;
der::nested(input, tag, Error::BadDER, |input| {
der::nested(input, der::Tag::Sequence, Error::BadDER, |input| {
if input.at_end() {
return Err(Error::BadDER);
}
Ok(ExtensionIterator(SequenceIterator::read(input)))
})
})
} else {
Ok(ExtensionIterator(SequenceIterator::read(input)))
}?;
Ok(X509Certificate {
das,
serial,
subject,
not_before,
not_after,
issuer,
subject_public_key_info,
extensions,
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_openssl_generated_cert() {
let signature = include_bytes!("../testing.sig");
let invalid_signature = include_bytes!("../testing.bad-sig");
let forged_message = include_bytes!("../forged-message.txt");
let message = include_bytes!("../gen-bad-cert.sh");
let certificate = include_bytes!("../testing.crt");
let cert = parse_certificate(certificate).unwrap();
assert_eq!(
cert.subject_public_key_info.algorithm(),
include_bytes!("data/alg-ecdsa-p256.der")
);
assert_eq!(cert.subject_public_key_info.key().len(), 65);
cert.verify_signature_against_scheme(
1586128701,
SignatureScheme::ECDSA_NISTP256_SHA256,
message,
signature,
)
.expect("OpenSSL generates syntactically valid certificates");
assert_eq!(
cert.verify_signature_against_scheme(
1586128701,
SignatureScheme::ECDSA_NISTP256_SHA256,
message,
invalid_signature,
)
.expect_err("corrupting a signature invalidates it"),
Error::InvalidSignatureForPublicKey
);
assert_eq!(
cert.verify_signature_against_scheme(
1586128701,
SignatureScheme::ECDSA_NISTP256_SHA256,
message,
invalid_signature,
)
.expect_err("corrupting a message invalidates it"),
Error::InvalidSignatureForPublicKey
);
assert_eq!(
cert.verify_signature_against_scheme(
1586128701,
SignatureScheme::ECDSA_NISTP256_SHA256,
forged_message,
signature,
)
.expect_err("forgery undetected?"),
Error::InvalidSignatureForPublicKey
);
}
}