use crate::ui;
use anyhow::{Context, Result};
use colored::Colorize;
use syntarq_core::crypto::{aes_gcm::AesGcmCipher, argon2, hashing::HashAlgorithm, traits::Cipher};
pub async fn run() -> Result<()> {
ui::header("๐งช Testing Cryptographic Functions");
let mut passed = 0;
let mut failed = 0;
ui::section("Test 1: Password Hashing (Argon2id)");
match test_password_hashing().await {
Ok(_) => {
ui::success("Password hashing works correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("Password hashing failed: {}", e));
failed += 1;
}
}
ui::section("Test 2: AES-256-GCM Encryption");
match test_aes_encryption().await {
Ok(_) => {
ui::success("AES-256-GCM encryption works correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("AES encryption failed: {}", e));
failed += 1;
}
}
ui::section("Test 3: Cryptographic Hashing");
match test_hashing().await {
Ok(_) => {
ui::success("Hashing algorithms work correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("Hashing failed: {}", e));
failed += 1;
}
}
ui::section("Test 4: Identity Creation");
match test_identity_creation().await {
Ok(_) => {
ui::success("Identity creation works correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("Identity creation failed: {}", e));
failed += 1;
}
}
ui::section("Test 5: Service Key Derivation");
match test_service_keys().await {
Ok(_) => {
ui::success("Service key derivation works correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("Service key derivation failed: {}", e));
failed += 1;
}
}
#[cfg(feature = "post-quantum")]
{
ui::section("Test 6: Post-Quantum Cryptography");
match test_post_quantum().await {
Ok(_) => {
ui::success("Post-quantum crypto works correctly");
passed += 1;
}
Err(e) => {
ui::error(&format!("Post-quantum crypto failed: {}", e));
failed += 1;
}
}
}
ui::separator();
ui::section("Test Summary");
ui::kv_pair("Passed", &format!("{}", passed).green().to_string());
if failed > 0 {
ui::kv_pair("Failed", &format!("{}", failed).red().to_string());
} else {
ui::kv_pair("Failed", &format!("{}", failed));
}
if failed == 0 {
ui::separator();
ui::success("All cryptographic tests passed! โ
");
} else {
ui::separator();
ui::error(&format!("{} test(s) failed", failed));
}
Ok(())
}
async fn test_password_hashing() -> Result<()> {
let password = b"test_password_123";
let hash = argon2::hash_password(password).context("Failed to hash password")?;
ui::info(&format!(" Hash length: {} bytes", hash.len()));
let valid = argon2::verify_password(password, &hash).context("Failed to verify password")?;
if !valid {
anyhow::bail!("Password verification failed for correct password");
}
ui::info(" Correct password verified โ");
let wrong_password = b"wrong_password";
let valid = argon2::verify_password(wrong_password, &hash)?;
if valid {
anyhow::bail!("Wrong password was incorrectly verified as valid");
}
ui::info(" Wrong password rejected โ");
Ok(())
}
async fn test_aes_encryption() -> Result<()> {
let data = b"Hello, Syntarq! This is a secret message.";
let cipher = AesGcmCipher::generate();
ui::info(" Cipher generated");
let encrypted = cipher.encrypt(data).context("Failed to encrypt data")?;
ui::info(&format!(
" Encrypted: {} bytes -> {} bytes",
data.len(),
encrypted.total_size()
));
let decrypted = cipher
.decrypt(&encrypted)
.context("Failed to decrypt data")?;
ui::info(&format!(" Decrypted: {} bytes", decrypted.len()));
if data.as_slice() != decrypted.as_slice() {
anyhow::bail!("Decrypted data doesn't match original");
}
ui::info(" Data integrity verified โ");
let aad = b"metadata";
let encrypted_aad = cipher
.encrypt_with_ad(data, aad)
.context("Failed to encrypt with AAD")?;
let decrypted_aad = cipher
.decrypt_with_ad(&encrypted_aad, aad)
.context("Failed to decrypt with AAD")?;
if data.as_slice() != decrypted_aad.as_slice() {
anyhow::bail!("AAD decrypted data doesn't match original");
}
ui::info(" AAD encryption verified โ");
Ok(())
}
async fn test_hashing() -> Result<()> {
let data = b"Test data for hashing";
let sha256_hash = syntarq_core::crypto::hashing::hash_data(data, HashAlgorithm::Sha256)?;
ui::info(&format!(" SHA-256: {} bytes", sha256_hash.len()));
if sha256_hash.len() != 32 {
anyhow::bail!("SHA-256 hash has incorrect length");
}
let blake3_hash = syntarq_core::crypto::hashing::hash_data(data, HashAlgorithm::Blake3)?;
ui::info(&format!(" BLAKE3: {} bytes", blake3_hash.len()));
if blake3_hash.len() != 32 {
anyhow::bail!("BLAKE3 hash has incorrect length");
}
let sha256_hash2 = syntarq_core::crypto::hashing::hash_data(data, HashAlgorithm::Sha256)?;
if sha256_hash != sha256_hash2 {
anyhow::bail!("Hashing is not deterministic");
}
ui::info(" Deterministic hashing verified โ");
Ok(())
}
async fn test_identity_creation() -> Result<()> {
use syntarq_core::identity::SyntarqIdentity;
let password = "test_identity_password_123";
let (identity, _hash) =
SyntarqIdentity::create(password).context("Failed to create identity")?;
ui::info(&format!(" Identity created: {}", identity.id()));
let unlocked = SyntarqIdentity::unlock(identity.id(), password, &_hash, None)
.context("Failed to unlock identity")?;
ui::info(" Identity unlocked โ");
if identity.id() != unlocked.id() {
anyhow::bail!("Identity IDs don't match after unlock");
}
ui::info(" Identity persistence verified โ");
Ok(())
}
async fn test_service_keys() -> Result<()> {
use syntarq_core::identity::{ServiceType, SyntarqIdentity};
let password = "service_key_test_123";
let (identity, _) = SyntarqIdentity::create(password).context("Failed to create identity")?;
for service_type in ServiceType::all() {
let key = identity
.derive_service_key(*service_type)
.context("Failed to derive service key")?;
ui::info(&format!(
" {} key: {} bytes",
service_type,
key.as_bytes().len()
));
if key.as_bytes().len() != 32 {
anyhow::bail!("Service key has incorrect length");
}
}
let key1 = identity.derive_service_key(ServiceType::Pass)?;
let key2 = identity.derive_service_key(ServiceType::Pass)?;
if key1.as_bytes() != key2.as_bytes() {
anyhow::bail!("Service key derivation is not deterministic");
}
ui::info(" Deterministic key derivation verified โ");
Ok(())
}
#[cfg(feature = "post-quantum")]
async fn test_post_quantum() -> Result<()> {
use syntarq_core::crypto::pq::{
dilithium::DilithiumSigner, hybrid::HybridCipher, kyber::KyberCipher,
};
ui::info(" Testing Kyber-1024...");
let kyber = KyberCipher::new().context("Failed to create Kyber cipher")?;
let (pk, sk) = kyber
.generate_keypair()
.context("Failed to generate Kyber keypair")?;
let (ct, ss1) = kyber
.encapsulate(&pk)
.context("Failed to encapsulate with Kyber")?;
let ss2 = kyber
.decapsulate(&sk, &ct)
.context("Failed to decapsulate with Kyber")?;
if ss1.as_bytes() != ss2.as_bytes() {
anyhow::bail!("Kyber shared secrets don't match");
}
ui::info(" Kyber KEM working โ");
ui::info(" Testing Dilithium5...");
let signer = DilithiumSigner::new().context("Failed to create Dilithium signer")?;
let (pk, sk) = signer
.generate_keypair()
.context("Failed to generate Dilithium keypair")?;
let message = b"Test message for signing";
let signature = signer
.sign(message, &sk)
.context("Failed to sign message")?;
let valid = signer
.verify(message, &signature, &pk)
.context("Failed to verify signature")?;
if !valid {
anyhow::bail!("Dilithium signature verification failed");
}
ui::info(" Dilithium signatures working โ");
ui::info(" Testing Hybrid Encryption...");
let cipher = HybridCipher::new().context("Failed to create hybrid cipher")?;
let (pk, sk) = cipher
.generate_keypair()
.context("Failed to generate hybrid keypair")?;
let data = b"Quantum-resistant secret data";
let ciphertext = cipher
.hybrid_encrypt(data, &pk)
.context("Failed to hybrid encrypt")?;
let plaintext = cipher
.hybrid_decrypt(&ciphertext, &sk)
.context("Failed to hybrid decrypt")?;
if data.as_slice() != plaintext.as_slice() {
anyhow::bail!("Hybrid decrypted data doesn't match original");
}
ui::info(" Hybrid encryption working โ");
Ok(())
}