use core::ops::Deref;
use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm};
use crate::error::Error;
use crate::subject_name::{verify_dns_names, verify_ip_address_names};
use crate::{DerTypeId, cert, der, sct, signed_data};
pub struct EndEntityCert<'a> {
inner: cert::Cert<'a>,
}
impl<'a> TryFrom<&'a CertificateDer<'a>> for EndEntityCert<'a> {
type Error = Error;
fn try_from(cert: &'a CertificateDer<'a>) -> Result<Self, Self::Error> {
Ok(Self {
inner: cert::Cert::from_der(untrusted::Input::from(cert.as_ref()))?,
})
}
}
impl EndEntityCert<'_> {
pub fn verify_is_valid_for_subject_name(
&self,
server_name: &ServerName<'_>,
) -> Result<(), Error> {
match server_name {
ServerName::DnsName(dns_name) => verify_dns_names(dns_name, &self.inner),
ServerName::IpAddress(ip_address) => verify_ip_address_names(ip_address, &self.inner),
_ => Err(Error::UnsupportedNameType),
}
}
pub fn verify_signature(
&self,
signature_alg: &dyn SignatureVerificationAlgorithm,
msg: &[u8],
signature: &[u8],
) -> Result<(), Error> {
if let Some(key_usage) = self.inner.key_usage {
check_key_usage_digital_signature(key_usage)?;
}
signed_data::verify_signature(
signature_alg,
self.inner.spki,
untrusted::Input::from(msg),
untrusted::Input::from(signature),
)
}
pub fn sct_log_timestamps<'a>(
&'a self,
) -> Result<impl Iterator<Item = Result<sct::LogIdAndTimestamp, sct::Error>> + 'a, sct::Error>
{
Ok(sct::SctParser::new(self.scts)?.map(|sct| sct.map(|sct| sct.log_id_and_timestamp())))
}
}
fn check_key_usage_digital_signature(key_usage: untrusted::Input<'_>) -> Result<(), Error> {
let bit_string = key_usage.read_all(Error::TrailingData(DerTypeId::BitString), |reader| {
der::expect_tag(reader, der::Tag::BitString)
})?;
const DIGITAL_SIGNATURE: usize = 0;
match der::bit_string_flags(bit_string)?.bit_set(DIGITAL_SIGNATURE) {
true => Ok(()),
false => Err(Error::KeyUsageMissingDigitalSignature),
}
}
impl<'a> Deref for EndEntityCert<'a> {
type Target = cert::Cert<'a>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::*;
use crate::test_utils;
use crate::test_utils::RCGEN_SIGNATURE_ALG;
#[test]
fn printable_string_common_name() {
const DNS_NAME: &str = "test.example.com";
let issuer = test_utils::make_issuer("Test");
let ee_cert = {
let mut params = test_utils::end_entity_params(vec![DNS_NAME.to_string()]);
params.distinguished_name.push(
rcgen::DnType::CommonName,
rcgen::DnValue::PrintableString(
rcgen::string::PrintableString::try_from("example.com").unwrap(),
),
);
params
.signed_by(
&rcgen::KeyPair::generate_for(RCGEN_SIGNATURE_ALG).unwrap(),
&issuer,
)
.expect("failed to make ee cert (this is a test bug)")
};
expect_dns_name(ee_cert.der(), DNS_NAME);
}
#[test]
fn empty_sequence_common_name() {
let ee_cert_der = {
let bytes = include_bytes!("../tests/misc/empty_sequence_common_name.der");
CertificateDer::from(&bytes[..])
};
expect_dns_name(&ee_cert_der, "example.com");
}
fn expect_dns_name(der: &CertificateDer<'_>, name: &str) {
let cert =
EndEntityCert::try_from(der).expect("should parse end entity certificate correctly");
let mut names = cert.valid_dns_names();
assert_eq!(names.next(), Some(name));
assert_eq!(names.next(), None);
}
}