use std::fmt::Debug;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::OnceLock;
use crate::buffer::{Buf, TmpBuf};
use crate::crypto::{Aad, Nonce};
use crate::dtls12::message::Dtls12CipherSuite;
use crate::types::{Dtls13CipherSuite, HashAlgorithm, NamedGroup, SignatureAlgorithm};
#[cfg(feature = "_crypto-common")]
pub const OID_P256: spki::ObjectIdentifier =
spki::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
#[cfg(feature = "_crypto-common")]
pub const OID_P384: spki::ObjectIdentifier = spki::ObjectIdentifier::new_unwrap("1.3.132.0.34");
pub trait CryptoSafe: Send + Sync + Debug + UnwindSafe + RefUnwindSafe {}
impl<T: Send + Sync + Debug + UnwindSafe + RefUnwindSafe> CryptoSafe for T {}
pub trait Cipher: CryptoSafe {
fn encrypt(&mut self, plaintext: &mut Buf, aad: Aad, nonce: Nonce) -> Result<(), String>;
fn decrypt(&mut self, ciphertext: &mut TmpBuf, aad: Aad, nonce: Nonce) -> Result<(), String>;
}
pub trait HashContext: CryptoSafe {
fn update(&mut self, data: &[u8]);
fn clone_and_finalize(&self, out: &mut Buf);
}
pub trait SigningKey: CryptoSafe {
fn sign(&mut self, data: &[u8], hash_alg: HashAlgorithm, out: &mut Buf) -> Result<(), String>;
fn algorithm(&self) -> SignatureAlgorithm;
fn hash_algorithm(&self) -> HashAlgorithm;
fn supported_hash_algorithms(&self) -> &[HashAlgorithm];
}
pub trait ActiveKeyExchange: CryptoSafe {
fn pub_key(&self) -> &[u8];
fn complete(self: Box<Self>, peer_pub: &[u8], out: &mut Buf) -> Result<(), String>;
fn group(&self) -> NamedGroup;
}
pub trait SupportedDtls12CipherSuite: CryptoSafe {
fn suite(&self) -> Dtls12CipherSuite;
fn hash_algorithm(&self) -> HashAlgorithm;
fn key_lengths(&self) -> (usize, usize, usize);
fn explicit_nonce_len(&self) -> usize;
fn tag_len(&self) -> usize;
fn min_protected_fragment_len(&self) -> usize {
self.explicit_nonce_len() + self.tag_len()
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String>;
}
pub trait SupportedKxGroup: CryptoSafe {
fn name(&self) -> NamedGroup;
fn start_exchange(&self, buf: Buf) -> Result<Box<dyn ActiveKeyExchange>, String>;
}
pub trait SignatureVerifier: CryptoSafe {
fn verify_signature(
&self,
cert_der: &[u8],
data: &[u8],
signature: &[u8],
hash_alg: HashAlgorithm,
sig_alg: SignatureAlgorithm,
) -> Result<(), String>;
}
const SUPPORTED_VERIFY_SCHEMES: &[(SignatureAlgorithm, HashAlgorithm, NamedGroup)] = &[
(
SignatureAlgorithm::ECDSA,
HashAlgorithm::SHA256,
NamedGroup::Secp256r1,
),
(
SignatureAlgorithm::ECDSA,
HashAlgorithm::SHA256,
NamedGroup::Secp384r1,
),
(
SignatureAlgorithm::ECDSA,
HashAlgorithm::SHA384,
NamedGroup::Secp256r1,
),
(
SignatureAlgorithm::ECDSA,
HashAlgorithm::SHA384,
NamedGroup::Secp384r1,
),
];
pub fn check_verify_scheme(
sig_alg: SignatureAlgorithm,
hash_alg: HashAlgorithm,
group: NamedGroup,
) -> Result<(), String> {
if SUPPORTED_VERIFY_SCHEMES
.iter()
.any(|(s, h, g)| *s == sig_alg && *h == hash_alg && *g == group)
{
Ok(())
} else {
Err(format!(
"Unsupported signature verification: {:?} + {:?} + {:?}",
sig_alg, hash_alg, group
))
}
}
#[cfg(feature = "_crypto-common")]
pub fn cert_named_group(cert_der: &[u8]) -> Result<NamedGroup, String> {
use der::Decode;
use spki::ObjectIdentifier;
use x509_cert::Certificate as X509Certificate;
let cert = X509Certificate::from_der(cert_der)
.map_err(|e| format!("Failed to parse certificate: {e}"))?;
let spki = &cert.tbs_certificate.subject_public_key_info;
let curve_oid: ObjectIdentifier = spki
.algorithm
.parameters
.as_ref()
.ok_or("Missing EC curve parameter in certificate")?
.decode_as()
.map_err(|_| "Invalid EC curve parameter in certificate".to_string())?;
match curve_oid {
OID_P256 => Ok(NamedGroup::Secp256r1),
OID_P384 => Ok(NamedGroup::Secp384r1),
_ => Err(format!("Unsupported EC curve: {}", curve_oid)),
}
}
pub trait KeyProvider: CryptoSafe {
fn load_private_key(&self, key_der: &[u8]) -> Result<Box<dyn SigningKey>, String>;
}
pub trait SecureRandom: CryptoSafe {
fn fill(&self, buf: &mut [u8]) -> Result<(), String>;
}
pub trait HashProvider: CryptoSafe {
fn create_hash(&self, algorithm: HashAlgorithm) -> Box<dyn HashContext>;
}
pub trait HmacProvider: CryptoSafe {
fn hmac_sha256(&self, key: &[u8], data: &[u8]) -> Result<[u8; 32], String> {
let mut out = [0u8; 32];
self.hmac(HashAlgorithm::SHA256, key, data, &mut out)?;
Ok(out)
}
fn hmac(
&self,
hash: HashAlgorithm,
key: &[u8],
data: &[u8],
out: &mut [u8],
) -> Result<usize, String>;
}
pub trait SupportedDtls13CipherSuite: CryptoSafe {
fn suite(&self) -> Dtls13CipherSuite;
fn hash_algorithm(&self) -> HashAlgorithm;
fn key_len(&self) -> usize;
fn iv_len(&self) -> usize;
fn tag_len(&self) -> usize;
fn min_protected_fragment_len(&self) -> usize {
self.tag_len()
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String>;
fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16];
}
#[derive(Debug, Clone)]
pub struct CryptoProvider {
pub kx_groups: &'static [&'static dyn SupportedKxGroup],
pub signature_verification: &'static dyn SignatureVerifier,
pub key_provider: &'static dyn KeyProvider,
pub secure_random: &'static dyn SecureRandom,
pub hash_provider: &'static dyn HashProvider,
pub hmac_provider: &'static dyn HmacProvider,
pub cipher_suites: &'static [&'static dyn SupportedDtls12CipherSuite],
pub dtls13_cipher_suites: &'static [&'static dyn SupportedDtls13CipherSuite],
}
static DEFAULT: OnceLock<CryptoProvider> = OnceLock::new();
impl CryptoProvider {
pub fn install_default(provider: CryptoProvider) {
DEFAULT
.set(provider)
.expect("CryptoProvider::install_default() called more than once");
}
pub fn get_default() -> Option<&'static CryptoProvider> {
DEFAULT.get()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "rcgen")]
fn cert_named_group_p256() {
use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P256_SHA256};
let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap();
let params = CertificateParams::new(Vec::<String>::new()).unwrap();
let cert = params.self_signed(&key_pair).unwrap();
let group = cert_named_group(cert.der()).unwrap();
assert_eq!(group, NamedGroup::Secp256r1);
}
#[test]
#[cfg(feature = "rcgen")]
fn cert_named_group_p384() {
use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P384_SHA384};
let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
let params = CertificateParams::new(Vec::<String>::new()).unwrap();
let cert = params.self_signed(&key_pair).unwrap();
let group = cert_named_group(cert.der()).unwrap();
assert_eq!(group, NamedGroup::Secp384r1);
}
#[test]
#[cfg(feature = "rcgen")]
fn cert_named_group_invalid_der() {
let result = cert_named_group(&[0x00, 0x01, 0x02]);
assert!(result.is_err());
}
}