MOD: formatting
This commit is contained in:
parent
971f1aeaaa
commit
68836cfff4
|
|
@ -42,30 +42,30 @@ impl AeadAlgorithm {
|
|||
/// Human-readable name, suitable for logs and diagnostics.
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::AesGcm256 => "AES-256-GCM",
|
||||
Self::ChaCha20Poly1305 => "ChaCha20-Poly1305",
|
||||
Self::AesGcm256 => "AES-256-GCM",
|
||||
Self::ChaCha20Poly1305 => "ChaCha20-Poly1305",
|
||||
Self::XChaCha20Poly1305 => "XChaCha20-Poly1305",
|
||||
Self::Ascon128a => "Ascon-AEAD-128a",
|
||||
Self::Ascon128a => "Ascon-AEAD-128a",
|
||||
}
|
||||
}
|
||||
|
||||
/// Expected nonce length in bytes for this algorithm.
|
||||
pub fn nonce_len(self) -> usize {
|
||||
match self {
|
||||
Self::AesGcm256 => 12,
|
||||
Self::ChaCha20Poly1305 => 12,
|
||||
Self::AesGcm256 => 12,
|
||||
Self::ChaCha20Poly1305 => 12,
|
||||
Self::XChaCha20Poly1305 => 24,
|
||||
Self::Ascon128a => 16,
|
||||
Self::Ascon128a => 16,
|
||||
}
|
||||
}
|
||||
|
||||
/// Expected key length in bytes for this algorithm.
|
||||
pub fn key_len(self) -> usize {
|
||||
match self {
|
||||
Self::AesGcm256 => 32,
|
||||
Self::ChaCha20Poly1305 => 32,
|
||||
Self::AesGcm256 => 32,
|
||||
Self::ChaCha20Poly1305 => 32,
|
||||
Self::XChaCha20Poly1305 => 32,
|
||||
Self::Ascon128a => 16,
|
||||
Self::Ascon128a => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -112,12 +112,12 @@ impl KdfAlgorithm {
|
|||
/// Human-readable name.
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Sha256 => "HKDF-SHA-256",
|
||||
Self::Sha384 => "HKDF-SHA-384",
|
||||
Self::Sha512 => "HKDF-SHA-512",
|
||||
Self::Sha256 => "HKDF-SHA-256",
|
||||
Self::Sha384 => "HKDF-SHA-384",
|
||||
Self::Sha512 => "HKDF-SHA-512",
|
||||
Self::Blake2b512 => "HKDF-BLAKE2b-512",
|
||||
Self::Argon2id => "Argon2id",
|
||||
Self::Kmac256 => "KMAC256",
|
||||
Self::Argon2id => "Argon2id",
|
||||
Self::Kmac256 => "KMAC256",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,24 +223,24 @@ impl HashAlgorithm {
|
|||
/// Human-readable name.
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Sha256 => "SHA-256",
|
||||
Self::Sha384 => "SHA-384",
|
||||
Self::Sha512 => "SHA-512",
|
||||
Self::Sha256 => "SHA-256",
|
||||
Self::Sha384 => "SHA-384",
|
||||
Self::Sha512 => "SHA-512",
|
||||
Self::Blake2b512 => "BLAKE2b-512",
|
||||
Self::Sha3_256 => "SHA3-256",
|
||||
Self::Sha3_512 => "SHA3-512",
|
||||
Self::Sha3_256 => "SHA3-256",
|
||||
Self::Sha3_512 => "SHA3-512",
|
||||
}
|
||||
}
|
||||
|
||||
/// Digest output length in bytes.
|
||||
pub fn digest_len(self) -> usize {
|
||||
match self {
|
||||
Self::Sha256 => 32,
|
||||
Self::Sha384 => 48,
|
||||
Self::Sha512 => 64,
|
||||
Self::Sha256 => 32,
|
||||
Self::Sha384 => 48,
|
||||
Self::Sha512 => 64,
|
||||
Self::Blake2b512 => 64,
|
||||
Self::Sha3_256 => 32,
|
||||
Self::Sha3_512 => 64,
|
||||
Self::Sha3_256 => 32,
|
||||
Self::Sha3_512 => 64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -285,10 +285,10 @@ impl KemAlgorithm {
|
|||
/// Human-readable name.
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::X25519 => "X25519",
|
||||
Self::X448 => "X448",
|
||||
Self::MlKem768 => "ML-KEM-768",
|
||||
Self::MlKem1024 => "ML-KEM-1024",
|
||||
Self::X25519 => "X25519",
|
||||
Self::X448 => "X448",
|
||||
Self::MlKem768 => "ML-KEM-768",
|
||||
Self::MlKem1024 => "ML-KEM-1024",
|
||||
Self::ClassicMcEliece460896 => "Classic-McEliece-460896",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,11 +110,7 @@ pub trait MacProvider {
|
|||
/// Cryptographic hash operations.
|
||||
pub trait HashProvider {
|
||||
/// Compute a digest of `data` using the specified algorithm.
|
||||
fn hash(
|
||||
&self,
|
||||
algo: HashAlgorithm,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError>;
|
||||
fn hash(&self, algo: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, CryptoError>;
|
||||
}
|
||||
|
||||
/// Key encapsulation mechanism operations.
|
||||
|
|
@ -124,10 +120,7 @@ pub trait HashProvider {
|
|||
/// functions separately and looks up a `KemProvider` from the registry.
|
||||
pub trait KemProvider {
|
||||
/// Generate a new KEM key pair.
|
||||
fn generate_keypair(
|
||||
&self,
|
||||
algo: KemAlgorithm,
|
||||
) -> Result<KemKeyPair, CryptoError>;
|
||||
fn generate_keypair(&self, algo: KemAlgorithm) -> Result<KemKeyPair, CryptoError>;
|
||||
|
||||
/// Encapsulate a shared secret for the holder of `public_key`.
|
||||
///
|
||||
|
|
@ -170,7 +163,9 @@ pub trait KemProvider {
|
|||
/// `ProviderRegistry::global().register(name, Box::new(provider))`.
|
||||
/// 5. Call `ccc_crypto_<name>::init()` from `ccc-flutter-bridge/src/lib.rs`.
|
||||
/// 6. Add NIST test vectors for every algorithm you expose.
|
||||
pub trait CryptoProvider: AeadProvider + KdfProvider + MacProvider + HashProvider + Send + Sync {
|
||||
pub trait CryptoProvider:
|
||||
AeadProvider + KdfProvider + MacProvider + HashProvider + Send + Sync
|
||||
{
|
||||
/// The provider's canonical name (e.g. `"wolfssl"`).
|
||||
///
|
||||
/// Must be lowercase, no spaces, match the `CccCryptoProvider` enum value
|
||||
|
|
|
|||
|
|
@ -118,12 +118,22 @@ mod tests {
|
|||
|
||||
impl AeadProvider for StubProvider {
|
||||
fn encrypt_aead(
|
||||
&self, _a: AeadAlgorithm, _k: &[u8], _n: &[u8], pt: &[u8], _aad: &[u8],
|
||||
&self,
|
||||
_a: AeadAlgorithm,
|
||||
_k: &[u8],
|
||||
_n: &[u8],
|
||||
pt: &[u8],
|
||||
_aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
Ok(pt.to_vec())
|
||||
}
|
||||
fn decrypt_aead(
|
||||
&self, _a: AeadAlgorithm, _k: &[u8], _n: &[u8], ct: &[u8], _aad: &[u8],
|
||||
&self,
|
||||
_a: AeadAlgorithm,
|
||||
_k: &[u8],
|
||||
_n: &[u8],
|
||||
ct: &[u8],
|
||||
_aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
Ok(ct.to_vec())
|
||||
}
|
||||
|
|
@ -131,7 +141,12 @@ mod tests {
|
|||
|
||||
impl KdfProvider for StubProvider {
|
||||
fn derive_key(
|
||||
&self, _a: KdfAlgorithm, _ikm: &[u8], _salt: &[u8], _info: &[u8], length: usize,
|
||||
&self,
|
||||
_a: KdfAlgorithm,
|
||||
_ikm: &[u8],
|
||||
_salt: &[u8],
|
||||
_info: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
Ok(Zeroizing::new(vec![0u8; length]))
|
||||
}
|
||||
|
|
@ -139,41 +154,56 @@ mod tests {
|
|||
|
||||
impl MacProvider for StubProvider {
|
||||
fn compute_mac(
|
||||
&self, _a: MacAlgorithm, _k: &[u8], _data: &[u8],
|
||||
&self,
|
||||
_a: MacAlgorithm,
|
||||
_k: &[u8],
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
Ok(vec![0u8; 32])
|
||||
}
|
||||
fn verify_mac(
|
||||
&self, _a: MacAlgorithm, _k: &[u8], _data: &[u8], _mac: &[u8],
|
||||
&self,
|
||||
_a: MacAlgorithm,
|
||||
_k: &[u8],
|
||||
_data: &[u8],
|
||||
_mac: &[u8],
|
||||
) -> Result<bool, CryptoError> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl HashProvider for StubProvider {
|
||||
fn hash(
|
||||
&self, _a: HashAlgorithm, data: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
fn hash(&self, _a: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
Ok(data.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoProvider for StubProvider {
|
||||
fn provider_name(&self) -> &'static str { "stub" }
|
||||
fn provider_name(&self) -> &'static str {
|
||||
"stub"
|
||||
}
|
||||
fn capabilities(&self) -> ProviderCapabilities {
|
||||
ProviderCapabilities::empty("stub")
|
||||
}
|
||||
fn self_test(&self) -> SelfTestReport {
|
||||
SelfTestReport::finalise("stub", vec![AlgoTestResult {
|
||||
algo_id: 12, algo_name: "stub".into(), passed: true, error_message: None,
|
||||
}])
|
||||
SelfTestReport::finalise(
|
||||
"stub",
|
||||
vec![AlgoTestResult {
|
||||
algo_id: 12,
|
||||
algo_name: "stub".into(),
|
||||
passed: true,
|
||||
error_message: None,
|
||||
}],
|
||||
)
|
||||
}
|
||||
fn benchmark(&self) -> BenchmarkReport {
|
||||
BenchmarkReport {
|
||||
provider_name: "stub".into(),
|
||||
results: vec![AlgoBenchResult {
|
||||
algo_id: 12, algo_name: "stub".into(),
|
||||
throughput_mbps: 999.0, efficiency_score: 100,
|
||||
algo_id: 12,
|
||||
algo_name: "stub".into(),
|
||||
throughput_mbps: 999.0,
|
||||
efficiency_score: 100,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +212,9 @@ mod tests {
|
|||
// ── Tests ──────────────────────────────────────────────────────────────────
|
||||
|
||||
fn fresh_registry() -> ProviderRegistry {
|
||||
ProviderRegistry { inner: Mutex::new(HashMap::new()) }
|
||||
ProviderRegistry {
|
||||
inner: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -216,7 +248,7 @@ mod tests {
|
|||
fn register_replaces_existing() {
|
||||
let reg = fresh_registry();
|
||||
reg.register("stub", Box::new(StubProvider));
|
||||
reg.register("stub", Box::new(StubProvider)); // replace
|
||||
reg.register("stub", Box::new(StubProvider)); // replace
|
||||
assert_eq!(reg.list().len(), 1);
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +259,13 @@ mod tests {
|
|||
let provider = reg.get("stub").unwrap();
|
||||
let plaintext = b"hello world";
|
||||
let ct = provider
|
||||
.encrypt_aead(AeadAlgorithm::AesGcm256, &[0u8; 32], &[0u8; 12], plaintext, b"")
|
||||
.encrypt_aead(
|
||||
AeadAlgorithm::AesGcm256,
|
||||
&[0u8; 32],
|
||||
&[0u8; 12],
|
||||
plaintext,
|
||||
b"",
|
||||
)
|
||||
.unwrap();
|
||||
let pt = provider
|
||||
.decrypt_aead(AeadAlgorithm::AesGcm256, &[0u8; 32], &[0u8; 12], &ct, b"")
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@
|
|||
//! a pre-installed wolfSSL at this path. The path must contain
|
||||
//! `include/wolfssl/` and `lib/libwolfssl.a`.
|
||||
|
||||
use std::{env, path::{Path, PathBuf}};
|
||||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// When the `stub_ffi` feature is enabled (e.g. for type-checking without
|
||||
|
|
@ -34,29 +37,31 @@ fn main() {
|
|||
|
||||
// ── 1. Locate wolfSSL ────────────────────────────────────────────────────
|
||||
|
||||
let (include_dir, lib_dir) =
|
||||
if let Ok(install_dir) = env::var("WOLFSSL_INSTALL_DIR") {
|
||||
// Use a pre-installed wolfSSL — skip the CMake build.
|
||||
let p = PathBuf::from(&install_dir);
|
||||
println!("cargo:warning=Using pre-installed wolfSSL at {}", install_dir);
|
||||
(p.join("include"), p.join("lib"))
|
||||
} else {
|
||||
// Build from source (default — uses our git submodule).
|
||||
let source_dir = env::var("WOLFSSL_SOURCE_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| workspace_root.join("vendors/wolfssl"));
|
||||
let (include_dir, lib_dir) = if let Ok(install_dir) = env::var("WOLFSSL_INSTALL_DIR") {
|
||||
// Use a pre-installed wolfSSL — skip the CMake build.
|
||||
let p = PathBuf::from(&install_dir);
|
||||
println!(
|
||||
"cargo:warning=Using pre-installed wolfSSL at {}",
|
||||
install_dir
|
||||
);
|
||||
(p.join("include"), p.join("lib"))
|
||||
} else {
|
||||
// Build from source (default — uses our git submodule).
|
||||
let source_dir = env::var("WOLFSSL_SOURCE_DIR")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| workspace_root.join("vendors/wolfssl"));
|
||||
|
||||
if !source_dir.join("CMakeLists.txt").exists() {
|
||||
panic!(
|
||||
"\n\nwolfSSL source not found at {}.\n\
|
||||
if !source_dir.join("CMakeLists.txt").exists() {
|
||||
panic!(
|
||||
"\n\nwolfSSL source not found at {}.\n\
|
||||
Run `git submodule update --init --recursive` to fetch it,\n\
|
||||
or set WOLFSSL_SOURCE_DIR to an alternative path.\n",
|
||||
source_dir.display()
|
||||
);
|
||||
}
|
||||
source_dir.display()
|
||||
);
|
||||
}
|
||||
|
||||
build_wolfssl_cmake(&source_dir, &out_dir)
|
||||
};
|
||||
build_wolfssl_cmake(&source_dir, &out_dir)
|
||||
};
|
||||
|
||||
// ── 2. Tell Cargo how to link ────────────────────────────────────────────
|
||||
|
||||
|
|
@ -153,15 +158,15 @@ fn build_wolfssl_cmake(source_dir: &Path, _out_dir: &Path) -> (PathBuf, PathBuf)
|
|||
.cflag("-DWOLFCRYPT_ONLY")
|
||||
// Key & certificate generation.
|
||||
// Adds X.509 cert/CSR/key generation capability on top of wolfCrypt.
|
||||
.define("WOLFSSL_KEYGEN", "yes") // -DWOLFSSL_KEY_GEN
|
||||
.define("WOLFSSL_CERTGEN", "yes") // -DWOLFSSL_CERT_GEN
|
||||
.define("WOLFSSL_CERTREQ", "yes") // -DWOLFSSL_CERT_REQ
|
||||
.define("WOLFSSL_CERTEXT", "yes"); // -DWOLFSSL_CERT_EXT
|
||||
.define("WOLFSSL_KEYGEN", "yes") // -DWOLFSSL_KEY_GEN
|
||||
.define("WOLFSSL_CERTGEN", "yes") // -DWOLFSSL_CERT_GEN
|
||||
.define("WOLFSSL_CERTREQ", "yes") // -DWOLFSSL_CERT_REQ
|
||||
.define("WOLFSSL_CERTEXT", "yes"); // -DWOLFSSL_CERT_EXT
|
||||
|
||||
let install_path = cfg.build();
|
||||
|
||||
let include_dir = install_path.join("include");
|
||||
let lib_dir = install_path.join("lib");
|
||||
let lib_dir = install_path.join("lib");
|
||||
|
||||
(include_dir, lib_dir)
|
||||
}
|
||||
|
|
@ -302,7 +307,7 @@ fn generate_bindings(include_dir: &Path, out_dir: &Path) {
|
|||
.allowlist_type("WC_RNG")
|
||||
.allowlist_type("OS_Seed")
|
||||
.allowlist_type("wc_Sha256")
|
||||
.allowlist_type("wc_Sha512") // also used as wc_Sha384 (typedef)
|
||||
.allowlist_type("wc_Sha512") // also used as wc_Sha384 (typedef)
|
||||
.allowlist_type("wc_Sha3")
|
||||
.allowlist_type("Blake2b")
|
||||
.allowlist_type("curve25519_key")
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@ pub fn encrypt(
|
|||
) -> Result<Vec<u8>, CryptoError> {
|
||||
validate_key_nonce(algo, key, nonce)?;
|
||||
match algo {
|
||||
AeadAlgorithm::AesGcm256 => aes_gcm_256_encrypt(key, nonce, plaintext, aad),
|
||||
AeadAlgorithm::ChaCha20Poly1305 => chacha20_poly1305_encrypt(key, nonce, plaintext, aad),
|
||||
AeadAlgorithm::AesGcm256 => aes_gcm_256_encrypt(key, nonce, plaintext, aad),
|
||||
AeadAlgorithm::ChaCha20Poly1305 => chacha20_poly1305_encrypt(key, nonce, plaintext, aad),
|
||||
AeadAlgorithm::XChaCha20Poly1305 => xchacha20_poly1305_encrypt(key, nonce, plaintext, aad),
|
||||
AeadAlgorithm::Ascon128a =>
|
||||
Err(CryptoError::FeatureNotCompiled("Ascon-128a".into())),
|
||||
AeadAlgorithm::Ascon128a => Err(CryptoError::FeatureNotCompiled("Ascon-128a".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,11 +46,14 @@ pub fn decrypt(
|
|||
) -> Result<Vec<u8>, CryptoError> {
|
||||
validate_key_nonce(algo, key, nonce)?;
|
||||
match algo {
|
||||
AeadAlgorithm::AesGcm256 => aes_gcm_256_decrypt(key, nonce, ciphertext_and_tag, aad),
|
||||
AeadAlgorithm::ChaCha20Poly1305 => chacha20_poly1305_decrypt(key, nonce, ciphertext_and_tag, aad),
|
||||
AeadAlgorithm::XChaCha20Poly1305 => xchacha20_poly1305_decrypt(key, nonce, ciphertext_and_tag, aad),
|
||||
AeadAlgorithm::Ascon128a =>
|
||||
Err(CryptoError::FeatureNotCompiled("Ascon-128a".into())),
|
||||
AeadAlgorithm::AesGcm256 => aes_gcm_256_decrypt(key, nonce, ciphertext_and_tag, aad),
|
||||
AeadAlgorithm::ChaCha20Poly1305 => {
|
||||
chacha20_poly1305_decrypt(key, nonce, ciphertext_and_tag, aad)
|
||||
}
|
||||
AeadAlgorithm::XChaCha20Poly1305 => {
|
||||
xchacha20_poly1305_decrypt(key, nonce, ciphertext_and_tag, aad)
|
||||
}
|
||||
AeadAlgorithm::Ascon128a => Err(CryptoError::FeatureNotCompiled("Ascon-128a".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -60,18 +62,22 @@ pub fn decrypt(
|
|||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn validate_key_nonce(algo: AeadAlgorithm, key: &[u8], nonce: &[u8]) -> Result<(), CryptoError> {
|
||||
let expected_key = algo.key_len();
|
||||
let expected_key = algo.key_len();
|
||||
let expected_nonce = algo.nonce_len();
|
||||
if key.len() != expected_key {
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"{}: expected {}-byte key, got {}",
|
||||
algo.name(), expected_key, key.len()
|
||||
algo.name(),
|
||||
expected_key,
|
||||
key.len()
|
||||
)));
|
||||
}
|
||||
if nonce.len() != expected_nonce {
|
||||
return Err(CryptoError::InvalidNonce(format!(
|
||||
"{}: expected {}-byte nonce, got {}",
|
||||
algo.name(), expected_nonce, nonce.len()
|
||||
algo.name(),
|
||||
expected_nonce,
|
||||
nonce.len()
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -85,7 +91,7 @@ const TAG_LEN: usize = 16;
|
|||
|
||||
fn aes_gcm_256_encrypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8], // 12 bytes
|
||||
nonce: &[u8], // 12 bytes
|
||||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
|
|
@ -98,11 +104,8 @@ fn aes_gcm_256_encrypt(
|
|||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
|
||||
// sizeof(Aes) = 288 bytes; alignment = 16.
|
||||
let layout = Layout::from_size_align(
|
||||
std::mem::size_of::<crate::sys::Aes>().max(288),
|
||||
16,
|
||||
)
|
||||
.expect("layout must be valid");
|
||||
let layout = Layout::from_size_align(std::mem::size_of::<crate::sys::Aes>().max(288), 16)
|
||||
.expect("layout must be valid");
|
||||
|
||||
let aes_ptr = alloc_zeroed(layout) as *mut crate::sys::Aes;
|
||||
if aes_ptr.is_null() {
|
||||
|
|
@ -116,18 +119,20 @@ fn aes_gcm_256_encrypt(
|
|||
);
|
||||
if ret != 0 {
|
||||
dealloc(aes_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_AesInit returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_AesInit returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_AesGcmSetKey(
|
||||
aes_ptr,
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
);
|
||||
let ret = crate::sys::wc_AesGcmSetKey(aes_ptr, key.as_ptr(), key.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_AesFree(aes_ptr);
|
||||
dealloc(aes_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InvalidKey(format!("wc_AesGcmSetKey returned {}", ret)));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_AesGcmSetKey returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_AesGcmEncrypt(
|
||||
|
|
@ -139,13 +144,20 @@ fn aes_gcm_256_encrypt(
|
|||
nonce.len() as u32,
|
||||
tag_buf.as_mut_ptr(),
|
||||
TAG_LEN as u32,
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len() as u32,
|
||||
);
|
||||
crate::sys::wc_AesFree(aes_ptr);
|
||||
dealloc(aes_ptr as *mut u8, layout);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_AesGcmEncrypt returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_AesGcmEncrypt returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +172,7 @@ fn aes_gcm_256_decrypt(
|
|||
) -> Result<Vec<u8>, CryptoError> {
|
||||
if ciphertext_and_tag.len() < TAG_LEN {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"AES-256-GCM: ciphertext too short (no room for tag)".into()
|
||||
"AES-256-GCM: ciphertext too short (no room for tag)".into(),
|
||||
));
|
||||
}
|
||||
let ct_len = ciphertext_and_tag.len() - TAG_LEN;
|
||||
|
|
@ -172,11 +184,8 @@ fn aes_gcm_256_decrypt(
|
|||
// alignment so the C code's SIMD operations work correctly on ARM64.
|
||||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
|
||||
let layout = Layout::from_size_align(
|
||||
std::mem::size_of::<crate::sys::Aes>().max(288),
|
||||
16,
|
||||
)
|
||||
.expect("layout must be valid");
|
||||
let layout = Layout::from_size_align(std::mem::size_of::<crate::sys::Aes>().max(288), 16)
|
||||
.expect("layout must be valid");
|
||||
|
||||
let aes_ptr = alloc_zeroed(layout) as *mut crate::sys::Aes;
|
||||
if aes_ptr.is_null() {
|
||||
|
|
@ -190,18 +199,20 @@ fn aes_gcm_256_decrypt(
|
|||
);
|
||||
if ret != 0 {
|
||||
dealloc(aes_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_AesInit returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_AesInit returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_AesGcmSetKey(
|
||||
aes_ptr,
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
);
|
||||
let ret = crate::sys::wc_AesGcmSetKey(aes_ptr, key.as_ptr(), key.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_AesFree(aes_ptr);
|
||||
dealloc(aes_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InvalidKey(format!("wc_AesGcmSetKey returned {}", ret)));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_AesGcmSetKey returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_AesGcmDecrypt(
|
||||
|
|
@ -213,7 +224,11 @@ fn aes_gcm_256_decrypt(
|
|||
nonce.len() as u32,
|
||||
tag.as_ptr(),
|
||||
TAG_LEN as u32,
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len() as u32,
|
||||
);
|
||||
crate::sys::wc_AesFree(aes_ptr);
|
||||
|
|
@ -233,7 +248,7 @@ fn aes_gcm_256_decrypt(
|
|||
|
||||
fn chacha20_poly1305_encrypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8], // 12 bytes
|
||||
nonce: &[u8], // 12 bytes
|
||||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
|
|
@ -245,7 +260,11 @@ fn chacha20_poly1305_encrypt(
|
|||
let ret = crate::sys::wc_ChaCha20Poly1305_Encrypt(
|
||||
key.as_ptr(),
|
||||
nonce.as_ptr(),
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len() as u32,
|
||||
plaintext.as_ptr(),
|
||||
plaintext.len() as u32,
|
||||
|
|
@ -253,9 +272,10 @@ fn chacha20_poly1305_encrypt(
|
|||
tag_buf.as_mut_ptr(),
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_ChaCha20Poly1305_Encrypt returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_ChaCha20Poly1305_Encrypt returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +290,7 @@ fn chacha20_poly1305_decrypt(
|
|||
) -> Result<Vec<u8>, CryptoError> {
|
||||
if ciphertext_and_tag.len() < TAG_LEN {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"ChaCha20-Poly1305: ciphertext too short".into()
|
||||
"ChaCha20-Poly1305: ciphertext too short".into(),
|
||||
));
|
||||
}
|
||||
let ct_len = ciphertext_and_tag.len() - TAG_LEN;
|
||||
|
|
@ -282,7 +302,11 @@ fn chacha20_poly1305_decrypt(
|
|||
let ret = crate::sys::wc_ChaCha20Poly1305_Decrypt(
|
||||
key.as_ptr(),
|
||||
nonce.as_ptr(),
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len() as u32,
|
||||
ciphertext.as_ptr(),
|
||||
ct_len as u32,
|
||||
|
|
@ -310,7 +334,7 @@ fn chacha20_poly1305_decrypt(
|
|||
|
||||
fn xchacha20_poly1305_encrypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8], // 24 bytes
|
||||
nonce: &[u8], // 24 bytes
|
||||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
|
|
@ -325,7 +349,11 @@ fn xchacha20_poly1305_encrypt(
|
|||
dst.len(),
|
||||
plaintext.as_ptr(),
|
||||
plaintext.len(),
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
|
|
@ -333,9 +361,10 @@ fn xchacha20_poly1305_encrypt(
|
|||
key.len(),
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_XChaCha20Poly1305_Encrypt returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_XChaCha20Poly1305_Encrypt returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,13 +373,13 @@ fn xchacha20_poly1305_encrypt(
|
|||
|
||||
fn xchacha20_poly1305_decrypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8], // 24 bytes
|
||||
nonce: &[u8], // 24 bytes
|
||||
ciphertext_and_tag: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
if ciphertext_and_tag.len() < TAG_LEN {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"XChaCha20-Poly1305: ciphertext too short".into()
|
||||
"XChaCha20-Poly1305: ciphertext too short".into(),
|
||||
));
|
||||
}
|
||||
let pt_len = ciphertext_and_tag.len() - TAG_LEN;
|
||||
|
|
@ -364,7 +393,11 @@ fn xchacha20_poly1305_decrypt(
|
|||
dst.len(),
|
||||
ciphertext_and_tag.as_ptr(),
|
||||
ciphertext_and_tag.len(),
|
||||
if aad.is_empty() { std::ptr::null() } else { aad.as_ptr() },
|
||||
if aad.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
aad.as_ptr()
|
||||
},
|
||||
aad.len(),
|
||||
nonce.as_ptr(),
|
||||
nonce.len(),
|
||||
|
|
|
|||
|
|
@ -43,12 +43,15 @@ pub fn probe_capabilities(benchmarks: Option<&BenchmarkReport>) -> ProviderCapab
|
|||
.map(|r| r.efficiency_score)
|
||||
.unwrap_or(0);
|
||||
|
||||
caps = caps.with_aead(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true, // wolfSSL output is deterministic for same key/nonce
|
||||
efficiency_score: efficiency,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_aead(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true, // wolfSSL output is deterministic for same key/nonce
|
||||
efficiency_score: efficiency,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Ascon-128a is not in this wolfSSL build.
|
||||
|
|
@ -57,72 +60,105 @@ pub fn probe_capabilities(benchmarks: Option<&BenchmarkReport>) -> ProviderCapab
|
|||
// ── KDF ──────────────────────────────────────────────────────────────────
|
||||
for algo in [KdfAlgorithm::Sha256, KdfAlgorithm::Sha384, KdfAlgorithm::Sha512] {
|
||||
let available = crate::kdf::derive_key(algo, b"key", b"salt", b"info", 32).is_ok();
|
||||
caps = caps.with_kdf(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 80, // HKDF is fast; benchmark not run for KDF
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_kdf(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 80, // HKDF is fast; benchmark not run for KDF
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
for algo in [KdfAlgorithm::Blake2b512, KdfAlgorithm::Argon2id] {
|
||||
let salt: &[u8] = if algo == KdfAlgorithm::Argon2id { b"saltsalt" } else { b"" };
|
||||
let salt: &[u8] = if algo == KdfAlgorithm::Argon2id {
|
||||
b"saltsalt"
|
||||
} else {
|
||||
b""
|
||||
};
|
||||
let available = crate::kdf::derive_key(algo, b"key", salt, b"", 32).is_ok();
|
||||
caps = caps.with_kdf(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: if algo == KdfAlgorithm::Argon2id { 10 } else { 70 },
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_kdf(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: if algo == KdfAlgorithm::Argon2id {
|
||||
10
|
||||
} else {
|
||||
70
|
||||
},
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
caps = caps.with_kdf(KdfAlgorithm::Kmac256, AlgorithmCapability::unavailable());
|
||||
|
||||
// ── MAC ──────────────────────────────────────────────────────────────────
|
||||
for algo in [MacAlgorithm::HmacSha256, MacAlgorithm::HmacSha384, MacAlgorithm::HmacSha512] {
|
||||
let available = crate::mac::compute_mac(algo, b"key", b"data").is_ok();
|
||||
caps = caps.with_mac(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 85,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_mac(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 85,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
{
|
||||
let available = crate::mac::compute_mac(MacAlgorithm::Blake2bMac, b"key-that-is-16-by", b"data").is_ok();
|
||||
caps = caps.with_mac(MacAlgorithm::Blake2bMac, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 90,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
let available =
|
||||
crate::mac::compute_mac(MacAlgorithm::Blake2bMac, b"key-that-is-16-by", b"data")
|
||||
.is_ok();
|
||||
caps = caps.with_mac(
|
||||
MacAlgorithm::Blake2bMac,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 90,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
caps = caps.with_mac(MacAlgorithm::Poly1305, AlgorithmCapability::unavailable());
|
||||
|
||||
// ── Hash ─────────────────────────────────────────────────────────────────
|
||||
for algo in [
|
||||
HashAlgorithm::Sha256, HashAlgorithm::Sha384, HashAlgorithm::Sha512,
|
||||
HashAlgorithm::Blake2b512, HashAlgorithm::Sha3_256, HashAlgorithm::Sha3_512,
|
||||
HashAlgorithm::Sha256,
|
||||
HashAlgorithm::Sha384,
|
||||
HashAlgorithm::Sha512,
|
||||
HashAlgorithm::Blake2b512,
|
||||
HashAlgorithm::Sha3_256,
|
||||
HashAlgorithm::Sha3_512,
|
||||
] {
|
||||
let available = crate::hash::hash(algo, b"probe").is_ok();
|
||||
caps = caps.with_hash(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 90,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_hash(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: true,
|
||||
efficiency_score: 90,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── KEM ──────────────────────────────────────────────────────────────────
|
||||
for algo in [KemAlgorithm::X25519, KemAlgorithm::X448] {
|
||||
let available = crate::kem::generate_keypair(algo).is_ok();
|
||||
caps = caps.with_kem(algo, AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: false, // key generation is randomised
|
||||
efficiency_score: 80,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
});
|
||||
caps = caps.with_kem(
|
||||
algo,
|
||||
AlgorithmCapability {
|
||||
available,
|
||||
deterministic_io: false, // key generation is randomised
|
||||
efficiency_score: 80,
|
||||
reliability_score: if available { 100 } else { 0 },
|
||||
},
|
||||
);
|
||||
}
|
||||
for algo in [
|
||||
KemAlgorithm::MlKem768, KemAlgorithm::MlKem1024,
|
||||
KemAlgorithm::MlKem768,
|
||||
KemAlgorithm::MlKem1024,
|
||||
KemAlgorithm::ClassicMcEliece460896,
|
||||
] {
|
||||
caps = caps.with_kem(algo, AlgorithmCapability::unavailable());
|
||||
|
|
@ -153,7 +189,7 @@ pub fn run_benchmark() -> BenchmarkReport {
|
|||
let mut raw_results: Vec<(AeadAlgorithm, f64)> = Vec::new();
|
||||
|
||||
for algo in algos {
|
||||
let key = vec![0u8; algo.key_len()];
|
||||
let key = vec![0u8; algo.key_len()];
|
||||
let nonce = vec![0u8; algo.nonce_len()];
|
||||
|
||||
// Probe first; skip unavailable algorithms.
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ use ccc_crypto_core::{algorithms::HashAlgorithm, error::CryptoError};
|
|||
/// [`HashAlgorithm::digest_len`] for output lengths).
|
||||
pub fn hash(algo: HashAlgorithm, data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
match algo {
|
||||
HashAlgorithm::Sha256 => sha256(data),
|
||||
HashAlgorithm::Sha384 => sha384(data),
|
||||
HashAlgorithm::Sha512 => sha512(data),
|
||||
HashAlgorithm::Sha256 => sha256(data),
|
||||
HashAlgorithm::Sha384 => sha384(data),
|
||||
HashAlgorithm::Sha512 => sha512(data),
|
||||
HashAlgorithm::Blake2b512 => blake2b_512(data),
|
||||
HashAlgorithm::Sha3_256 => sha3_256(data),
|
||||
HashAlgorithm::Sha3_512 => sha3_512(data),
|
||||
HashAlgorithm::Sha3_256 => sha3_256(data),
|
||||
HashAlgorithm::Sha3_512 => sha3_512(data),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,11 +42,9 @@ fn sha256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
|
||||
// Size reported by C's sizeof(wc_Sha256) = 120 bytes; alignment = 16.
|
||||
let layout = Layout::from_size_align(
|
||||
std::mem::size_of::<crate::sys::wc_Sha256>().max(120),
|
||||
16,
|
||||
)
|
||||
.expect("layout must be valid");
|
||||
let layout =
|
||||
Layout::from_size_align(std::mem::size_of::<crate::sys::wc_Sha256>().max(120), 16)
|
||||
.expect("layout must be valid");
|
||||
|
||||
let sha_ptr = alloc_zeroed(layout) as *mut crate::sys::wc_Sha256;
|
||||
if sha_ptr.is_null() {
|
||||
|
|
@ -56,23 +54,33 @@ fn sha256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let ret = crate::sys::wc_InitSha256(sha_ptr);
|
||||
if ret != 0 {
|
||||
dealloc(sha_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha256 returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitSha256 returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha256Update(
|
||||
sha_ptr,
|
||||
if data.is_empty() { std::ptr::null() } else { data.as_ptr() },
|
||||
if data.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
data.as_ptr()
|
||||
},
|
||||
data.len() as u32,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha256Free(sha_ptr);
|
||||
dealloc(sha_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha256Update returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha256Update returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha256Final(sha_ptr, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha256Free(sha_ptr);
|
||||
dealloc(sha_ptr as *mut u8, layout);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha256Final returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha256Final returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -85,17 +93,23 @@ fn sha384(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha384(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha384 returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitSha384 returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha384Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha384Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha384Update returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha384Update returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha384Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha384Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha384Final returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha384Final returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -107,17 +121,23 @@ fn sha512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut sha: crate::sys::wc_Sha512 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha512(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha512 returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitSha512 returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha512Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha512Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha512Update returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha512Update returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha512Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha512Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha512Final returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha512Final returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -135,17 +155,23 @@ fn sha3_256(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha3_256(&mut sha, std::ptr::null_mut(), -2);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha3_256 returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitSha3_256 returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_256_Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha3_256_Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_256_Update returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha3_256_Update returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_256_Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha3_256_Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_256_Final returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha3_256_Final returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -157,17 +183,23 @@ fn sha3_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut sha: crate::sys::wc_Sha3 = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitSha3_512(&mut sha, std::ptr::null_mut(), -2);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitSha3_512 returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitSha3_512 returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_512_Update(&mut sha, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_Sha3_512_Free(&mut sha);
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_512_Update returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha3_512_Update returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Sha3_512_Final(&mut sha, out.as_mut_ptr());
|
||||
crate::sys::wc_Sha3_512_Free(&mut sha);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Sha3_512_Final returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Sha3_512_Final returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
@ -187,16 +219,22 @@ pub fn blake2b_512(data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||
let ret = crate::sys::wc_InitBlake2b(&mut b2b, OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitBlake2b returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitBlake2b returned {ret}"
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_Blake2bUpdate(&mut b2b, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Blake2bUpdate returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Blake2bUpdate returned {ret}"
|
||||
)));
|
||||
}
|
||||
// requestSz = OUT_LEN → produce the full 64-byte digest.
|
||||
let ret = crate::sys::wc_Blake2bFinal(&mut b2b, out.as_mut_ptr(), OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_Blake2bFinal returned {ret}")));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Blake2bFinal returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
|
|
|
|||
|
|
@ -54,8 +54,7 @@ pub fn derive_key(
|
|||
KdfAlgorithm::Sha512 => hkdf(WC_HASH_TYPE_SHA512, ikm, salt, info, length),
|
||||
KdfAlgorithm::Blake2b512 => blake2b_kdf(ikm, salt, info, length),
|
||||
KdfAlgorithm::Argon2id => argon2id(ikm, salt, length),
|
||||
KdfAlgorithm::Kmac256 =>
|
||||
Err(CryptoError::FeatureNotCompiled("KMAC256 (Phase 5+)".into())),
|
||||
KdfAlgorithm::Kmac256 => Err(CryptoError::FeatureNotCompiled("KMAC256 (Phase 5+)".into())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,16 +102,20 @@ fn hkdf_extract(
|
|||
unsafe {
|
||||
let ret = crate::sys::wc_Tls13_HKDF_Extract(
|
||||
prk.as_mut_ptr(),
|
||||
if salt.is_empty() { std::ptr::null() } else { salt.as_ptr() },
|
||||
if salt.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
salt.as_ptr()
|
||||
},
|
||||
salt.len() as u32,
|
||||
ikm_buf.as_mut_ptr(),
|
||||
ikm_buf.len() as u32,
|
||||
hash_type,
|
||||
);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Tls13_HKDF_Extract returned {ret}")
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Tls13_HKDF_Extract returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(prk)
|
||||
|
|
@ -133,14 +136,14 @@ fn hkdf_expand(
|
|||
// Maximum HKDF output is 255 * HashLen (RFC 5869 §2.3).
|
||||
let max_len = 255 * hash_len;
|
||||
if length > max_len {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
format!("HKDF: requested length {length} exceeds maximum {max_len}")
|
||||
));
|
||||
return Err(CryptoError::InvalidInput(format!(
|
||||
"HKDF: requested length {length} exceeds maximum {max_len}"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut okm = Zeroizing::new(vec![0u8; length]);
|
||||
let mut t = Vec::new(); // T(i-1); starts empty per spec
|
||||
let mut pos = 0usize;
|
||||
let mut okm = Zeroizing::new(vec![0u8; length]);
|
||||
let mut t = Vec::new(); // T(i-1); starts empty per spec
|
||||
let mut pos = 0usize;
|
||||
let mut counter = 1u8;
|
||||
|
||||
while pos < length {
|
||||
|
|
@ -156,9 +159,9 @@ fn hkdf_expand(
|
|||
let copy_len = (length - pos).min(hash_len);
|
||||
okm[pos..pos + copy_len].copy_from_slice(&t[..copy_len]);
|
||||
pos += copy_len;
|
||||
counter = counter.checked_add(1).ok_or_else(|| {
|
||||
CryptoError::InternalError("HKDF counter overflow".into())
|
||||
})?;
|
||||
counter = counter
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| CryptoError::InternalError("HKDF counter overflow".into()))?;
|
||||
}
|
||||
|
||||
Ok(okm)
|
||||
|
|
@ -194,7 +197,7 @@ fn blake2b_kdf(
|
|||
// callers should use HKDF-SHA512 instead.
|
||||
if length > digest.len() {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"BLAKE2b-512 KDF: requested length exceeds 64 bytes; use HKDF-SHA512".into()
|
||||
"BLAKE2b-512 KDF: requested length exceeds 64 bytes; use HKDF-SHA512".into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +224,7 @@ fn argon2id(
|
|||
// Argon2 requires at least 8 bytes of salt.
|
||||
if salt.len() < 8 {
|
||||
return Err(CryptoError::InvalidInput(
|
||||
"Argon2id requires at least 8-byte salt".into()
|
||||
"Argon2id requires at least 8-byte salt".into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -232,10 +235,10 @@ fn argon2id(
|
|||
// argon2_iterations: 3
|
||||
// argon2_parallelism: 4
|
||||
let params = Params::new(
|
||||
65_536, // m_cost: 64 MiB
|
||||
3, // t_cost: iterations
|
||||
4, // p_cost: parallelism (lanes)
|
||||
Some(length), // output length
|
||||
65_536, // m_cost: 64 MiB
|
||||
3, // t_cost: iterations
|
||||
4, // p_cost: parallelism (lanes)
|
||||
Some(length), // output length
|
||||
)
|
||||
.map_err(|e| CryptoError::InternalError(format!("Argon2 params error: {e}")))?;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,13 @@ use ccc_crypto_core::{
|
|||
pub fn generate_keypair(algo: KemAlgorithm) -> Result<KemKeyPair, CryptoError> {
|
||||
match algo {
|
||||
KemAlgorithm::X25519 => x25519_generate(),
|
||||
KemAlgorithm::X448 => x448_generate(),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5 (requires wolfSSL PQ build)".into()
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into()
|
||||
)),
|
||||
KemAlgorithm::X448 => x448_generate(),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 => Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5 (requires wolfSSL PQ build)".into(),
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 => Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,15 +42,13 @@ pub fn encapsulate(
|
|||
) -> Result<KemEncapResult, CryptoError> {
|
||||
match algo {
|
||||
KemAlgorithm::X25519 => x25519_encapsulate(recipient_public_key),
|
||||
KemAlgorithm::X448 => x448_encapsulate(recipient_public_key),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5".into()
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into()
|
||||
)),
|
||||
KemAlgorithm::X448 => x448_encapsulate(recipient_public_key),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 => Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5".into(),
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 => Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,15 +61,13 @@ pub fn decapsulate(
|
|||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
match algo {
|
||||
KemAlgorithm::X25519 => x25519_decapsulate(private_key, peer_ephemeral_public_key),
|
||||
KemAlgorithm::X448 => x448_decapsulate(private_key, peer_ephemeral_public_key),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5".into()
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 =>
|
||||
Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into()
|
||||
)),
|
||||
KemAlgorithm::X448 => x448_decapsulate(private_key, peer_ephemeral_public_key),
|
||||
KemAlgorithm::MlKem768 | KemAlgorithm::MlKem1024 => Err(CryptoError::FeatureNotCompiled(
|
||||
"ML-KEM is deferred to Phase 5".into(),
|
||||
)),
|
||||
KemAlgorithm::ClassicMcEliece460896 => Err(CryptoError::FeatureNotCompiled(
|
||||
"Classic-McEliece is deferred to Phase 5".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,41 +90,51 @@ fn x25519_generate() -> Result<KemKeyPair, CryptoError> {
|
|||
|
||||
let ret = crate::sys::wc_InitRng(&mut rng);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitRng returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitRng returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_curve25519_make_key(&mut rng, 32, &mut key);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve25519_make_key returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve25519_make_key returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let mut pub_sz = public_key.len() as u32;
|
||||
let mut pub_sz = public_key.len() as u32;
|
||||
let mut priv_sz = private_key.len() as u32;
|
||||
|
||||
// Export in little-endian (EC25519_LITTLE_ENDIAN=0) so all key
|
||||
// Export in little-endian (EC25519_LITTLE_ENDIAN=0) so all key
|
||||
// material is in RFC 7748 canonical format.
|
||||
let ret = crate::sys::wc_curve25519_export_key_raw_ex(
|
||||
&mut key,
|
||||
private_key.as_mut_ptr(), &mut priv_sz,
|
||||
public_key.as_mut_ptr(), &mut pub_sz,
|
||||
private_key.as_mut_ptr(),
|
||||
&mut priv_sz,
|
||||
public_key.as_mut_ptr(),
|
||||
&mut pub_sz,
|
||||
X25519_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve25519_free(&mut key);
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve25519_export_key_raw_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve25519_export_key_raw_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
crate::sys::wc_curve25519_free(&mut key);
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
}
|
||||
|
||||
Ok(KemKeyPair { public_key, private_key })
|
||||
Ok(KemKeyPair {
|
||||
public_key,
|
||||
private_key,
|
||||
})
|
||||
}
|
||||
|
||||
fn x25519_encapsulate(recipient_pub: &[u8]) -> Result<KemEncapResult, CryptoError> {
|
||||
|
|
@ -140,7 +144,7 @@ fn x25519_encapsulate(recipient_pub: &[u8]) -> Result<KemEncapResult, CryptoErro
|
|||
let shared = x25519_dh(&ephemeral.private_key, recipient_pub)?;
|
||||
Ok(KemEncapResult {
|
||||
// KemKeyPair implements ZeroizeOnDrop (adds Drop) so fields must be cloned.
|
||||
ciphertext: ephemeral.public_key.clone(),
|
||||
ciphertext: ephemeral.public_key.clone(),
|
||||
shared_secret: (*shared).clone(),
|
||||
})
|
||||
}
|
||||
|
|
@ -163,57 +167,63 @@ fn x25519_decapsulate(
|
|||
fn x25519_dh(private_key: &[u8], public_key: &[u8]) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
if private_key.len() != 32 || public_key.len() != 32 {
|
||||
return Err(CryptoError::InvalidKey(
|
||||
"X25519: keys must be 32 bytes each".into()
|
||||
"X25519: keys must be 32 bytes each".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut shared = Zeroizing::new(vec![0u8; 32]);
|
||||
|
||||
unsafe {
|
||||
let mut local_key: crate::sys::curve25519_key = std::mem::zeroed();
|
||||
let mut local_key: crate::sys::curve25519_key = std::mem::zeroed();
|
||||
let mut remote_key: crate::sys::curve25519_key = std::mem::zeroed();
|
||||
|
||||
// Initialise both key structs before use (sets dp, calls fe_init, etc.).
|
||||
let ret = crate::sys::wc_curve25519_init(&mut local_key);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve25519_init (local) returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve25519_init (local) returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_curve25519_init(&mut remote_key);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve25519_free(&mut local_key);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve25519_init (remote) returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve25519_init (remote) returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
// Import private key in little-endian (EC25519_LITTLE_ENDIAN = 0).
|
||||
let ret = crate::sys::wc_curve25519_import_private_ex(
|
||||
private_key.as_ptr(), 32,
|
||||
private_key.as_ptr(),
|
||||
32,
|
||||
&mut local_key,
|
||||
X25519_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve25519_free(&mut local_key);
|
||||
crate::sys::wc_curve25519_free(&mut remote_key);
|
||||
return Err(CryptoError::InvalidKey(
|
||||
format!("wc_curve25519_import_private_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_curve25519_import_private_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
// Import remote public key in little-endian.
|
||||
let ret = crate::sys::wc_curve25519_import_public_ex(
|
||||
public_key.as_ptr(), 32,
|
||||
public_key.as_ptr(),
|
||||
32,
|
||||
&mut remote_key,
|
||||
X25519_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve25519_free(&mut local_key);
|
||||
crate::sys::wc_curve25519_free(&mut remote_key);
|
||||
return Err(CryptoError::InvalidKey(
|
||||
format!("wc_curve25519_import_public_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_curve25519_import_public_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let mut shared_sz = 32u32;
|
||||
|
|
@ -231,9 +241,10 @@ fn x25519_dh(private_key: &[u8], public_key: &[u8]) -> Result<Zeroizing<Vec<u8>>
|
|||
crate::sys::wc_curve25519_free(&mut remote_key);
|
||||
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve25519_shared_secret_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve25519_shared_secret_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,56 +261,66 @@ fn x25519_dh(private_key: &[u8], public_key: &[u8]) -> Result<Zeroizing<Vec<u8>>
|
|||
const X448_LE: i32 = 0;
|
||||
|
||||
fn x448_generate() -> Result<KemKeyPair, CryptoError> {
|
||||
let mut public_key = vec![0u8; 56];
|
||||
let mut public_key = vec![0u8; 56];
|
||||
let mut private_key = vec![0u8; 56];
|
||||
|
||||
unsafe {
|
||||
let mut key: crate::sys::curve448_key = std::mem::zeroed();
|
||||
let mut rng: crate::sys::WC_RNG = std::mem::zeroed();
|
||||
let mut rng: crate::sys::WC_RNG = std::mem::zeroed();
|
||||
|
||||
let ret = crate::sys::wc_InitRng(&mut rng);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(format!("wc_InitRng returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitRng returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_curve448_make_key(&mut rng, 56, &mut key);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve448_make_key returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve448_make_key returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let mut pub_sz = public_key.len() as u32;
|
||||
let mut pub_sz = public_key.len() as u32;
|
||||
let mut priv_sz = private_key.len() as u32;
|
||||
|
||||
// Export in little-endian (EC448_LITTLE_ENDIAN=0), RFC 7748 canonical format.
|
||||
let ret = crate::sys::wc_curve448_export_key_raw_ex(
|
||||
&mut key,
|
||||
private_key.as_mut_ptr(), &mut priv_sz,
|
||||
public_key.as_mut_ptr(), &mut pub_sz,
|
||||
private_key.as_mut_ptr(),
|
||||
&mut priv_sz,
|
||||
public_key.as_mut_ptr(),
|
||||
&mut pub_sz,
|
||||
X448_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve448_free(&mut key);
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve448_export_key_raw_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve448_export_key_raw_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
crate::sys::wc_curve448_free(&mut key);
|
||||
crate::sys::wc_FreeRng(&mut rng);
|
||||
}
|
||||
|
||||
Ok(KemKeyPair { public_key, private_key })
|
||||
Ok(KemKeyPair {
|
||||
public_key,
|
||||
private_key,
|
||||
})
|
||||
}
|
||||
|
||||
fn x448_encapsulate(recipient_pub: &[u8]) -> Result<KemEncapResult, CryptoError> {
|
||||
let ephemeral = x448_generate()?;
|
||||
let shared = x448_dh(&ephemeral.private_key, recipient_pub)?;
|
||||
let shared = x448_dh(&ephemeral.private_key, recipient_pub)?;
|
||||
Ok(KemEncapResult {
|
||||
ciphertext: ephemeral.public_key.clone(),
|
||||
ciphertext: ephemeral.public_key.clone(),
|
||||
shared_secret: (*shared).clone(),
|
||||
})
|
||||
}
|
||||
|
|
@ -319,52 +340,64 @@ fn x448_decapsulate(
|
|||
/// pattern) before importing keys.
|
||||
fn x448_dh(private_key: &[u8], public_key: &[u8]) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
if private_key.len() != 56 || public_key.len() != 56 {
|
||||
return Err(CryptoError::InvalidKey("X448: keys must be 56 bytes each".into()));
|
||||
return Err(CryptoError::InvalidKey(
|
||||
"X448: keys must be 56 bytes each".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut shared = Zeroizing::new(vec![0u8; 56]);
|
||||
|
||||
unsafe {
|
||||
let mut local_key: crate::sys::curve448_key = std::mem::zeroed();
|
||||
let mut local_key: crate::sys::curve448_key = std::mem::zeroed();
|
||||
let mut remote_key: crate::sys::curve448_key = std::mem::zeroed();
|
||||
|
||||
// Initialise both key structs before use.
|
||||
let ret = crate::sys::wc_curve448_init(&mut local_key);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve448_init (local) returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve448_init (local) returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
let ret = crate::sys::wc_curve448_init(&mut remote_key);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve448_free(&mut local_key);
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve448_init (remote) returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve448_init (remote) returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
// Import private key in little-endian (EC448_LITTLE_ENDIAN = 0).
|
||||
let ret = crate::sys::wc_curve448_import_private_ex(
|
||||
private_key.as_ptr(), 56, &mut local_key, X448_LE,
|
||||
private_key.as_ptr(),
|
||||
56,
|
||||
&mut local_key,
|
||||
X448_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve448_free(&mut local_key);
|
||||
crate::sys::wc_curve448_free(&mut remote_key);
|
||||
return Err(CryptoError::InvalidKey(
|
||||
format!("wc_curve448_import_private_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_curve448_import_private_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
// Import remote public key in little-endian.
|
||||
let ret = crate::sys::wc_curve448_import_public_ex(
|
||||
public_key.as_ptr(), 56, &mut remote_key, X448_LE,
|
||||
public_key.as_ptr(),
|
||||
56,
|
||||
&mut remote_key,
|
||||
X448_LE,
|
||||
);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_curve448_free(&mut local_key);
|
||||
crate::sys::wc_curve448_free(&mut remote_key);
|
||||
return Err(CryptoError::InvalidKey(
|
||||
format!("wc_curve448_import_public_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_curve448_import_public_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let mut shared_sz = 56u32;
|
||||
|
|
@ -381,9 +414,10 @@ fn x448_dh(private_key: &[u8], public_key: &[u8]) -> Result<Zeroizing<Vec<u8>>,
|
|||
crate::sys::wc_curve448_free(&mut remote_key);
|
||||
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_curve448_shared_secret_ex returned {}", ret)
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_curve448_shared_secret_ex returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ pub mod provider;
|
|||
|
||||
// FFI bindings generated by bindgen in build.rs.
|
||||
// When wolfSSL headers are not present (e.g. docs.rs), use a stub.
|
||||
#[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code, clippy::all)]
|
||||
#[allow(
|
||||
non_camel_case_types,
|
||||
non_snake_case,
|
||||
non_upper_case_globals,
|
||||
dead_code,
|
||||
clippy::all
|
||||
)]
|
||||
mod sys {
|
||||
// ── Manual ABI-critical type definitions ────────────────────────────────
|
||||
//
|
||||
|
|
@ -112,48 +118,98 @@ mod sys {
|
|||
|
||||
// ── AES-GCM ─────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_AesGcmSetKey(aes: *mut Aes, key: *const c_uchar, len: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_AesGcmSetKey(aes: *mut Aes, key: *const c_uchar, len: c_uint) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_AesGcmEncrypt(
|
||||
aes: *mut Aes, out: *mut c_uchar, in_: *const c_uchar, sz: c_uint,
|
||||
iv: *const c_uchar, iv_sz: c_uint, auth_tag: *mut c_uchar, auth_tag_sz: c_uint,
|
||||
auth_in: *const c_uchar, auth_in_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
aes: *mut Aes,
|
||||
out: *mut c_uchar,
|
||||
in_: *const c_uchar,
|
||||
sz: c_uint,
|
||||
iv: *const c_uchar,
|
||||
iv_sz: c_uint,
|
||||
auth_tag: *mut c_uchar,
|
||||
auth_tag_sz: c_uint,
|
||||
auth_in: *const c_uchar,
|
||||
auth_in_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_AesGcmDecrypt(
|
||||
aes: *mut Aes, out: *mut c_uchar, in_: *const c_uchar, sz: c_uint,
|
||||
iv: *const c_uchar, iv_sz: c_uint, auth_tag: *const c_uchar, auth_tag_sz: c_uint,
|
||||
auth_in: *const c_uchar, auth_in_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_AesInit(aes: *mut Aes, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_AesFree(aes: *mut Aes) { unreachable!() }
|
||||
aes: *mut Aes,
|
||||
out: *mut c_uchar,
|
||||
in_: *const c_uchar,
|
||||
sz: c_uint,
|
||||
iv: *const c_uchar,
|
||||
iv_sz: c_uint,
|
||||
auth_tag: *const c_uchar,
|
||||
auth_tag_sz: c_uint,
|
||||
auth_in: *const c_uchar,
|
||||
auth_in_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_AesInit(aes: *mut Aes, heap: *mut c_void, dev_id: c_int) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_AesFree(aes: *mut Aes) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── ChaCha20-Poly1305 ────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_ChaCha20Poly1305_Encrypt(
|
||||
key: *const c_uchar, iv: *const c_uchar,
|
||||
aad: *const c_uchar, aad_sz: c_uint,
|
||||
in_: *const c_uchar, in_sz: c_uint,
|
||||
out: *mut c_uchar, auth_tag: *mut c_uchar,
|
||||
) -> c_int { unreachable!() }
|
||||
key: *const c_uchar,
|
||||
iv: *const c_uchar,
|
||||
aad: *const c_uchar,
|
||||
aad_sz: c_uint,
|
||||
in_: *const c_uchar,
|
||||
in_sz: c_uint,
|
||||
out: *mut c_uchar,
|
||||
auth_tag: *mut c_uchar,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_ChaCha20Poly1305_Decrypt(
|
||||
key: *const c_uchar, iv: *const c_uchar,
|
||||
aad: *const c_uchar, aad_sz: c_uint,
|
||||
in_: *const c_uchar, in_sz: c_uint,
|
||||
auth_tag: *const c_uchar, out: *mut c_uchar,
|
||||
) -> c_int { unreachable!() }
|
||||
key: *const c_uchar,
|
||||
iv: *const c_uchar,
|
||||
aad: *const c_uchar,
|
||||
aad_sz: c_uint,
|
||||
in_: *const c_uchar,
|
||||
in_sz: c_uint,
|
||||
auth_tag: *const c_uchar,
|
||||
out: *mut c_uchar,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_XChaCha20Poly1305_Encrypt(
|
||||
dst: *mut c_uchar, dst_space: usize,
|
||||
src: *const c_uchar, src_len: usize,
|
||||
ad: *const c_uchar, ad_len: usize,
|
||||
nonce: *const c_uchar, nonce_len: usize,
|
||||
key: *const c_uchar, key_len: usize,
|
||||
) -> c_int { unreachable!() }
|
||||
dst: *mut c_uchar,
|
||||
dst_space: usize,
|
||||
src: *const c_uchar,
|
||||
src_len: usize,
|
||||
ad: *const c_uchar,
|
||||
ad_len: usize,
|
||||
nonce: *const c_uchar,
|
||||
nonce_len: usize,
|
||||
key: *const c_uchar,
|
||||
key_len: usize,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_XChaCha20Poly1305_Decrypt(
|
||||
dst: *mut c_uchar, dst_space: usize,
|
||||
src: *const c_uchar, src_len: usize,
|
||||
ad: *const c_uchar, ad_len: usize,
|
||||
nonce: *const c_uchar, nonce_len: usize,
|
||||
key: *const c_uchar, key_len: usize,
|
||||
) -> c_int { unreachable!() }
|
||||
dst: *mut c_uchar,
|
||||
dst_space: usize,
|
||||
src: *const c_uchar,
|
||||
src_len: usize,
|
||||
ad: *const c_uchar,
|
||||
ad_len: usize,
|
||||
nonce: *const c_uchar,
|
||||
nonce_len: usize,
|
||||
key: *const c_uchar,
|
||||
key_len: usize,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── SHA-256 (init/update/final) ──────────────────────────────────────
|
||||
|
||||
|
|
@ -195,76 +251,161 @@ mod sys {
|
|||
|
||||
pub unsafe fn wc_InitBlake2b(b2b: *mut Blake2b, digest_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_InitBlake2b_WithKey(
|
||||
b2b: *mut Blake2b, digest_sz: c_uint,
|
||||
key: *const c_uchar, key_sz: c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Blake2bUpdate(b2b: *mut Blake2b, in_: *const c_uchar, in_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_Blake2bFinal(b2b: *mut Blake2b, final_: *mut c_uchar, request_sz: c_uint) -> c_int { unreachable!() }
|
||||
b2b: *mut Blake2b,
|
||||
digest_sz: c_uint,
|
||||
key: *const c_uchar,
|
||||
key_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_Blake2bUpdate(
|
||||
b2b: *mut Blake2b,
|
||||
in_: *const c_uchar,
|
||||
in_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_Blake2bFinal(
|
||||
b2b: *mut Blake2b,
|
||||
final_: *mut c_uchar,
|
||||
request_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── HMAC ────────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_HmacInit(hmac: *mut Hmac, heap: *mut c_void, dev_id: c_int) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_HmacSetKey(hmac: *mut Hmac, type_: c_int, key: *const c_uchar, key_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_HmacUpdate(hmac: *mut Hmac, in_: *const c_uchar, in_sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_HmacFinal(hmac: *mut Hmac, out: *mut c_uchar) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_HmacFree(hmac: *mut Hmac) { unreachable!() }
|
||||
pub unsafe fn wc_HmacSetKey(
|
||||
hmac: *mut Hmac,
|
||||
type_: c_int,
|
||||
key: *const c_uchar,
|
||||
key_sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── HKDF (TLS-1.3 Extract — RFC 5869 Extract step) ──────────────────
|
||||
|
||||
pub unsafe fn wc_Tls13_HKDF_Extract(
|
||||
prk: *mut c_uchar,
|
||||
salt: *const c_uchar, salt_len: c_uint,
|
||||
ikm: *mut c_uchar, ikm_len: c_uint,
|
||||
salt: *const c_uchar,
|
||||
salt_len: c_uint,
|
||||
ikm: *mut c_uchar,
|
||||
ikm_len: c_uint,
|
||||
digest: c_int,
|
||||
) -> c_int { unreachable!() }
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── RNG ─────────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_InitRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_RNG_GenerateBlock(rng: *mut WC_RNG, buf: *mut c_uchar, sz: c_uint) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_FreeRng(rng: *mut WC_RNG) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_RNG_GenerateBlock(
|
||||
rng: *mut WC_RNG,
|
||||
buf: *mut c_uchar,
|
||||
sz: c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── Curve25519 ───────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_curve25519_make_key(rng: *mut WC_RNG, key_sz: c_int, key: *mut curve25519_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve25519_init(key: *mut curve25519_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve25519_make_key(
|
||||
rng: *mut WC_RNG,
|
||||
key_sz: c_int,
|
||||
key: *mut curve25519_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve25519_export_key_raw(
|
||||
key: *mut curve25519_key,
|
||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||
pub_: *mut c_uchar, pub_sz: *mut c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
priv_: *mut c_uchar,
|
||||
priv_sz: *mut c_uint,
|
||||
pub_: *mut c_uchar,
|
||||
pub_sz: *mut c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve25519_import_private(
|
||||
priv_: *const c_uchar, priv_sz: c_uint, key: *mut curve25519_key,
|
||||
) -> c_int { unreachable!() }
|
||||
priv_: *const c_uchar,
|
||||
priv_sz: c_uint,
|
||||
key: *mut curve25519_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve25519_import_public(
|
||||
pub_: *const c_uchar, pub_sz: c_uint, key: *mut curve25519_key,
|
||||
) -> c_int { unreachable!() }
|
||||
pub_: *const c_uchar,
|
||||
pub_sz: c_uint,
|
||||
key: *mut curve25519_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve25519_shared_secret_ex(
|
||||
local: *mut curve25519_key, remote: *mut curve25519_key,
|
||||
out: *mut c_uchar, out_sz: *mut c_uint, endian: c_int,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve25519_free(key: *mut curve25519_key) { unreachable!() }
|
||||
local: *mut curve25519_key,
|
||||
remote: *mut curve25519_key,
|
||||
out: *mut c_uchar,
|
||||
out_sz: *mut c_uint,
|
||||
endian: c_int,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve25519_free(key: *mut curve25519_key) {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
// ── Curve448 ─────────────────────────────────────────────────────────
|
||||
|
||||
pub unsafe fn wc_curve448_make_key(rng: *mut WC_RNG, key_sz: c_int, key: *mut curve448_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve448_init(key: *mut curve448_key) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve448_make_key(
|
||||
rng: *mut WC_RNG,
|
||||
key_sz: c_int,
|
||||
key: *mut curve448_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_init(key: *mut curve448_key) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_export_key_raw(
|
||||
key: *mut curve448_key,
|
||||
priv_: *mut c_uchar, priv_sz: *mut c_uint,
|
||||
pub_: *mut c_uchar, pub_sz: *mut c_uint,
|
||||
) -> c_int { unreachable!() }
|
||||
priv_: *mut c_uchar,
|
||||
priv_sz: *mut c_uint,
|
||||
pub_: *mut c_uchar,
|
||||
pub_sz: *mut c_uint,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_import_private(
|
||||
priv_: *const c_uchar, priv_sz: c_uint, key: *mut curve448_key,
|
||||
) -> c_int { unreachable!() }
|
||||
priv_: *const c_uchar,
|
||||
priv_sz: c_uint,
|
||||
key: *mut curve448_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_import_public(
|
||||
pub_: *const c_uchar, pub_sz: c_uint, key: *mut curve448_key,
|
||||
) -> c_int { unreachable!() }
|
||||
pub_: *const c_uchar,
|
||||
pub_sz: c_uint,
|
||||
key: *mut curve448_key,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_shared_secret_ex(
|
||||
local: *mut curve448_key, remote: *mut curve448_key,
|
||||
out: *mut c_uchar, out_sz: *mut c_uint, endian: c_int,
|
||||
) -> c_int { unreachable!() }
|
||||
pub unsafe fn wc_curve448_free(key: *mut curve448_key) { unreachable!() }
|
||||
local: *mut curve448_key,
|
||||
remote: *mut curve448_key,
|
||||
out: *mut c_uchar,
|
||||
out_sz: *mut c_uint,
|
||||
endian: c_int,
|
||||
) -> c_int {
|
||||
unreachable!()
|
||||
}
|
||||
pub unsafe fn wc_curve448_free(key: *mut curve448_key) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +417,6 @@ pub use provider::WolfSslProvider;
|
|||
/// Call this once from `ccc_init()` in the bridge crate.
|
||||
pub fn init() {
|
||||
let provider = WolfSslProvider::new();
|
||||
ccc_crypto_core::ProviderRegistry::global()
|
||||
.register("wolfssl", Box::new(provider));
|
||||
ccc_crypto_core::ProviderRegistry::global().register("wolfssl", Box::new(provider));
|
||||
log::info!("[ccc-crypto-wolfssl] provider registered");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,20 +17,15 @@ const WC_HASH_TYPE_SHA512: i32 = 8;
|
|||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Compute a MAC tag over `data` using the specified algorithm and `key`.
|
||||
pub fn compute_mac(
|
||||
algo: MacAlgorithm,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
pub fn compute_mac(algo: MacAlgorithm, key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
match algo {
|
||||
MacAlgorithm::HmacSha256 => hmac(WC_HASH_TYPE_SHA256, key, data, 32),
|
||||
MacAlgorithm::HmacSha384 => hmac(WC_HASH_TYPE_SHA384, key, data, 48),
|
||||
MacAlgorithm::HmacSha512 => hmac(WC_HASH_TYPE_SHA512, key, data, 64),
|
||||
MacAlgorithm::Blake2bMac => blake2b_mac(key, data),
|
||||
MacAlgorithm::Poly1305 =>
|
||||
Err(CryptoError::UnsupportedAlgorithm(
|
||||
"Poly1305 standalone MAC is not exposed in this Phase".into()
|
||||
)),
|
||||
MacAlgorithm::Poly1305 => Err(CryptoError::UnsupportedAlgorithm(
|
||||
"Poly1305 standalone MAC is not exposed in this Phase".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,11 +66,8 @@ pub(crate) fn hmac(
|
|||
use std::alloc::{alloc_zeroed, dealloc, Layout};
|
||||
|
||||
// sizeof(Hmac) = 456 bytes; alignment = 16 (from embedded wc_Sha256).
|
||||
let layout = Layout::from_size_align(
|
||||
std::mem::size_of::<crate::sys::Hmac>().max(456),
|
||||
16,
|
||||
)
|
||||
.expect("layout must be valid");
|
||||
let layout = Layout::from_size_align(std::mem::size_of::<crate::sys::Hmac>().max(456), 16)
|
||||
.expect("layout must be valid");
|
||||
|
||||
let hmac_ptr = alloc_zeroed(layout) as *mut crate::sys::Hmac;
|
||||
if hmac_ptr.is_null() {
|
||||
|
|
@ -90,40 +82,40 @@ pub(crate) fn hmac(
|
|||
);
|
||||
if ret != 0 {
|
||||
dealloc(hmac_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_HmacInit returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_HmacInit returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_HmacSetKey(
|
||||
hmac_ptr,
|
||||
hash_type,
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
);
|
||||
let ret = crate::sys::wc_HmacSetKey(hmac_ptr, hash_type, key.as_ptr(), key.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_HmacFree(hmac_ptr);
|
||||
dealloc(hmac_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InvalidKey(format!("wc_HmacSetKey returned {}", ret)));
|
||||
return Err(CryptoError::InvalidKey(format!(
|
||||
"wc_HmacSetKey returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_HmacUpdate(
|
||||
hmac_ptr,
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
);
|
||||
let ret = crate::sys::wc_HmacUpdate(hmac_ptr, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
crate::sys::wc_HmacFree(hmac_ptr);
|
||||
dealloc(hmac_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_HmacUpdate returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_HmacUpdate returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_HmacFinal(
|
||||
hmac_ptr,
|
||||
out.as_mut_ptr(),
|
||||
);
|
||||
let ret = crate::sys::wc_HmacFinal(hmac_ptr, out.as_mut_ptr());
|
||||
if ret != 0 {
|
||||
crate::sys::wc_HmacFree(hmac_ptr);
|
||||
dealloc(hmac_ptr as *mut u8, layout);
|
||||
return Err(CryptoError::InternalError(format!("wc_HmacFinal returned {}", ret)));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_HmacFinal returned {}",
|
||||
ret
|
||||
)));
|
||||
}
|
||||
|
||||
crate::sys::wc_HmacFree(hmac_ptr);
|
||||
|
|
@ -146,7 +138,7 @@ pub(crate) fn hmac(
|
|||
fn blake2b_mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||
if key.is_empty() || key.len() > 64 {
|
||||
return Err(CryptoError::InvalidKey(
|
||||
"BLAKE2b-MAC: key must be 1–64 bytes".into()
|
||||
"BLAKE2b-MAC: key must be 1–64 bytes".into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -157,30 +149,26 @@ fn blake2b_mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
let mut b2b: crate::sys::Blake2b = std::mem::zeroed();
|
||||
|
||||
// Initialise with digest size and key for keyed (MAC) mode.
|
||||
let ret = crate::sys::wc_InitBlake2b_WithKey(
|
||||
&mut b2b,
|
||||
OUT_LEN,
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
);
|
||||
let ret =
|
||||
crate::sys::wc_InitBlake2b_WithKey(&mut b2b, OUT_LEN, key.as_ptr(), key.len() as u32);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_InitBlake2b_WithKey returned {ret}")
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_InitBlake2b_WithKey returned {ret}"
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_Blake2bUpdate(&mut b2b, data.as_ptr(), data.len() as u32);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bUpdate returned {ret}")
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Blake2bUpdate returned {ret}"
|
||||
)));
|
||||
}
|
||||
|
||||
let ret = crate::sys::wc_Blake2bFinal(&mut b2b, out.as_mut_ptr(), OUT_LEN);
|
||||
if ret != 0 {
|
||||
return Err(CryptoError::InternalError(
|
||||
format!("wc_Blake2bFinal returned {ret}")
|
||||
));
|
||||
return Err(CryptoError::InternalError(format!(
|
||||
"wc_Blake2bFinal returned {ret}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +189,10 @@ fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
|
|||
}
|
||||
// Accumulate XOR differences; the branch is on the final accumulated value
|
||||
// only, not on any individual byte.
|
||||
let diff: u8 = a.iter().zip(b.iter()).fold(0u8, |acc, (x, y)| acc | (x ^ y));
|
||||
let diff: u8 = a
|
||||
.iter()
|
||||
.zip(b.iter())
|
||||
.fold(0u8, |acc, (x, y)| acc | (x ^ y));
|
||||
diff == 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ use ccc_crypto_core::{
|
|||
algorithms::{AeadAlgorithm, HashAlgorithm, KdfAlgorithm, KemAlgorithm, MacAlgorithm},
|
||||
capabilities::ProviderCapabilities,
|
||||
error::CryptoError,
|
||||
provider::{
|
||||
AeadProvider, CryptoProvider, HashProvider, KdfProvider, KemProvider, MacProvider,
|
||||
},
|
||||
provider::{AeadProvider, CryptoProvider, HashProvider, KdfProvider, KemProvider, MacProvider},
|
||||
types::{AlgoTestResult, BenchmarkReport, KemEncapResult, KemKeyPair, SelfTestReport},
|
||||
};
|
||||
|
||||
|
|
@ -27,34 +25,34 @@ use crate::{aead, capabilities, hash, kdf, kem, mac};
|
|||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
struct AeadVector {
|
||||
algo: AeadAlgorithm,
|
||||
key: &'static str,
|
||||
nonce: &'static str,
|
||||
aad: &'static str,
|
||||
pt: &'static str,
|
||||
ct_tag: &'static str, // ciphertext || tag, hex-encoded
|
||||
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\
|
||||
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\
|
||||
algo: AeadAlgorithm::ChaCha20Poly1305,
|
||||
key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
|
||||
nonce: "070000004041424344454647",
|
||||
aad: "50515253c0c1c2c3c4c5c6c7",
|
||||
pt: "4c616469657320616e642047656e746c656d656e206f662074686520636c617373\
|
||||
206f6620273939",
|
||||
ct_tag: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63\
|
||||
dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b369\
|
||||
|
|
@ -97,14 +95,19 @@ impl WolfSslProvider {
|
|||
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());
|
||||
log::info!(
|
||||
"[ccc-crypto-wolfssl] benchmark complete ({} algos)",
|
||||
report.results.len()
|
||||
);
|
||||
report
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WolfSslProvider {
|
||||
fn default() -> Self { Self::new() }
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -113,15 +116,23 @@ impl Default for WolfSslProvider {
|
|||
|
||||
impl AeadProvider for WolfSslProvider {
|
||||
fn encrypt_aead(
|
||||
&self, algo: AeadAlgorithm, key: &[u8], nonce: &[u8],
|
||||
plaintext: &[u8], aad: &[u8],
|
||||
&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],
|
||||
&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)
|
||||
}
|
||||
|
|
@ -129,7 +140,12 @@ impl AeadProvider for WolfSslProvider {
|
|||
|
||||
impl KdfProvider for WolfSslProvider {
|
||||
fn derive_key(
|
||||
&self, algo: KdfAlgorithm, ikm: &[u8], salt: &[u8], info: &[u8], length: usize,
|
||||
&self,
|
||||
algo: KdfAlgorithm,
|
||||
ikm: &[u8],
|
||||
salt: &[u8],
|
||||
info: &[u8],
|
||||
length: usize,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
kdf::derive_key(algo, ikm, salt, info, length)
|
||||
}
|
||||
|
|
@ -137,13 +153,20 @@ impl KdfProvider for WolfSslProvider {
|
|||
|
||||
impl MacProvider for WolfSslProvider {
|
||||
fn compute_mac(
|
||||
&self, algo: MacAlgorithm, key: &[u8], data: &[u8],
|
||||
&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],
|
||||
&self,
|
||||
algo: MacAlgorithm,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
mac_bytes: &[u8],
|
||||
) -> Result<bool, CryptoError> {
|
||||
mac::verify_mac(algo, key, data, mac_bytes)
|
||||
}
|
||||
|
|
@ -162,12 +185,17 @@ impl KemProvider for WolfSslProvider {
|
|||
kem::generate_keypair(algo)
|
||||
}
|
||||
fn encapsulate(
|
||||
&self, algo: KemAlgorithm, public_key: &[u8],
|
||||
&self,
|
||||
algo: KemAlgorithm,
|
||||
public_key: &[u8],
|
||||
) -> Result<KemEncapResult, CryptoError> {
|
||||
kem::encapsulate(algo, public_key)
|
||||
}
|
||||
fn decapsulate(
|
||||
&self, algo: KemAlgorithm, private_key: &[u8], ciphertext: &[u8],
|
||||
&self,
|
||||
algo: KemAlgorithm,
|
||||
private_key: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
kem::decapsulate(algo, private_key, ciphertext)
|
||||
}
|
||||
|
|
@ -178,13 +206,17 @@ impl KemProvider for WolfSslProvider {
|
|||
// ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
impl CryptoProvider for WolfSslProvider {
|
||||
fn provider_name(&self) -> &'static str { "wolfssl" }
|
||||
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()
|
||||
self.caps
|
||||
.get_or_init(|| {
|
||||
let bench = self.get_or_run_benchmark();
|
||||
capabilities::probe_capabilities(Some(bench))
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn self_test(&self) -> SelfTestReport {
|
||||
|
|
@ -218,10 +250,10 @@ fn run_aead_vector(v: &AeadVector) -> AlgoTestResult {
|
|||
.collect()
|
||||
};
|
||||
|
||||
let key = decode(v.key);
|
||||
let nonce = decode(v.nonce);
|
||||
let aad = decode(v.aad);
|
||||
let pt = decode(v.pt);
|
||||
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) {
|
||||
|
|
@ -237,7 +269,8 @@ fn run_aead_vector(v: &AeadVector) -> AlgoTestResult {
|
|||
passed: false,
|
||||
error_message: Some(format!(
|
||||
"output mismatch: got {} bytes, expected {} bytes",
|
||||
ct_tag.len(), expected.len()
|
||||
ct_tag.len(),
|
||||
expected.len()
|
||||
)),
|
||||
},
|
||||
Err(e) => AlgoTestResult {
|
||||
|
|
|
|||
|
|
@ -61,17 +61,17 @@ struct HashVec {
|
|||
/// 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,
|
||||
name: &'static str,
|
||||
algo: KemAlgorithm,
|
||||
/// Alice's static private key (little-endian).
|
||||
alice_private: &'static str,
|
||||
alice_private: &'static str,
|
||||
/// Alice's corresponding public key (little-endian), used for the Bob→Alice
|
||||
/// direction check.
|
||||
alice_public: &'static str,
|
||||
alice_public: &'static str,
|
||||
/// Bob's static private key (little-endian).
|
||||
bob_private: &'static str,
|
||||
bob_private: &'static str,
|
||||
/// Bob's corresponding public key (little-endian).
|
||||
bob_public: &'static str,
|
||||
bob_public: &'static str,
|
||||
/// Expected shared secret (little-endian).
|
||||
expected_shared: &'static str,
|
||||
}
|
||||
|
|
@ -347,12 +347,24 @@ fn run_aead(p: &WolfSslProvider, failures: &mut usize) {
|
|||
// 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(_) => {
|
||||
*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); }
|
||||
Ok(ct_tag) => {
|
||||
*failures += 1;
|
||||
fail(v.name, &ct_tag, &expected);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} (encrypt error: {e})", v.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -367,8 +379,14 @@ fn run_kdf(p: &WolfSslProvider, failures: &mut usize) {
|
|||
|
||||
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); }
|
||||
Ok(out) => {
|
||||
*failures += 1;
|
||||
fail(v.name, &out, &expected);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} ({e})", v.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,12 +403,24 @@ fn run_mac(p: &WolfSslProvider, failures: &mut usize) {
|
|||
// 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(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); }
|
||||
Ok(tag) => {
|
||||
*failures += 1;
|
||||
fail(v.name, &tag, &expected);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} ({e})", v.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -403,8 +433,14 @@ fn run_hash(p: &WolfSslProvider, failures: &mut usize) {
|
|||
|
||||
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); }
|
||||
Ok(digest) => {
|
||||
*failures += 1;
|
||||
fail(v.name, &digest, &expected);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} ({e})", v.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -431,16 +467,28 @@ fn run_kem(p: &WolfSslProvider, failures: &mut usize) {
|
|||
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); }
|
||||
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); }
|
||||
Ok(shared) => {
|
||||
*failures += 1;
|
||||
fail(&test_b, &shared, &expected);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} ({e})", test_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -455,27 +503,33 @@ fn run_kem_roundtrip(p: &WolfSslProvider, failures: &mut usize) {
|
|||
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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -511,10 +565,22 @@ fn run_xchacha20_kat(p: &WolfSslProvider, failures: &mut usize) {
|
|||
// 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) {
|
||||
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); }
|
||||
Ok(_) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} (decrypt mismatch)", rt_name);
|
||||
}
|
||||
Err(e) => {
|
||||
*failures += 1;
|
||||
println!(" [FAIL] {} (decrypt error: {e})", rt_name);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Auth-failure check ────────────────────────────────────
|
||||
|
|
@ -522,10 +588,24 @@ fn run_xchacha20_kat(p: &WolfSslProvider, failures: &mut usize) {
|
|||
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); }
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue