use alloc::vec::Vec;
use core::fmt;
use pki_types::{
CertificateDer, ServerName, SignatureVerificationAlgorithm, SubjectPublicKeyInfoDer, UnixTime,
};
use super::anchors::RootCertStore;
use super::pki_error;
use crate::enums::SignatureScheme;
use crate::error::{Error, PeerMisbehaved};
use crate::verify::{DigitallySignedStruct, HandshakeSignatureValid};
#[allow(dead_code)]
pub fn verify_server_cert_signed_by_trust_anchor(
cert: &ParsedCertificate<'_>,
roots: &RootCertStore,
intermediates: &[CertificateDer<'_>],
now: UnixTime,
supported_algs: &[&dyn SignatureVerificationAlgorithm],
) -> Result<(), Error> {
verify_server_cert_signed_by_trust_anchor_impl(
cert,
roots,
intermediates,
None, now,
supported_algs,
)
}
pub fn verify_server_name(
cert: &ParsedCertificate<'_>,
server_name: &ServerName<'_>,
) -> Result<(), Error> {
cert.0
.verify_is_valid_for_subject_name(server_name)
.map_err(pki_error)
}
#[derive(Clone, Copy)]
#[allow(unreachable_pub)]
pub struct WebPkiSupportedAlgorithms {
pub all: &'static [&'static dyn SignatureVerificationAlgorithm],
pub mapping: &'static [(
SignatureScheme,
&'static [&'static dyn SignatureVerificationAlgorithm],
)],
}
impl WebPkiSupportedAlgorithms {
pub fn supported_schemes(&self) -> Vec<SignatureScheme> {
self.mapping
.iter()
.map(|item| item.0)
.collect()
}
fn convert_scheme(
&self,
scheme: SignatureScheme,
) -> Result<&[&'static dyn SignatureVerificationAlgorithm], Error> {
self.mapping
.iter()
.filter_map(|item| if item.0 == scheme { Some(item.1) } else { None })
.next()
.ok_or_else(|| PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into())
}
pub fn fips(&self) -> bool {
self.all.iter().all(|alg| alg.fips())
&& self
.mapping
.iter()
.all(|item| item.1.iter().all(|alg| alg.fips()))
}
}
impl fmt::Debug for WebPkiSupportedAlgorithms {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: ")?;
f.debug_list()
.entries(self.mapping.iter().map(|item| item.0))
.finish()?;
write!(f, " }}")
}
}
pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>);
impl ParsedCertificate<'_> {
pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> {
self.0.subject_public_key_info()
}
}
impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> {
type Error = Error;
fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> {
webpki::EndEntityCert::try_from(value)
.map_err(pki_error)
.map(ParsedCertificate)
}
}
pub fn verify_tls12_signature(
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
supported_schemes: &WebPkiSupportedAlgorithms,
) -> Result<HandshakeSignatureValid, Error> {
let possible_algs = supported_schemes.convert_scheme(dss.scheme)?;
let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
let mut error = None;
for alg in possible_algs {
match cert.verify_signature(*alg, message, dss.signature()) {
Err(err @ webpki::Error::UnsupportedSignatureAlgorithmForPublicKeyContext(_)) => {
error = Some(err);
continue;
}
Err(e) => return Err(pki_error(e)),
Ok(()) => return Ok(HandshakeSignatureValid::assertion()),
}
}
#[allow(deprecated)] Err(pki_error(error.unwrap_or(
webpki::Error::UnsupportedSignatureAlgorithmForPublicKey,
)))
}
pub fn verify_tls13_signature(
msg: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
supported_schemes: &WebPkiSupportedAlgorithms,
) -> Result<HandshakeSignatureValid, Error> {
if !dss.scheme.supported_in_tls13() {
return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
}
let alg = supported_schemes.convert_scheme(dss.scheme)?[0];
let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?;
cert.verify_signature(alg, msg, dss.signature())
.map_err(pki_error)
.map(|_| HandshakeSignatureValid::assertion())
}
pub fn verify_tls13_signature_with_raw_key(
msg: &[u8],
spki: &SubjectPublicKeyInfoDer<'_>,
dss: &DigitallySignedStruct,
supported_schemes: &WebPkiSupportedAlgorithms,
) -> Result<HandshakeSignatureValid, Error> {
if !dss.scheme.supported_in_tls13() {
return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into());
}
let raw_key = webpki::RawPublicKeyEntity::try_from(spki).map_err(pki_error)?;
let alg = supported_schemes.convert_scheme(dss.scheme)?[0];
raw_key
.verify_signature(alg, msg, dss.signature())
.map_err(pki_error)
.map(|_| HandshakeSignatureValid::assertion())
}
pub(crate) fn verify_server_cert_signed_by_trust_anchor_impl(
cert: &ParsedCertificate<'_>,
roots: &RootCertStore,
intermediates: &[CertificateDer<'_>],
revocation: Option<webpki::RevocationOptions<'_>>,
now: UnixTime,
supported_algs: &[&dyn SignatureVerificationAlgorithm],
) -> Result<(), Error> {
let result = cert.0.verify_for_usage(
supported_algs,
&roots.roots,
intermediates,
now,
webpki::KeyUsage::server_auth(),
revocation,
None,
);
match result {
Ok(_) => Ok(()),
Err(e) => Err(pki_error(e)),
}
}
#[cfg(test)]
mod tests {
use std::format;
use super::*;
#[test]
fn certificate_debug() {
assert_eq!(
"CertificateDer(0x6162)",
format!("{:?}", CertificateDer::from(b"ab".to_vec()))
);
}
#[cfg(feature = "ring")]
#[test]
fn webpki_supported_algorithms_is_debug() {
assert_eq!(
"WebPkiSupportedAlgorithms { all: [ .. ], mapping: [ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256, ED25519, RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, RSA_PKCS1_SHA384, RSA_PKCS1_SHA256] }",
format!(
"{:?}",
crate::crypto::ring::default_provider().signature_verification_algorithms
)
);
}
}