251 lines
10 KiB
Rust
251 lines
10 KiB
Rust
//! [`WolfSslProvider`] — the main provider struct implementing
|
|
//! [`CryptoProvider`] for the wolfSSL / wolfCrypt library.
|
|
//!
|
|
//! Capabilities are live-probed on first construction. Benchmark results
|
|
//! feed into the `efficiency_score` fields of [`ProviderCapabilities`].
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
use zeroize::Zeroizing;
|
|
|
|
use ccc_crypto_core::{
|
|
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
|
|
capabilities::ProviderCapabilities,
|
|
error::CryptoError,
|
|
provider::{
|
|
AeadProvider, CryptoProvider, HashProvider, KdfProvider, KemProvider, MacProvider,
|
|
},
|
|
types::{AlgoTestResult, BenchmarkReport, KemEncapResult, KemKeyPair, SelfTestReport},
|
|
};
|
|
|
|
use crate::{aead, capabilities, hash, kdf, kem, mac};
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// Embedded NIST / RFC test vectors for self_test()
|
|
// These are checked at runtime; failures gate the provider from being marked
|
|
// `available` in the capability catalog.
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
struct AeadVector {
|
|
algo: AeadAlgorithm,
|
|
key: &'static str,
|
|
nonce: &'static str,
|
|
aad: &'static str,
|
|
pt: &'static str,
|
|
ct_tag: &'static str, // ciphertext || tag, hex-encoded
|
|
}
|
|
|
|
/// NIST SP 800-38D and RFC 8439 test vectors.
|
|
static AEAD_VECTORS: &[AeadVector] = &[
|
|
// ── AES-256-GCM: NIST CAVS test case GCM-256/96/128 (test case 1) ───────
|
|
AeadVector {
|
|
algo: AeadAlgorithm::AesGcm256,
|
|
key: "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
|
|
nonce: "cafebabefacedbaddecaf888",
|
|
aad: "",
|
|
pt: "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72\
|
|
1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
|
|
ct_tag: "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa\
|
|
8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
|
|
},
|
|
// ── ChaCha20-Poly1305: RFC 8439 §2.8.2 test vector ──────────────────────
|
|
AeadVector {
|
|
algo: AeadAlgorithm::ChaCha20Poly1305,
|
|
key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
|
|
nonce: "070000004041424344454647",
|
|
aad: "50515253c0c1c2c3c4c5c6c7",
|
|
pt: "4c616469657320616e642047656e746c656d656e206f662074686520636c617373\
|
|
206f6620273939",
|
|
ct_tag: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63\
|
|
dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b369\
|
|
2ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3\
|
|
ff4def08e4b7a9de576d26586cec64b6116",
|
|
},
|
|
];
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// WolfSslProvider
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
/// The wolfSSL / wolfCrypt crypto provider.
|
|
///
|
|
/// Constructed by [`crate::init()`] and registered into the global
|
|
/// [`ProviderRegistry`][ccc_crypto_core::registry::ProviderRegistry].
|
|
///
|
|
/// Capabilities and benchmark results are computed lazily on first access to
|
|
/// avoid slowing down startup if the provider is never queried.
|
|
pub struct WolfSslProvider {
|
|
/// Lazy-initialised capabilities + benchmark results.
|
|
caps: OnceLock<ProviderCapabilities>,
|
|
bench: OnceLock<BenchmarkReport>,
|
|
}
|
|
|
|
impl WolfSslProvider {
|
|
/// Create a new wolfSSL provider.
|
|
///
|
|
/// Does not run probes or benchmarks at construction time — those are
|
|
/// deferred to the first call to [`capabilities()`] / [`benchmark()`].
|
|
pub fn new() -> Self {
|
|
Self {
|
|
caps: OnceLock::new(),
|
|
bench: OnceLock::new(),
|
|
}
|
|
}
|
|
|
|
/// Return the cached benchmark report, running the benchmark if needed.
|
|
fn get_or_run_benchmark(&self) -> &BenchmarkReport {
|
|
self.bench.get_or_init(|| {
|
|
log::info!("[ccc-crypto-wolfssl] running throughput benchmark…");
|
|
let report = capabilities::run_benchmark();
|
|
log::info!("[ccc-crypto-wolfssl] benchmark complete ({} algos)", report.results.len());
|
|
report
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Default for WolfSslProvider {
|
|
fn default() -> Self { Self::new() }
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// Primitive trait implementations — thin delegation to module functions
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
impl AeadProvider for WolfSslProvider {
|
|
fn encrypt_aead(
|
|
&self, algo: AeadAlgorithm, key: &[u8], nonce: &[u8],
|
|
plaintext: &[u8], aad: &[u8],
|
|
) -> Result<Vec<u8>, CryptoError> {
|
|
aead::encrypt(algo, key, nonce, plaintext, aad)
|
|
}
|
|
|
|
fn decrypt_aead(
|
|
&self, algo: AeadAlgorithm, key: &[u8], nonce: &[u8],
|
|
ciphertext_and_tag: &[u8], aad: &[u8],
|
|
) -> Result<Vec<u8>, CryptoError> {
|
|
aead::decrypt(algo, key, nonce, ciphertext_and_tag, aad)
|
|
}
|
|
}
|
|
|
|
impl KdfProvider for WolfSslProvider {
|
|
fn derive_key(
|
|
&self, algo: KdfAlgorithm, ikm: &[u8], salt: &[u8], info: &[u8], length: usize,
|
|
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
|
kdf::derive_key(algo, ikm, salt, info, length)
|
|
}
|
|
}
|
|
|
|
impl MacProvider for WolfSslProvider {
|
|
fn compute_mac(
|
|
&self, algo: MacAlgorithm, key: &[u8], data: &[u8],
|
|
) -> Result<Vec<u8>, CryptoError> {
|
|
mac::compute_mac(algo, key, data)
|
|
}
|
|
|
|
fn verify_mac(
|
|
&self, algo: MacAlgorithm, key: &[u8], data: &[u8], mac_bytes: &[u8],
|
|
) -> Result<bool, CryptoError> {
|
|
mac::verify_mac(algo, key, data, mac_bytes)
|
|
}
|
|
}
|
|
|
|
impl HashProvider for WolfSslProvider {
|
|
fn hash(&self, algo: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|
hash::hash(algo, data)
|
|
}
|
|
}
|
|
|
|
/// `WolfSslProvider` also implements `KemProvider` but it is not part of the
|
|
/// `CryptoProvider` supertrait. Bridge functions call it directly.
|
|
impl KemProvider for WolfSslProvider {
|
|
fn generate_keypair(&self, algo: KemAlgorithm) -> Result<KemKeyPair, CryptoError> {
|
|
kem::generate_keypair(algo)
|
|
}
|
|
fn encapsulate(
|
|
&self, algo: KemAlgorithm, public_key: &[u8],
|
|
) -> Result<KemEncapResult, CryptoError> {
|
|
kem::encapsulate(algo, public_key)
|
|
}
|
|
fn decapsulate(
|
|
&self, algo: KemAlgorithm, private_key: &[u8], ciphertext: &[u8],
|
|
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
|
kem::decapsulate(algo, private_key, ciphertext)
|
|
}
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// CryptoProvider umbrella impl
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
impl CryptoProvider for WolfSslProvider {
|
|
fn provider_name(&self) -> &'static str { "wolfssl" }
|
|
|
|
fn capabilities(&self) -> ProviderCapabilities {
|
|
self.caps.get_or_init(|| {
|
|
let bench = self.get_or_run_benchmark();
|
|
capabilities::probe_capabilities(Some(bench))
|
|
}).clone()
|
|
}
|
|
|
|
fn self_test(&self) -> SelfTestReport {
|
|
let mut results: Vec<AlgoTestResult> = Vec::new();
|
|
|
|
// Run AEAD test vectors.
|
|
for v in AEAD_VECTORS {
|
|
let result = run_aead_vector(v);
|
|
results.push(result);
|
|
}
|
|
|
|
SelfTestReport::finalise("wolfssl", results)
|
|
}
|
|
|
|
fn benchmark(&self) -> BenchmarkReport {
|
|
self.get_or_run_benchmark().clone()
|
|
}
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// Test vector runner
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
fn run_aead_vector(v: &AeadVector) -> AlgoTestResult {
|
|
let test_name = format!("{} NIST/RFC vector", v.algo.name());
|
|
|
|
let decode = |hex: &str| -> Vec<u8> {
|
|
(0..hex.len())
|
|
.step_by(2)
|
|
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap_or(0))
|
|
.collect()
|
|
};
|
|
|
|
let key = decode(v.key);
|
|
let nonce = decode(v.nonce);
|
|
let aad = decode(v.aad);
|
|
let pt = decode(v.pt);
|
|
let expected = decode(v.ct_tag);
|
|
|
|
match aead::encrypt(v.algo, &key, &nonce, &pt, &aad) {
|
|
Ok(ct_tag) if ct_tag == expected => AlgoTestResult {
|
|
algo_id: v.algo as u32,
|
|
algo_name: test_name,
|
|
passed: true,
|
|
error_message: None,
|
|
},
|
|
Ok(ct_tag) => AlgoTestResult {
|
|
algo_id: v.algo as u32,
|
|
algo_name: test_name.clone(),
|
|
passed: false,
|
|
error_message: Some(format!(
|
|
"output mismatch: got {} bytes, expected {} bytes",
|
|
ct_tag.len(), expected.len()
|
|
)),
|
|
},
|
|
Err(e) => AlgoTestResult {
|
|
algo_id: v.algo as u32,
|
|
algo_name: test_name,
|
|
passed: false,
|
|
error_message: Some(e.to_string()),
|
|
},
|
|
}
|
|
}
|