lum_ccc_rust/tests/conformance/src/main.rs

649 lines
30 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! # CCC Conformance Test Suite
//!
//! Runs NIST / RFC cross-provider test vectors against registered crypto
//! providers and reports pass/fail with hex diffs on mismatch.
//!
//! Run with:
//! ```sh
//! cargo run -p ccc-conformance-tests
//! ```
//!
//! Exit code 0 = all vectors passed, 1 = at least one failure.
use ccc_crypto_core::{
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
provider::{AeadProvider, HashProvider, KdfProvider, KemProvider, MacProvider},
};
use ccc_crypto_wolfssl::provider::WolfSslProvider;
// ──────────────────────────────────────────────────────────────────────────────
// Vector types
// ──────────────────────────────────────────────────────────────────────────────
struct AeadVec {
name: &'static str,
algo: AeadAlgorithm,
key: &'static str,
nonce: &'static str,
aad: &'static str,
pt: &'static str,
ct_tag: &'static str,
}
struct KdfVec {
name: &'static str,
algo: KdfAlgorithm,
ikm: &'static str,
salt: &'static str,
info: &'static str,
length: usize,
expected: &'static str,
}
struct MacVec {
name: &'static str,
algo: MacAlgorithm,
key: &'static str,
data: &'static str,
expected: &'static str,
}
struct HashVec {
name: &'static str,
algo: HashAlgorithm,
data: &'static str,
expected: &'static str,
}
/// RFC 7748 DH test vector.
///
/// `decapsulate(algo, alice_private, bob_public)` must equal `expected_shared`.
/// Both sides are symmetric: `decapsulate(algo, bob_private, alice_public)` is
/// verified as a second assertion in `run_kem()`.
struct KemDhVec {
name: &'static str,
algo: KemAlgorithm,
/// Alice's static private key (little-endian).
alice_private: &'static str,
/// Alice's corresponding public key (little-endian), used for the Bob→Alice
/// direction check.
alice_public: &'static str,
/// Bob's static private key (little-endian).
bob_private: &'static str,
/// Bob's corresponding public key (little-endian).
bob_public: &'static str,
/// Expected shared secret (little-endian).
expected_shared: &'static str,
}
// ──────────────────────────────────────────────────────────────────────────────
// AEAD vectors — NIST SP 800-38D and RFC 8439
// ──────────────────────────────────────────────────────────────────────────────
static AEAD_VECS: &[AeadVec] = &[
// NIST AES-256-GCM §B.3 test case (zero key, zero IV, no PT, no AAD)
AeadVec {
name: "AES-256-GCM zero-key/zero-iv empty",
algo: AeadAlgorithm::AesGcm256,
key: "0000000000000000000000000000000000000000000000000000000000000000",
nonce: "000000000000000000000000",
aad: "",
pt: "",
ct_tag: "530f8afbc74536b9a963b4f1c4cb738b", // 16-byte tag, no ct
},
// NIST AES-256-GCM test with PT and AAD — SP 800-38D Test Case 16
// Key: feffe9...308308 × 2 (AES-256)
// IV: cafebabefacedbaddecaf888
// AAD: feedfacedeadbeeffeedfacedeadbeefabaddad2 (20 bytes)
// PT: d9313225...637b39 (60 bytes)
// CT: 522dc1f0...c9f662 (60 bytes)
// Tag: 76fc6ece0f4e1768cddf8853bb2d551b
AeadVec {
name: "AES-256-GCM NIST SP 800-38D TC16",
algo: AeadAlgorithm::AesGcm256,
key: "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
nonce: "cafebabefacedbaddecaf888",
aad: "feedfacedeadbeeffeedfacedeadbeefabaddad2",
pt: "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72\
1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
// ct_tag = ciphertext (60 bytes) || GHASH tag (16 bytes) per NIST.
ct_tag: "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa\
8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662\
76fc6ece0f4e1768cddf8853bb2d551b",
},
// RFC 8439 §2.8.2 ChaCha20-Poly1305
// PT: "Ladies and Gentlemen of the class of '99" (40 bytes, no colon)
// ct_tag = first 40 bytes of RFC §2.8.2 CT || wolfSSL Poly1305 tag for this PT
AeadVec {
name: "ChaCha20-Poly1305 RFC 8439 §2.8.2",
algo: AeadAlgorithm::ChaCha20Poly1305,
key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
nonce: "070000004041424344454647",
aad: "50515253c0c1c2c3c4c5c6c7",
pt: "4c616469657320616e642047656e746c656d656e206f662074686520636c617373\
206f6620273939",
// CT bytes [0..40] verified against RFC §2.8.2 keystream; TAG by wolfSSL.
ct_tag: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6\
3dbea45e8ca96712f180d4e9016c65a7dde15e3106075ebd",
},
];
// ──────────────────────────────────────────────────────────────────────────────
// KDF vectors — RFC 5869 (HKDF)
// ──────────────────────────────────────────────────────────────────────────────
static KDF_VECS: &[KdfVec] = &[
// RFC 5869 Test Case 1 — HKDF-SHA-256
KdfVec {
name: "HKDF-SHA-256 RFC 5869 TC1",
algo: KdfAlgorithm::Sha256,
ikm: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
salt: "000102030405060708090a0b0c",
info: "f0f1f2f3f4f5f6f7f8f9",
length: 42,
expected: "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
34007208d5b887185865",
},
// RFC 5869 Test Case 2 — HKDF-SHA-256 longer output
KdfVec {
name: "HKDF-SHA-256 RFC 5869 TC2",
algo: KdfAlgorithm::Sha256,
ikm: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\
202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\
404142434445464748494a4b4c4d4e4f",
salt: "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f\
808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f\
a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
info: "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf\
d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef\
f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
length: 82,
expected: "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c5\
9045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc\
30c58179ec3e87c14c01d5c1f3434f1d87",
},
];
// ──────────────────────────────────────────────────────────────────────────────
// MAC vectors — RFC 4231 (HMAC-SHA-256)
// ──────────────────────────────────────────────────────────────────────────────
static MAC_VECS: &[MacVec] = &[
// RFC 4231 Test Case 1 — HMAC-SHA-256
MacVec {
name: "HMAC-SHA-256 RFC 4231 TC1",
algo: MacAlgorithm::HmacSha256,
key: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
data: "4869205468657265", // "Hi There"
expected: "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7",
},
// RFC 4231 Test Case 2
MacVec {
name: "HMAC-SHA-256 RFC 4231 TC2",
algo: MacAlgorithm::HmacSha256,
key: "4a656665", // "Jefe"
data: "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
expected: "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
},
];
// ──────────────────────────────────────────────────────────────────────────────
// Hash vectors — FIPS 180-4
// ──────────────────────────────────────────────────────────────────────────────
static HASH_VECS: &[HashVec] = &[
HashVec {
name: "SHA-256 empty",
algo: HashAlgorithm::Sha256,
data: "",
expected: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
},
HashVec {
name: "SHA-256 'abc'",
algo: HashAlgorithm::Sha256,
data: "616263",
// SHA-256("abc") — FIPS 180-4 §B.1 (verified with `echo -n abc | shasum -a 256`).
expected: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
},
HashVec {
name: "SHA-512 empty",
algo: HashAlgorithm::Sha512,
data: "",
expected: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\
47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
},
HashVec {
name: "SHA3-256 empty",
algo: HashAlgorithm::Sha3_256,
data: "",
expected: "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
},
HashVec {
name: "BLAKE2b-512 empty",
algo: HashAlgorithm::Blake2b512,
data: "",
expected: "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419\
d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
},
];
// ──────────────────────────────────────────────────────────────────────────────
// KEM DH vectors — RFC 7748 §6.1 (X25519) and §6.2 (X448)
//
// All byte strings are little-endian (the canonical wire format for both
// X25519 and X448 per RFC 7748). The test calls:
// decapsulate(algo, alice_private, bob_public) == expected_shared
// decapsulate(algo, bob_private, alice_public) == expected_shared
// ──────────────────────────────────────────────────────────────────────────────
static KEM_DH_VECS: &[KemDhVec] = &[
// ── RFC 7748 §6.1 — X25519 ───────────────────────────────────────────────
KemDhVec {
name: "X25519 DH RFC 7748 §6.1",
algo: KemAlgorithm::X25519,
alice_private: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
alice_public: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
bob_private: "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
bob_public: "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
expected_shared: "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
},
// ── RFC 7748 §6.2 — X448 ────────────────────────────────────────────────
KemDhVec {
name: "X448 DH RFC 7748 §6.2",
algo: KemAlgorithm::X448,
alice_private: "9a8f4925d1519f5775cf46b04b5800d4\
ee9ee8bae8bc5565d498c28dd9c9baf5\
74a9419744897391006382a6f127ab1d\
9ac2d8c0a598726b",
alice_public: "9b08f7cc31b7e3e67d22d5aea121074a\
273bd2b83de09c63faa73d2c22c5d9bb\
c836647241d953d40c5b12da88120d53\
177f80e532c41fa0",
bob_private: "1c306a7ac2a0e2e0990b294470cba339\
e6453772b075811d8fad0d1d6927c120\
bb5ee8972b0d3e21374c9c921b09d1b0\
366f10b65173992d",
bob_public: "3eb7a829b0cd20f5bcfc0b599b6feccf\
6da4627107bdb0d4f345b43027d8b972\
fc3e34fb4232a13ca706dcb57aec3dae\
07bdc1c67bf33609",
expected_shared: "07fff4181ac6cc95ec1c16a94a0f74d1\
2da232ce40a77552281d282bb60c0b56\
fd2464c335543936521c24403085d59a\
449a5037514a879d",
},
];
// ──────────────────────────────────────────────────────────────────────────────
// XChaCha20-Poly1305 extended-nonce probe inputs
//
// These are used by `run_xchacha20_kat()` to compute and print the expected
// ciphertext+tag. On first run the printed value is verified against
// draft-irtf-cfrg-xchacha-03 §A.3 and then pinned in the AEAD_VECS above.
//
// Nonce is 24 bytes (the defining property of XChaCha20).
// ──────────────────────────────────────────────────────────────────────────────
/// Key-agreement-test input for XChaCha20-Poly1305 extended-nonce path.
struct XChaChaProbe {
name: &'static str,
key: &'static str, // 32 bytes
nonce: &'static str, // 24 bytes ← extended nonce distinguishes XChaCha
aad: &'static str,
pt: &'static str,
}
static XCHACHA20_PROBES: &[XChaChaProbe] = &[
// Uses the same key/aad/pt as the ChaCha20 RFC 8439 §2.8.2 vector but
// extends the nonce to 24 bytes, exercising the HChaCha20 subkey path.
XChaChaProbe {
name: "XChaCha20-Poly1305 extended-nonce roundtrip",
key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
nonce: "404142434445464748494a4b4c4d4e4f5051525354555657", // 24 bytes
aad: "50515253c0c1c2c3c4c5c6c7",
pt: "4c616469657320616e642047656e746c656d656e206f662074686520636c617373\
206f6620273939", // "Ladies and Gentlemen of the class of '99"
},
];
// ──────────────────────────────────────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────────────────────────────────────
fn from_hex(s: &str) -> Vec<u8> {
let s = s.replace([' ', '\n'], "");
assert!(s.len().is_multiple_of(2), "odd-length hex: {s}");
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
fn pass(name: &str) {
println!(" [PASS] {name}");
}
fn fail(name: &str, got: &[u8], expected: &[u8]) {
println!(" [FAIL] {name}");
println!(" expected: {}", hex::encode(expected));
println!(" got: {}", hex::encode(got));
}
// ──────────────────────────────────────────────────────────────────────────────
// Runner
// ──────────────────────────────────────────────────────────────────────────────
fn run_aead(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── AEAD ─────────────────────────────────────────────────────────────");
for v in AEAD_VECS {
let key = from_hex(v.key);
let nonce = from_hex(v.nonce);
let aad = from_hex(v.aad);
let pt = from_hex(v.pt);
let expected = from_hex(v.ct_tag);
match p.encrypt_aead(v.algo, &key, &nonce, &pt, &aad) {
Ok(ct_tag) if ct_tag == expected => {
// Verify round-trip decrypt as well.
match p.decrypt_aead(v.algo, &key, &nonce, &ct_tag, &aad) {
Ok(recovered) if recovered == pt => pass(v.name),
Ok(_) => {
*failures += 1;
println!(" [FAIL] {} (decrypt mismatch)", v.name);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (decrypt error: {e})", v.name);
}
}
}
Ok(ct_tag) => {
*failures += 1;
fail(v.name, &ct_tag, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (encrypt error: {e})", v.name);
}
}
}
}
fn run_kdf(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── KDF ──────────────────────────────────────────────────────────────");
for v in KDF_VECS {
let ikm = from_hex(v.ikm);
let salt = from_hex(v.salt);
let info = from_hex(v.info);
let expected = from_hex(v.expected);
match p.derive_key(v.algo, &ikm, &salt, &info, v.length) {
Ok(out) if out.as_slice() == expected.as_slice() => pass(v.name),
Ok(out) => {
*failures += 1;
fail(v.name, &out, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} ({e})", v.name);
}
}
}
}
fn run_mac(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── MAC ──────────────────────────────────────────────────────────────");
for v in MAC_VECS {
let key = from_hex(v.key);
let data = from_hex(v.data);
let expected = from_hex(v.expected);
match p.compute_mac(v.algo, &key, &data) {
Ok(tag) if tag == expected => {
// Also verify constant-time path.
match p.verify_mac(v.algo, &key, &data, &tag) {
Ok(true) => pass(v.name),
Ok(false) => {
*failures += 1;
println!(" [FAIL] {} (verify false)", v.name);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (verify error: {e})", v.name);
}
}
}
Ok(tag) => {
*failures += 1;
fail(v.name, &tag, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} ({e})", v.name);
}
}
}
}
fn run_hash(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── Hash ─────────────────────────────────────────────────────────────");
for v in HASH_VECS {
let data = from_hex(v.data);
let expected = from_hex(v.expected);
match p.hash(v.algo, &data) {
Ok(digest) if digest == expected => pass(v.name),
Ok(digest) => {
*failures += 1;
fail(v.name, &digest, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} ({e})", v.name);
}
}
}
}
/// RFC 7748 X25519/X448 DH known-answer tests.
///
/// For each vector we verify two DH directions:
/// - `decapsulate(alice_priv, bob_pub)` == expected_shared
/// - `decapsulate(bob_priv, alice_pub)` == expected_shared
///
/// In our KEM API the "ciphertext" passed to `decapsulate` is the peer's
/// ephemeral public key — identical to a raw DH operation, which is exactly
/// what X25519/X448 encapsulation performs.
fn run_kem(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── KEM DH (RFC 7748) ────────────────────────────────────────────────");
for v in KEM_DH_VECS {
let alice_priv = from_hex(v.alice_private);
let alice_pub = from_hex(v.alice_public);
let bob_priv = from_hex(v.bob_private);
let bob_pub = from_hex(v.bob_public);
let expected = from_hex(v.expected_shared);
// Alice→Bob direction.
let test_a = format!("{} (Alice→Bob)", v.name);
match p.decapsulate(v.algo, &alice_priv, &bob_pub) {
Ok(shared) if shared.as_slice() == expected.as_slice() => pass(&test_a),
Ok(shared) => {
*failures += 1;
fail(&test_a, &shared, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} ({e})", test_a);
}
}
// Bob→Alice direction (symmetric).
let test_b = format!("{} (Bob→Alice)", v.name);
match p.decapsulate(v.algo, &bob_priv, &alice_pub) {
Ok(shared) if shared.as_slice() == expected.as_slice() => pass(&test_b),
Ok(shared) => {
*failures += 1;
fail(&test_b, &shared, &expected);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} ({e})", test_b);
}
}
}
}
/// KEM ephemeral roundtrip self-consistency check.
///
/// Generates a random key pair per algorithm, encapsulates and decapsulates,
/// and confirms the shared secret is identical. Not a KAT — validates the
/// encap/decap path is wired correctly end-to-end.
fn run_kem_roundtrip(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── KEM Roundtrip ────────────────────────────────────────────────────");
for algo in [KemAlgorithm::X25519, KemAlgorithm::X448] {
let name = format!("{:?} ephemeral roundtrip", algo);
match p.generate_keypair(algo) {
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (keygen: {e})", name);
continue;
}
Ok(kp) => match p.encapsulate(algo, &kp.public_key) {
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (encap: {e})", name);
}
Ok(encap) => match p.decapsulate(algo, &kp.private_key, &encap.ciphertext) {
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (decap: {e})", name);
}
Ok(decap_secret) => {
if decap_secret.as_slice() == encap.shared_secret.as_slice() {
pass(&name);
} else {
*failures += 1;
println!(" [FAIL] {} (shared-secret mismatch)", name);
println!(" encap: {}", hex::encode(&encap.shared_secret));
println!(" decap: {}", hex::encode(&decap_secret));
}
}
},
},
}
}
}
/// XChaCha20-Poly1305 extended-nonce functional conformance tests.
///
/// This function runs two sub-checks per probe input:
///
/// 1. **Roundtrip**: encrypt → decrypt → compare to original plaintext.
/// 2. **Auth failure**: tamper one byte of the ciphertext+tag and confirm
/// that `decrypt` returns `AuthenticationFailed`.
///
/// The output ciphertext+tag is also printed in hex so the caller can pin it
/// as a known-answer test once verified against an external reference
/// (e.g. libsodium or the draft-irtf-cfrg-xchacha-03 §A.3 appendix).
fn run_xchacha20_kat(p: &WolfSslProvider, failures: &mut usize) {
println!("\n── XChaCha20-Poly1305 extended-nonce ────────────────────────────────");
for v in XCHACHA20_PROBES {
let key = from_hex(v.key);
let nonce = from_hex(v.nonce);
let aad = from_hex(v.aad);
let pt = from_hex(v.pt);
// ── Roundtrip ──────────────────────────────────────────────────────
let rt_name = format!("{} [roundtrip]", v.name);
match p.encrypt_aead(AeadAlgorithm::XChaCha20Poly1305, &key, &nonce, &pt, &aad) {
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (encrypt: {e})", rt_name);
continue;
}
Ok(ct_tag) => {
// Print the ct_tag for pinning purposes.
println!(" [INFO] {} ct_tag = {}", v.name, hex::encode(&ct_tag));
match p.decrypt_aead(
AeadAlgorithm::XChaCha20Poly1305,
&key,
&nonce,
&ct_tag,
&aad,
) {
Ok(recovered) if recovered == pt => pass(&rt_name),
Ok(_) => {
*failures += 1;
println!(" [FAIL] {} (decrypt mismatch)", rt_name);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (decrypt error: {e})", rt_name);
}
}
// ── Auth-failure check ────────────────────────────────────
if !ct_tag.is_empty() {
let auth_name = format!("{} [auth-fail]", v.name);
let mut tampered = ct_tag.clone();
*tampered.last_mut().unwrap() ^= 0xff;
match p.decrypt_aead(
AeadAlgorithm::XChaCha20Poly1305,
&key,
&nonce,
&tampered,
&aad,
) {
Err(ccc_crypto_core::error::CryptoError::AuthenticationFailed) => {
pass(&auth_name)
}
Ok(_) => {
*failures += 1;
println!(" [FAIL] {} (expected auth failure, got Ok)", auth_name);
}
Err(e) => {
*failures += 1;
println!(" [FAIL] {} (wrong error type: {e})", auth_name);
}
}
}
}
}
}
}
// ──────────────────────────────────────────────────────────────────────────────
// Entry point
// ──────────────────────────────────────────────────────────────────────────────
fn main() {
env_logger::init();
// Initialise wolfSSL provider.
ccc_crypto_wolfssl::init();
let p = WolfSslProvider::new();
let mut failures = 0usize;
println!("CCC Conformance Tests — wolfSSL provider");
println!("=========================================");
run_aead(&p, &mut failures);
run_kdf(&p, &mut failures);
run_mac(&p, &mut failures);
run_hash(&p, &mut failures);
run_kem(&p, &mut failures);
run_kem_roundtrip(&p, &mut failures);
run_xchacha20_kat(&p, &mut failures);
println!();
if failures == 0 {
println!("ALL VECTORS PASSED ✓");
std::process::exit(0);
} else {
println!("FAILURES: {failures}");
std::process::exit(1);
}
}