use crate::algorithms::ed25519::{ed25519_to_x25519_private, Ed25519Derivation, X25519PrivateKey, X25519PublicKey};
use crate::algorithms::secp256k1::{Secp256k1Derivation, Secp256k1PrivateKey, Secp256k1PublicKey};
use crate::algorithms::secp256r1::{Secp256r1Derivation, Secp256r1PrivateKey, Secp256r1PublicKey};
use crate::algorithms::{KeyDerivation, PrivateKey};
use crate::IntoSecret;
pub const TEST_SEED: &[u8] = b"able able able able able able able able able able able able";
pub const TEST_SEED_ALTERNATE: &[u8] = b"art art art art art art art art art art art art";
pub fn create_keypair<T, D>(seed: &str, suffix: Option<&str>) -> Result<(T, T::PublicKey), crate::error::CryptoError>
where
T: PrivateKey,
D: KeyDerivation<PrivateKey = T>,
{
let mut seed_bytes = seed.as_bytes().to_vec();
if let Some(suffix) = suffix {
seed_bytes.extend_from_slice(suffix.as_bytes());
}
let private_key = D::derive_from_seed(seed_bytes.into_secret())?;
let public_key = private_key.as_public_key();
Ok((private_key, public_key))
}
pub fn create_secp256k1_keypair(
seed: &str,
suffix: Option<&str>,
) -> Result<(Secp256k1PrivateKey, Secp256k1PublicKey), crate::error::CryptoError> {
create_keypair::<Secp256k1PrivateKey, Secp256k1Derivation>(seed, suffix)
}
pub fn create_secp256r1_keypair(
seed: &str,
suffix: Option<&str>,
) -> Result<(Secp256r1PrivateKey, Secp256r1PublicKey), crate::error::CryptoError> {
create_keypair::<Secp256r1PrivateKey, Secp256r1Derivation>(seed, suffix)
}
pub fn create_x25519_keypair(
seed: &str,
suffix: Option<&str>,
) -> Result<(X25519PrivateKey, X25519PublicKey), crate::error::CryptoError> {
let mut seed_bytes = seed.as_bytes().to_vec();
if let Some(suffix) = suffix {
seed_bytes.extend_from_slice(suffix.as_bytes());
}
let ed25519_private = Ed25519Derivation::derive_from_seed(seed_bytes.into_secret())?;
let x25519_private = ed25519_to_x25519_private(&ed25519_private)?;
let x25519_public = x25519_private.derive_public_key();
Ok((x25519_private, x25519_public))
}
macro_rules! test_key_derivation {
(
$derivation_type:ty,
$private_key_type:ty,
$public_key_type:ty,
$expected_key_len:expr,
$expected_hex_len:expr,
$seed_suffix:expr
) => {
#[test]
fn test_key_derivation() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let private_bytes: SecretBox<Vec<u8>> = (&private_key).into();
let _recovered_private = <$private_key_type>::try_from(private_bytes.expose_secret().as_slice())?;
assert_eq!(
SecretBox::<Vec<u8>>::from(&private_key).expose_secret(),
SecretBox::<Vec<u8>>::from(&_recovered_private).expose_secret()
);
let public_bytes: Vec<u8> = (&public_key).into();
let recovered_public = <$public_key_type>::try_from(public_bytes.as_slice())?;
assert_eq!(Vec::<u8>::from(&public_key), Vec::<u8>::from(&recovered_public));
let hex_formatted = hex::encode(Vec::<u8>::from(&public_key));
assert_eq!(hex_formatted.len(), $expected_hex_len);
let public_key_bytes = Vec::<u8>::from(&public_key);
assert_eq!(public_key_bytes.len(), $expected_key_len);
Ok(())
}
#[test]
fn test_deterministic() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED_ALTERNATE;
let key1 = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let key2 = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
assert_eq!(
SecretBox::<Vec<u8>>::from(&key1).expose_secret(),
SecretBox::<Vec<u8>>::from(&key2).expose_secret()
);
let (pub1, pub2) = (key1.as_public_key(), key2.as_public_key());
assert_eq!(Vec::<u8>::from(&pub1), Vec::<u8>::from(&pub2));
Ok(())
}
#[test]
fn test_different_seeds() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed1 = crate::test_utils::TEST_SEED.to_vec();
let mut seed2 = crate::test_utils::TEST_SEED.to_vec();
seed1.extend_from_slice(b"_seed1");
seed2.extend_from_slice(b"_seed2");
let key1 = <$derivation_type>::derive_from_seed(seed1.into_secret())?;
let key2 = <$derivation_type>::derive_from_seed(seed2.into_secret())?;
assert_ne!(
SecretBox::<Vec<u8>>::from(&key1).expose_secret(),
SecretBox::<Vec<u8>>::from(&key2).expose_secret()
);
let (pub1, pub2) = (key1.as_public_key(), key2.as_public_key());
assert_ne!(Vec::<u8>::from(&pub1), Vec::<u8>::from(&pub2));
Ok(())
}
#[test]
fn test_serialization_roundtrip() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let private_bytes: SecretBox<Vec<u8>> = (&private_key).into();
let _recovered_private = <$private_key_type>::try_from(private_bytes.expose_secret().as_slice())?;
let public_bytes: Vec<u8> = (&public_key).into();
let recovered_public = <$public_key_type>::try_from(public_bytes.as_slice())?;
let original_pub_bytes = Vec::<u8>::from(&public_key);
let recovered_pub_bytes = Vec::<u8>::from(&recovered_public);
assert_eq!(original_pub_bytes, recovered_pub_bytes);
Ok(())
}
};
}
macro_rules! test_crypto_utils {
(
$derivation_type:ty,
$private_key_type:ty,
$expected_key_size:expr,
$algo_name:expr,
$seed_suffix:expr
) => {
#[test]
fn test_key_derivation_utility_methods() {
let valid_key = [0x01; $expected_key_size]; assert!(<$derivation_type>::is_valid_key_material(valid_key));
let invalid_key = [0x01; 16]; assert!(!<$derivation_type>::is_valid_key_material(invalid_key));
let zero_key = [0x00; $expected_key_size];
if $algo_name != "ed25519" {
assert!(!<$derivation_type>::is_valid_key_material(zero_key));
}
assert_eq!(<$derivation_type>::key_size(), $expected_key_size);
}
#[test]
fn test_debug_formatting() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed = crate::test_utils::TEST_SEED.to_vec();
seed.extend_from_slice(concat!("_", $seed_suffix, "_debug").as_bytes());
let private_key = <$derivation_type>::derive_from_seed(seed.into_secret())?;
let debug_string = format!("{private_key:?}");
assert!(debug_string.contains(concat!(stringify!($private_key_type))));
assert!(debug_string.contains("[REDACTED]"));
assert!(!debug_string.contains("SecretKey"));
Ok(())
}
};
}
#[cfg(feature = "signature")]
macro_rules! test_signatures {
(
$derivation_type:ty,
$seed_suffix:expr
) => {
const TEST_MESSAGE: &[u8] = b"test message for signing";
#[test]
fn test_signing_operations() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let signature = private_key.try_sign(TEST_MESSAGE)?;
assert!(public_key.verify(TEST_MESSAGE, &signature).is_ok());
let wrong_message = b"wrong message";
assert!(public_key.verify(wrong_message, &signature).is_err());
Ok(())
}
#[test]
fn test_signature_deterministic() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let signature1 = private_key.try_sign(TEST_MESSAGE)?;
let signature2 = private_key.try_sign(TEST_MESSAGE)?;
assert_eq!(signature1.to_bytes(), signature2.to_bytes());
Ok(())
}
#[test]
fn test_signature_verification_failures() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut alice_seed = crate::test_utils::TEST_SEED.to_vec();
let mut bob_seed = crate::test_utils::TEST_SEED.to_vec();
alice_seed.extend_from_slice(b"_alice");
bob_seed.extend_from_slice(b"_bob");
let alice_private = <$derivation_type>::derive_from_seed(alice_seed.into_secret())?;
let alice_public = alice_private.as_public_key();
let bob_private = <$derivation_type>::derive_from_seed(bob_seed.into_secret())?;
let bob_public = bob_private.as_public_key();
let alice_signature = alice_private.try_sign(TEST_MESSAGE)?;
assert!(alice_public.verify(TEST_MESSAGE, &alice_signature).is_ok());
assert!(bob_public.verify(TEST_MESSAGE, &alice_signature).is_err());
let bob_signature = bob_private.try_sign(TEST_MESSAGE)?;
assert_ne!(alice_signature.to_bytes(), bob_signature.to_bytes());
assert!(bob_public.verify(TEST_MESSAGE, &bob_signature).is_ok());
Ok(())
}
#[test]
fn test_crypto_signer_ext_trait() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
assert!(private_key.has_private_key());
let algorithm = private_key.to_algorithm();
let expected_algorithm = match stringify!($seed_suffix) {
"\"ed25519\"" => crate::algorithms::Algorithm::Ed25519,
"\"secp256k1\"" => crate::algorithms::Algorithm::Secp256k1,
"\"secp256r1\"" => crate::algorithms::Algorithm::Secp256r1,
_ => return Err("Unknown algorithm".into()),
};
assert_eq!(algorithm, expected_algorithm);
let verifying_key = private_key.verifying_key();
assert!(!verifying_key.public_key_bytes().is_empty());
let expected_public_key = private_key.as_public_key();
assert_eq!(verifying_key.public_key_bytes(), Vec::<u8>::from(&expected_public_key));
Ok(())
}
#[test]
fn test_crypto_verifier_ext_trait() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let public_key_bytes = public_key.public_key_bytes();
let expected_len = match stringify!($seed_suffix) {
"\"ed25519\"" => 32, "\"secp256k1\"" => 33, "\"secp256r1\"" => 33, _ => return Err("Unknown algorithm".into()),
};
assert_eq!(public_key_bytes.len(), expected_len);
Ok(())
}
#[test]
fn test_crypto_signer_with_options() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let message = b"test message for signing with options";
let default_options = crate::operations::signature::SigningOptions::default();
let signature_default = private_key.sign_with_options(message, default_options)?;
let raw_options = crate::operations::signature::SigningOptions::raw();
let different_hash = [0x42u8; 32]; let signature_raw = private_key.sign_with_options(different_hash, raw_options)?;
let cert_options = crate::operations::signature::SigningOptions::for_cert();
let signature_cert = private_key.sign_with_options(message, cert_options)?;
assert_ne!(signature_default.to_bytes(), signature_raw.to_bytes());
if stringify!($seed_suffix) == "\"ed25519\"" {
assert_eq!(signature_default.to_bytes(), signature_cert.to_bytes());
} else {
assert_ne!(signature_default.to_bytes(), signature_cert.to_bytes());
}
let regular_signature = private_key.try_sign(message)?;
assert_ne!(regular_signature.to_bytes(), signature_default.to_bytes());
Ok(())
}
#[test]
fn test_crypto_verifier_with_options() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let message = b"test message for verification with options";
let default_options = crate::operations::signature::SigningOptions::default();
let signature_default = private_key.sign_with_options(message, default_options)?;
assert!(public_key
.verify_with_options(message, &signature_default, default_options)
.is_ok());
let raw_options = crate::operations::signature::SigningOptions::raw();
let pre_computed_hash = crate::hash::hash_default(message);
let signature_raw = private_key.sign_with_options(pre_computed_hash, raw_options)?;
assert!(public_key
.verify_with_options(pre_computed_hash, &signature_raw, raw_options)
.is_ok());
let cert_options = crate::operations::signature::SigningOptions::for_cert();
let signature_cert = private_key.sign_with_options(message, cert_options)?;
assert!(public_key
.verify_with_options(message, &signature_cert, cert_options)
.is_ok());
assert!(public_key
.verify_with_options(pre_computed_hash, &signature_raw, default_options)
.is_err());
assert!(public_key
.verify_with_options(message, &signature_default, raw_options)
.is_err());
let wrong_message = b"wrong message";
assert!(public_key
.verify_with_options(wrong_message, &signature_default, default_options)
.is_err());
Ok(())
}
};
}
#[cfg(feature = "encryption")]
macro_rules! test_key_exchange {
(
$derivation_type:ty,
$seed_suffix:expr
) => {
#[test]
fn test_ecdh_operations() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut alice_seed = crate::test_utils::TEST_SEED.to_vec();
let mut bob_seed = crate::test_utils::TEST_SEED.to_vec();
alice_seed.extend_from_slice(b"_alice");
bob_seed.extend_from_slice(b"_bob");
let alice_private = <$derivation_type>::derive_from_seed(alice_seed.into_secret())?;
let alice_public = alice_private.as_public_key();
let bob_private = <$derivation_type>::derive_from_seed(bob_seed.into_secret())?;
let bob_public = bob_private.as_public_key();
let alice_shared = alice_private.ecdh(&bob_public)?;
let bob_shared = bob_private.ecdh(&alice_public)?;
assert_eq!(alice_shared, bob_shared);
assert!(!alice_shared.is_empty());
Ok(())
}
#[test]
fn test_ecdh_consistency() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed1 = crate::test_utils::TEST_SEED.to_vec();
let mut seed2 = crate::test_utils::TEST_SEED.to_vec();
seed1.extend_from_slice(b"_test1");
seed2.extend_from_slice(b"_test2");
let key1 = <$derivation_type>::derive_from_seed(seed1.into_secret())?;
let key2 = <$derivation_type>::derive_from_seed(seed2.into_secret())?;
let pub1 = key1.as_public_key();
let pub2 = key2.as_public_key();
let shared1 = key1.ecdh(&pub2)?;
let shared2 = key2.ecdh(&pub1)?;
assert_eq!(shared1, shared2);
let self_shared = key1.ecdh(&pub1)?;
assert!(!self_shared.is_empty());
Ok(())
}
};
}
#[cfg(feature = "encryption")]
macro_rules! test_ecdh {
(
$derivation_type:ty,
$private_key_type:ty,
$public_key_type:ty,
$seed_suffix:expr
) => {
#[test]
fn test_ecdh_key_exchange_trait() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed1 = crate::test_utils::TEST_SEED.to_vec();
let mut seed2 = crate::test_utils::TEST_SEED_ALTERNATE.to_vec();
seed1.extend_from_slice(concat!("_", $seed_suffix, "_1").as_bytes());
seed2.extend_from_slice(concat!("_", $seed_suffix, "_2").as_bytes());
let private_key1 = <$derivation_type>::derive_from_seed(seed1.into_secret())?;
let private_key2 = <$derivation_type>::derive_from_seed(seed2.into_secret())?;
let public_key1 = private_key1.as_public_key();
let public_key2 = private_key2.as_public_key();
let shared_secret1 = private_key1.ecdh(&public_key2)?;
let shared_secret2 = private_key2.ecdh(&public_key1)?;
assert_eq!(shared_secret1, shared_secret2);
assert!(!shared_secret1.is_empty());
let public_key2_bytes: Vec<u8> = (&public_key2).into();
let shared_secret1_bytes = private_key1.key_exchange(&public_key2_bytes)?;
assert_eq!(shared_secret1, shared_secret1_bytes);
let public_key1_bytes: Vec<u8> = (&public_key1).into();
let shared_secret2_bytes = private_key2.key_exchange(&public_key1_bytes)?;
assert_eq!(shared_secret2, shared_secret2_bytes);
let mut seed3 = crate::test_utils::TEST_SEED.to_vec();
seed3.extend_from_slice(concat!("_", $seed_suffix, "_3").as_bytes());
let private_key3 = <$derivation_type>::derive_from_seed(seed3.into_secret())?;
let public_key3 = private_key3.as_public_key();
let shared_secret3 = private_key1.ecdh(&public_key3)?;
assert_ne!(shared_secret1, shared_secret3);
let invalid_public_key = vec![0u8; 32]; let result = private_key1.key_exchange(&invalid_public_key);
assert!(result.is_err());
let aead_result = private_key1.derive_aead_key::<aes_gcm::Aes256Gcm>(&shared_secret1);
assert!(aead_result.is_err());
assert!(matches!(aead_result, Err(CryptoError::EncryptionNotSupported)));
Ok(())
}
};
}
#[cfg(any(feature = "der", feature = "rasn"))]
macro_rules! test_der {
(
$derivation_type:ty,
$expected_oid:expr,
$seed_suffix:expr
) => {
#[test]
fn test_oid_conversion() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed = crate::test_utils::TEST_SEED.to_vec();
seed.extend_from_slice(concat!("_", $seed_suffix, "_oid").as_bytes());
let private_key = <$derivation_type>::derive_from_seed(seed.into_secret())?;
let public_key = private_key.as_public_key();
let oid: keetanetwork_asn1::ObjectIdentifier = public_key.into();
assert_eq!(oid.to_string(), $expected_oid);
let oid: keetanetwork_asn1::ObjectIdentifier = private_key.into();
assert_eq!(oid.to_string(), $expected_oid);
Ok(())
}
};
}
#[cfg(feature = "encryption")]
macro_rules! test_asymmetric_encryption {
(
$derivation_type:ty,
$seed_suffix:expr
) => {
#[test]
fn test_asymmetric_encryption_trait() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let public_key = private_key.as_public_key();
let plaintext = b"test message for asymmetric encryption trait";
let ciphertext_from_private = private_key.encrypt(plaintext)?;
assert!(!ciphertext_from_private.is_empty());
assert_ne!(ciphertext_from_private.as_slice(), plaintext);
let ciphertext_from_public = public_key.encrypt(plaintext)?;
assert!(!ciphertext_from_public.is_empty());
assert_ne!(ciphertext_from_public.as_slice(), plaintext);
assert_ne!(ciphertext_from_private, ciphertext_from_public);
let decrypted_from_private = private_key.decrypt(&ciphertext_from_private)?;
assert_eq!(decrypted_from_private, plaintext);
let decrypted_from_public = private_key.decrypt(&ciphertext_from_public)?;
assert_eq!(decrypted_from_public, plaintext);
let decrypt_result1 = public_key.decrypt(&ciphertext_from_private);
assert!(matches!(decrypt_result1, Err(CryptoError::InvalidOperation)));
Ok(())
}
#[test]
fn test_asymmetric_encryption_round_trip() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = crate::test_utils::TEST_SEED;
let private_key = <$derivation_type>::derive_from_seed(seed.to_vec().into_secret())?;
let plaintext = b"round trip test data with various characters: 123!@#$%^&*()";
let encrypted = private_key.encrypt(plaintext)?;
assert_ne!(encrypted.as_slice(), plaintext);
let decrypted = private_key.decrypt(&encrypted)?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn test_asymmetric_encryption_different_keys() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let mut seed1 = crate::test_utils::TEST_SEED.to_vec();
let mut seed2 = crate::test_utils::TEST_SEED_ALTERNATE.to_vec();
seed1.extend_from_slice(concat!("_", $seed_suffix, "_enc1").as_bytes());
seed2.extend_from_slice(concat!("_", $seed_suffix, "_enc2").as_bytes());
let private_key1 = <$derivation_type>::derive_from_seed(seed1.into_secret())?;
let private_key2 = <$derivation_type>::derive_from_seed(seed2.into_secret())?;
let public_key2 = private_key2.as_public_key();
let plaintext = b"test cross-key encryption";
let ciphertext = public_key2.encrypt(plaintext)?;
let decrypted = private_key2.decrypt(&ciphertext)?;
assert_eq!(decrypted, plaintext);
let decrypt_result = private_key1.decrypt(&ciphertext);
assert!(decrypt_result.is_err());
Ok(())
}
};
}
#[cfg(feature = "encryption")]
macro_rules! test_ecies {
(
$mod_name:ident,
$ecies_type:ty,
$create_keypair_fn:ident
) => {
mod $mod_name {
use super::*;
use crate::error::CryptoError;
use crate::operations::encryption::AsymmetricEncryption;
#[test]
fn basic() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (private_key, public_key) = $create_keypair_fn(seed, None)?;
let plaintext = b"Hello, ECIES world!";
let ciphertext = <$ecies_type>::encrypt(&public_key, plaintext)?;
assert_ne!(ciphertext.as_slice(), plaintext);
assert!(ciphertext.len() > plaintext.len());
let decrypted = <$ecies_type>::decrypt(&private_key, &ciphertext)?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn trait_implementation() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (private_key, public_key) = $create_keypair_fn(seed, None)?;
let plaintext = b"Testing AsymmetricEncryption trait";
let ciphertext: Vec<u8> = public_key.encrypt(plaintext)?;
let decrypted: Vec<u8> = private_key.decrypt(&ciphertext)?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn different_keys() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (alice_private, alice_public) = $create_keypair_fn(seed, Some("alice"))?;
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED_ALTERNATE)?;
let (bob_private, bob_public) = $create_keypair_fn(seed, Some("bob"))?;
let plaintext = b"Message from Alice to Bob";
let ciphertext_for_bob = <$ecies_type>::encrypt(&bob_public, plaintext)?;
let decrypted_by_bob = <$ecies_type>::decrypt(&bob_private, &ciphertext_for_bob)?;
assert_eq!(decrypted_by_bob, plaintext);
let alice_decrypt_result = <$ecies_type>::decrypt(&alice_private, &ciphertext_for_bob);
assert!(alice_decrypt_result.is_err());
let reverse_plaintext = b"Reply from Bob to Alice";
let ciphertext_for_alice = <$ecies_type>::encrypt(&alice_public, reverse_plaintext)?;
let decrypted_by_alice = <$ecies_type>::decrypt(&alice_private, &ciphertext_for_alice)?;
assert_eq!(decrypted_by_alice, reverse_plaintext);
let bob_decrypt_result = <$ecies_type>::decrypt(&bob_private, &ciphertext_for_alice);
assert!(bob_decrypt_result.is_err());
Ok(())
}
#[test]
fn ephemeral_keys() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (private_key, public_key) = $create_keypair_fn(seed, None)?;
let plaintext = b"Same message";
let ciphertext1 = <$ecies_type>::encrypt(&public_key, plaintext)?;
let ciphertext2 = <$ecies_type>::encrypt(&public_key, plaintext)?;
assert_ne!(ciphertext1, ciphertext2);
let decrypted1 = <$ecies_type>::decrypt(&private_key, &ciphertext1)?;
let decrypted2 = <$ecies_type>::decrypt(&private_key, &ciphertext2)?;
assert_eq!(decrypted1, plaintext);
assert_eq!(decrypted2, plaintext);
Ok(())
}
#[test]
fn invalid_ciphertext() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (private_key, _) = $create_keypair_fn(seed, None)?;
let short_ciphertext = [0u8; 50];
let result = <$ecies_type>::decrypt(&private_key, short_ciphertext);
assert!(matches!(result, Err(CryptoError::DecryptionFailed)));
Ok(())
}
#[test]
fn public_key_cannot_decrypt() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (_, public_key) = $create_keypair_fn(seed, None)?;
let fake_ciphertext = [0u8; 100];
let result: Result<Vec<u8>, CryptoError> = public_key.decrypt(fake_ciphertext);
assert!(matches!(result, Err(CryptoError::InvalidOperation)));
Ok(())
}
#[test]
fn short_cipher_boundary_condition() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let seed = core::str::from_utf8(crate::test_utils::TEST_SEED)?;
let (private_key, _) = $create_keypair_fn(seed, None)?;
let mut malformed_ciphertext = vec![0u8; 113];
malformed_ciphertext[0] = 0x04;
for (i, item) in malformed_ciphertext.iter_mut().enumerate().take(65).skip(1) {
*item = (i % 256) as u8;
}
malformed_ciphertext = vec![0u8; 112]; malformed_ciphertext[0] = 0x04;
for (i, item) in malformed_ciphertext.iter_mut().enumerate().take(65).skip(1) {
*item = (i % 256) as u8;
}
let result = <$ecies_type>::decrypt(&private_key, &malformed_ciphertext);
assert!(matches!(result, Err(CryptoError::DecryptionFailed)));
Ok(())
}
}
};
}
#[cfg(feature = "encryption")]
macro_rules! test_aes_symmetric {
(
$cipher_type:ty,
$key_size:expr,
$cipher_name:expr
) => {
#[test]
fn test_basic_encrypt_decrypt() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let cipher = <$cipher_type>::new();
let key = vec![0x42u8; $key_size];
let plaintext = b"Hello, AES encryption world!";
let ciphertext = cipher.encrypt(&key, None, plaintext)?;
assert_ne!(ciphertext.as_slice(), plaintext);
assert!(ciphertext.len() >= plaintext.len());
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, plaintext);
Ok(())
}
#[test]
fn test_cipher_properties() {
let cipher = <$cipher_type>::new();
assert_eq!(cipher.key_size(), $key_size);
assert_eq!(cipher.block_size(), 16);
}
#[test]
fn test_wrong_key_size() {
let cipher = <$cipher_type>::new();
let wrong_key = vec![0x42u8; $key_size + 1]; let plaintext = b"test";
let result = cipher.encrypt(&wrong_key, None, plaintext);
assert!(matches!(result, Err(CryptoError::InvalidKeySize)));
let fake_ciphertext = vec![0u8; 32];
let result = cipher.decrypt(&wrong_key, &fake_ciphertext);
assert!(matches!(result, Err(CryptoError::InvalidKeySize)));
}
#[test]
fn test_random_iv_different_ciphertexts() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let cipher = <$cipher_type>::new();
let key = vec![0x42u8; $key_size];
let plaintext = b"Same plaintext for randomness test";
let ciphertext1 = cipher.encrypt(&key, None, plaintext)?;
let ciphertext2 = cipher.encrypt(&key, None, plaintext)?;
assert_ne!(ciphertext1, ciphertext2);
let decrypted1 = cipher.decrypt(&key, &ciphertext1)?;
let decrypted2 = cipher.decrypt(&key, &ciphertext2)?;
assert_eq!(decrypted1, plaintext);
assert_eq!(decrypted2, plaintext);
Ok(())
}
#[test]
fn test_various_plaintext_sizes() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let cipher = <$cipher_type>::new();
let key = vec![0x42u8; $key_size];
let empty_plaintext = b"";
let ciphertext = cipher.encrypt(&key, None, empty_plaintext)?;
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, empty_plaintext);
let single_byte = b"A";
let ciphertext = cipher.encrypt(&key, None, single_byte)?;
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, single_byte);
let block_aligned = b"0123456789ABCDEF"; let ciphertext = cipher.encrypt(&key, None, block_aligned)?;
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, block_aligned);
let non_aligned = b"Hello, this is a test message that is not block aligned!";
let ciphertext = cipher.encrypt(&key, None, non_aligned)?;
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, non_aligned);
let large_data = vec![0x55u8; 1024]; let ciphertext = cipher.encrypt(&key, None, &large_data)?;
let decrypted = cipher.decrypt(&key, &ciphertext)?;
assert_eq!(decrypted, large_data);
Ok(())
}
#[test]
fn test_different_keys_different_results() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let plaintext = b"Test message for key difference verification";
let key1 = vec![0x42u8; $key_size];
let mut key2 = vec![0x42u8; $key_size];
key2[0] = 0x43;
let cipher = <$cipher_type>::new();
let ciphertext1 = cipher.encrypt(&key1, None, plaintext)?;
let ciphertext2 = cipher.encrypt(&key2, None, plaintext)?;
assert_ne!(ciphertext1, ciphertext2);
let decrypted1 = cipher.decrypt(&key1, &ciphertext1)?;
let decrypted2 = cipher.decrypt(&key2, &ciphertext2)?;
assert_eq!(decrypted1, plaintext);
assert_eq!(decrypted2, plaintext);
let wrong_decrypt1 = cipher.decrypt(&key2, &ciphertext1);
let wrong_decrypt2 = cipher.decrypt(&key1, &ciphertext2);
if let Ok(decrypted) = wrong_decrypt1 {
assert_ne!(decrypted, plaintext);
}
if let Ok(decrypted) = wrong_decrypt2 {
assert_ne!(decrypted, plaintext);
}
Ok(())
}
#[test]
fn test_deterministic_with_fixed_iv() -> Result<(), alloc::boxed::Box<dyn core::error::Error>> {
let cipher = <$cipher_type>::new();
let key = vec![0x42u8; $key_size];
let plaintext = b"Test deterministic encryption";
let fixed_iv = vec![0x12u8; 16];
let ciphertext1 = cipher.encrypt(&key, Some(&fixed_iv), plaintext)?;
let ciphertext2 = cipher.encrypt(&key, Some(&fixed_iv), plaintext)?;
assert_eq!(ciphertext1, ciphertext2);
let decrypted = cipher.decrypt(&key, &ciphertext1)?;
assert_eq!(decrypted, plaintext);
Ok(())
}
};
}
#[cfg(all(test, feature = "encryption"))]
pub(crate) use test_aes_symmetric;
#[cfg(all(test, feature = "encryption"))]
pub(crate) use test_asymmetric_encryption;
#[cfg(test)]
pub(crate) use test_crypto_utils;
#[cfg(all(test, any(feature = "der", feature = "rasn")))]
pub(crate) use test_der;
#[cfg(all(test, feature = "encryption"))]
pub(crate) use test_ecdh;
#[cfg(all(test, feature = "encryption"))]
pub(crate) use test_ecies;
#[cfg(test)]
pub(crate) use test_key_derivation;
#[cfg(all(test, feature = "encryption"))]
pub(crate) use test_key_exchange;
#[cfg(all(test, feature = "signature"))]
pub(crate) use test_signatures;