#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![deny(
const_err,
deprecated,
improper_ctypes,
non_shorthand_field_patterns,
nonstandard_style,
no_mangle_generic_items,
renamed_and_removed_lints,
unknown_lints,
type_alias_bounds,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
rust_2018_idioms,
unused,
future_incompatible,
clippy::all
)]
#![forbid(
unconditional_recursion,
unsafe_code,
intra_doc_link_resolution_failure,
while_true,
elided_lifetimes_in_paths
)]
mod das;
mod sequence;
mod time;
use ring::io::der;
mod spki;
pub use das::DataAlgorithmSignature;
pub use sequence::{ExtensionIterator, SequenceIterator};
pub use spki::{parse_algorithmid, Restrictions, SubjectPublicKeyInfo};
pub use time::{days_from_ymd, seconds_from_hms, ASN1Time, MAX_ASN1_TIMESTAMP, MIN_ASN1_TIMESTAMP};
#[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,
UnknownIssuer,
}
#[cfg(feature = "webpki")]
pub use w::Error;
#[derive(Debug)]
pub struct X509Certificate<'a> {
das: DataAlgorithmSignature<'a>,
serial: &'a [u8],
issuer: &'a [u8],
not_before: ASN1Time,
not_after: ASN1Time,
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) -> ASN1Time { self.not_before }
pub fn not_after(&self) -> ASN1Time { 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 check_signature(
&self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
) -> Result<(), Error> {
self.subject_public_key_info.check_signature(
algorithm,
message,
signature,
Restrictions::None,
)
}
pub fn check_tls13_signature(
&self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
) -> Result<(), Error> {
self.subject_public_key_info.check_signature(
algorithm,
message,
signature,
Restrictions::TLSv13,
)
}
pub fn check_tls12_signature(
&self, algorithm: SignatureScheme, message: &[u8], signature: &[u8],
) -> Result<(), Error> {
self.subject_public_key_info.check_signature(
algorithm,
message,
signature,
Restrictions::TLSv12,
)
}
pub fn valid_at_timestamp(&self, now: i64) -> Result<(), Error> {
if now < self.not_before.into() {
Err(Error::CertNotValidYet)
} else if now > self.not_after.into() {
Err(Error::CertExpired)
} else {
Ok(())
}
}
#[cfg(feature = "std")]
pub fn valid(&self) -> Result<(), Error> { self.valid_at_timestamp(ASN1Time::now()?.into()) }
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 check_signature_from(&self, cert: &X509Certificate<'_>) -> Result<(), Error> {
cert.check_signature(
parse_algorithmid(self.signature_algorithm_id())?,
self.tbs_certificate(),
self.signature(),
)
}
pub fn check_issued_by(&self, cert: &X509Certificate<'_>) -> Result<(), Error> {
if self.issuer != cert.subject {
return Err(Error::UnknownIssuer);
}
self.check_signature_from(cert)
}
#[deprecated(since = "0.3.3", note = "Use check_self_issued instead")]
pub fn check_self_signature(&self) -> Result<(), Error> { self.check_signature_from(self) }
pub fn check_self_issued(&self) -> Result<(), Error> { self.check_issued_by(self) }
}
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");
#[cfg(feature = "rsa")]
let ca_certificate = include_bytes!("../ca.crt");
let cert = parse_certificate(certificate).unwrap();
#[cfg(feature = "rsa")]
let ca_cert = parse_certificate(ca_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.valid_at_timestamp(1587492766).unwrap();
assert_eq!(cert.valid_at_timestamp(0), Err(Error::CertNotValidYet));
assert_eq!(
cert.valid_at_timestamp(i64::max_value()),
Err(Error::CertExpired)
);
cert.check_signature(SignatureScheme::ECDSA_NISTP256_SHA256, message, signature)
.expect("OpenSSL generates syntactically valid certificates");
assert_eq!(
cert.check_signature(
SignatureScheme::ECDSA_NISTP256_SHA256,
message,
invalid_signature,
)
.expect_err("corrupting a signature invalidates it"),
Error::InvalidSignatureForPublicKey
);
assert_eq!(
cert.check_signature(
SignatureScheme::ECDSA_NISTP256_SHA256,
message,
invalid_signature,
)
.expect_err("corrupting a message invalidates it"),
Error::InvalidSignatureForPublicKey
);
assert_eq!(
cert.check_signature(
SignatureScheme::ECDSA_NISTP256_SHA256,
forged_message,
signature,
)
.expect_err("forgery undetected?"),
Error::InvalidSignatureForPublicKey
);
#[cfg(feature = "rsa")]
ca_cert.check_self_issued().unwrap();
#[cfg(feature = "rsa")]
cert.check_issued_by(&ca_cert).unwrap();
}
}