use crate::ec::CurveId;
use crate::hash::{Sha256, Sha384, Sha512};
use crate::rng::RngCore;
use crate::signature_registry::{SignaturePolicy, find_by_tls_scheme};
use crate::tls::Error;
use crate::tls::codec::SignatureScheme;
use crate::tls::conn::ServerKey;
use crate::x509::{AnyPublicKey, Error as X509Error};
use alloc::vec::Vec;
const SIG_PREFIX: [u8; 64] = [0x20; 64];
pub(crate) fn certificate_verify_content(server: bool, transcript_hash: &[u8]) -> Vec<u8> {
let context: &[u8] = if server {
b"TLS 1.3, server CertificateVerify"
} else {
b"TLS 1.3, client CertificateVerify"
};
let mut out = Vec::with_capacity(SIG_PREFIX.len() + context.len() + 1 + transcript_hash.len());
out.extend_from_slice(&SIG_PREFIX);
out.extend_from_slice(context);
out.push(0);
out.extend_from_slice(transcript_hash);
out
}
pub(crate) fn signature_scheme_for(key: &ServerKey) -> SignatureScheme {
match key {
ServerKey::Rsa(_) => SignatureScheme::RSA_PSS_RSAE_SHA256,
ServerKey::Ecdsa(k) => match k.curve() {
CurveId::P256 => SignatureScheme::ECDSA_SECP256R1_SHA256,
CurveId::P384 => SignatureScheme::ECDSA_SECP384R1_SHA384,
CurveId::P521 => SignatureScheme::ECDSA_SECP521R1_SHA512,
CurveId::Secp256k1 => SignatureScheme::ECDSA_SECP256R1_SHA256,
},
ServerKey::Ed25519(_) => SignatureScheme::ED25519,
ServerKey::MlDsa44(_) => SignatureScheme::MLDSA44,
ServerKey::MlDsa65(_) => SignatureScheme::MLDSA65,
ServerKey::MlDsa87(_) => SignatureScheme::MLDSA87,
}
}
pub(crate) fn sign_certificate_verify<R: RngCore>(
key: &ServerKey,
content: &[u8],
rng: &mut R,
) -> Result<(SignatureScheme, Vec<u8>), Error> {
let scheme = signature_scheme_for(key);
let signature = match key {
ServerKey::Rsa(k) => k
.sign_pss::<Sha256, _>(content, rng)
.map_err(|_| Error::HandshakeFailure)?,
ServerKey::Ecdsa(k) => {
let curve = k.curve();
let sig = match curve {
CurveId::P384 => k.sign::<Sha384>(content),
CurveId::P521 => k.sign::<Sha512>(content),
_ => k.sign::<Sha256>(content),
}
.map_err(|_| Error::HandshakeFailure)?;
sig.to_der(curve)
}
ServerKey::Ed25519(k) => k.sign(content).to_bytes().to_vec(),
ServerKey::MlDsa44(k) => k
.sign(rng, content, b"")
.map_err(|_| Error::HandshakeFailure)?,
ServerKey::MlDsa65(k) => k
.sign(rng, content, b"")
.map_err(|_| Error::HandshakeFailure)?,
ServerKey::MlDsa87(k) => k
.sign(rng, content, b"")
.map_err(|_| Error::HandshakeFailure)?,
};
Ok((scheme, signature))
}
pub(crate) fn verify_signature(
scheme: SignatureScheme,
key: &AnyPublicKey,
message: &[u8],
signature: &[u8],
policy: &SignaturePolicy,
) -> Result<(), Error> {
let algo = find_by_tls_scheme(scheme.0).ok_or(Error::PeerMisbehaved)?;
let spki = key.to_spki_der();
if !policy.permits(algo, &spki) {
return Err(Error::BadCertificate);
}
match algo.verify(&spki, message, signature) {
Ok(()) => Ok(()),
Err(X509Error::UnsupportedAlgorithm) => Err(Error::PeerMisbehaved),
Err(X509Error::Malformed) => Err(Error::Decode),
Err(_) => Err(Error::BadCertificate),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::{Digest, Sha256};
use crate::test_util::from_hex_vec;
use crate::x509::Certificate;
#[test]
fn rfc8448_certificate_verify() {
let ch = from_hex_vec(include_str!("../../../testdata/rfc8448_client_hello.hex"));
let sh = from_hex_vec(include_str!("../../../testdata/rfc8448_server_hello.hex"));
let flight = from_hex_vec(include_str!(
"../../../testdata/rfc8448_server_flight_payload.hex"
));
let cert = Certificate::from_der(flight[51..483].to_vec()).unwrap();
let key = cert.subject_public_key().unwrap();
let mut transcript = Vec::new();
transcript.extend_from_slice(&ch);
transcript.extend_from_slice(&sh);
transcript.extend_from_slice(&flight[0..485]);
let th = Sha256::digest(&transcript);
let content = certificate_verify_content(true, th.as_ref());
let scheme = SignatureScheme(u16::from_be_bytes([flight[489], flight[490]]));
assert_eq!(scheme, SignatureScheme::RSA_PSS_RSAE_SHA256);
let sig = &flight[493..621];
let policy = SignaturePolicy::modern().with_min_rsa_bits(1024);
verify_signature(scheme, &key, &content, sig, &policy).unwrap();
let mut bad = content.clone();
*bad.last_mut().unwrap() ^= 0x01;
assert!(matches!(
verify_signature(scheme, &key, &bad, sig, &policy),
Err(Error::BadCertificate)
));
assert!(matches!(
verify_signature(
SignatureScheme::ECDSA_SECP256R1_SHA256,
&key,
&content,
sig,
&policy,
),
Err(Error::PeerMisbehaved)
));
}
}