use boring::{error::ErrorStack, hash::MessageDigest};
use rustls::{SignatureScheme, pki_types::alg_id};
use rustls_pki_types::{InvalidSignature, SignatureVerificationAlgorithm};
use crate::helper;
#[derive(Debug)]
pub struct BoringEcVerifier(SignatureScheme);
impl BoringEcVerifier {
pub const ECDSA_NISTP256_SHA256: Self = Self(SignatureScheme::ECDSA_NISTP256_SHA256);
pub const ECDSA_NISTP384_SHA384: Self = Self(SignatureScheme::ECDSA_NISTP384_SHA384);
pub const ECDSA_NISTP521_SHA512: Self = Self(SignatureScheme::ECDSA_NISTP521_SHA512);
}
impl SignatureVerificationAlgorithm for BoringEcVerifier {
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), rustls_pki_types::InvalidSignature> {
let (group, mut bn_ctx) = setup_ec_key(self.0)
.map_err(|e| helper::log_and_map("setup_ec_key", e, InvalidSignature))?;
let ec_point = get_ec_point(group.as_ref(), bn_ctx.as_mut(), public_key)
.map_err(|e| helper::log_and_map("ec_point", e, InvalidSignature))?;
let public_key = create_public_key(group.as_ref(), ec_point.as_ref())
.map_err(|e| helper::log_and_map("ec_public_key", e, InvalidSignature))?;
let mut verifier = match self.0 {
SignatureScheme::ECDSA_NISTP256_SHA256 => {
ec_verifier_from_params(public_key.as_ref(), MessageDigest::sha256())
}
SignatureScheme::ECDSA_NISTP384_SHA384 => {
ec_verifier_from_params(public_key.as_ref(), MessageDigest::sha384())
}
SignatureScheme::ECDSA_NISTP521_SHA512 => {
ec_verifier_from_params(public_key.as_ref(), MessageDigest::sha512())
}
_ => return Err(InvalidSignature),
}
.map_err(|e| helper::log_and_map("ec_verifier_from_params", e, InvalidSignature))?;
verifier.verify_oneshot(signature, message).map_or_else(
|e| Err(helper::log_and_map("verify_oneshot", e, InvalidSignature)),
|res| if res { Ok(()) } else { Err(InvalidSignature) },
)
}
fn public_key_alg_id(&self) -> rustls_pki_types::AlgorithmIdentifier {
match self.0 {
SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_P256,
SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_P384,
SignatureScheme::ECDSA_NISTP521_SHA512 => {
rustls_pki_types::AlgorithmIdentifier::from_slice(&[
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81,
0x04, 0x00, 0x23,
])
}
_ => unreachable!("BoringEcVerifier only supports configured ECDSA schemes"),
}
}
fn signature_alg_id(&self) -> rustls_pki_types::AlgorithmIdentifier {
match self.0 {
SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_SHA256,
SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_SHA384,
SignatureScheme::ECDSA_NISTP521_SHA512 => {
rustls_pki_types::AlgorithmIdentifier::from_slice(&[
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04,
])
}
_ => unreachable!("BoringEcVerifier only supports configured ECDSA schemes"),
}
}
fn fips(&self) -> bool {
cfg!(feature = "fips") && !matches!(self.0, SignatureScheme::ECDSA_NISTP521_SHA512)
}
}
fn ec_verifier_from_params(
key: &boring::pkey::PKeyRef<boring::pkey::Public>,
digest: MessageDigest,
) -> Result<boring::sign::Verifier<'_>, ErrorStack> {
boring::sign::Verifier::new(digest, key)
}
fn group_for_scheme(scheme: SignatureScheme) -> Result<boring::ec::EcGroup, ErrorStack> {
let nid = match scheme {
SignatureScheme::ECDSA_NISTP256_SHA256 => boring::nid::Nid::X9_62_PRIME256V1,
SignatureScheme::ECDSA_NISTP384_SHA384 => boring::nid::Nid::SECP384R1,
SignatureScheme::ECDSA_NISTP521_SHA512 => boring::nid::Nid::SECP521R1,
_ => unreachable!("BoringEcVerifier only supports configured ECDSA schemes"),
};
boring::ec::EcGroup::from_curve_name(nid)
}
fn setup_ec_key(
scheme: SignatureScheme,
) -> Result<(boring::ec::EcGroup, boring::bn::BigNumContext), ErrorStack> {
Ok((group_for_scheme(scheme)?, boring::bn::BigNumContext::new()?))
}
pub(crate) fn get_ec_point(
group: &boring::ec::EcGroupRef,
bignum_ctx: &mut boring::bn::BigNumContextRef,
spki_spk: &[u8],
) -> Result<boring::ec::EcPoint, ErrorStack> {
boring::ec::EcPoint::from_bytes(group, spki_spk, bignum_ctx)
}
pub(crate) fn create_public_key(
group: &boring::ec::EcGroupRef,
ec_point: &boring::ec::EcPointRef,
) -> Result<boring::pkey::PKey<boring::pkey::Public>, ErrorStack> {
boring::pkey::PKey::from_ec_key(boring::ec::EcKey::from_public_key(group, ec_point)?)
}