use soliton::primitives::aead::{aead_decrypt, aead_encrypt};
use soliton::primitives::argon2::{Argon2Params, argon2id};
use soliton::primitives::random::random_array;
const FAST: Argon2Params = Argon2Params {
m_cost: 8,
t_cost: 1,
p_cost: 1,
};
#[test]
fn passphrase_protects_keypair() {
let passphrase = b"correct horse battery staple";
let salt: [u8; 16] = random_array();
let nonce: [u8; 24] = random_array();
let keypair_bytes = b"secret identity keypair bytes (not real, just for test)";
let mut enc_key = [0u8; 32];
argon2id(passphrase, &salt, FAST, &mut enc_key).unwrap();
let ciphertext = aead_encrypt(&enc_key, &nonce, keypair_bytes, b"").unwrap();
let mut dec_key = [0u8; 32];
argon2id(passphrase, &salt, FAST, &mut dec_key).unwrap();
assert_eq!(enc_key, dec_key);
let plaintext = aead_decrypt(&dec_key, &nonce, &ciphertext, b"").unwrap();
assert_eq!(&*plaintext, keypair_bytes.as_slice());
}
#[test]
fn wrong_passphrase_fails_decryption() {
let salt: [u8; 16] = random_array();
let nonce: [u8; 24] = random_array();
let mut key_correct = [0u8; 32];
argon2id(b"correct passphrase", &salt, FAST, &mut key_correct).unwrap();
let ciphertext = aead_encrypt(&key_correct, &nonce, b"secret keypair", b"").unwrap();
let mut key_wrong = [0u8; 32];
argon2id(b"wrong passphrase", &salt, FAST, &mut key_wrong).unwrap();
assert!(
aead_decrypt(&key_wrong, &nonce, &ciphertext, b"").is_err(),
"decryption with wrong passphrase must fail"
);
}
#[test]
fn wrong_salt_fails_decryption() {
let salt_a = [0x11u8; 16];
let salt_b = [0x22u8; 16];
let nonce: [u8; 24] = random_array();
let mut key_a = [0u8; 32];
argon2id(b"passphrase", &salt_a, FAST, &mut key_a).unwrap();
let ciphertext = aead_encrypt(&key_a, &nonce, b"secret keypair", b"").unwrap();
let mut key_b = [0u8; 32];
argon2id(b"passphrase", &salt_b, FAST, &mut key_b).unwrap();
assert!(
aead_decrypt(&key_b, &nonce, &ciphertext, b"").is_err(),
"decryption with different salt must fail"
);
}
#[test]
fn empty_passphrase_is_accepted() {
let salt: [u8; 16] = [0x01u8; 16];
let mut out1 = [0u8; 32];
let mut out2 = [0u8; 32];
argon2id(b"", &salt, FAST, &mut out1).expect("empty passphrase must be accepted");
argon2id(b"", &salt, FAST, &mut out2).unwrap();
assert_eq!(
out1, out2,
"empty passphrase must produce deterministic output"
);
let mut out_nonempty = [0u8; 32];
argon2id(b"x", &salt, FAST, &mut out_nonempty).unwrap();
assert_ne!(
out1, out_nonempty,
"empty and non-empty passphrases must produce different keys"
);
}
#[test]
fn short_salt_returns_error() {
use soliton::error::Error;
let mut out = [0u8; 32];
let result = argon2id(b"passphrase", b"short", FAST, &mut out);
assert!(
matches!(
result,
Err(Error::InvalidLength {
expected: 8,
got: 5
})
),
"expected InvalidLength {{ expected: 8, got: 5 }}, got: {:?}",
result,
);
}
#[test]
#[ignore = "slow: uses production-grade Argon2 parameters (~0.5-2 s)"]
fn recommended_params_produce_output() {
let mut out = [0u8; 32];
argon2id(
b"passphrase",
b"saltsaltsaltsalt",
Argon2Params::RECOMMENDED,
&mut out,
)
.unwrap();
assert!(out.iter().any(|&b| b != 0));
}
#[test]
#[ignore = "slow: uses OWASP minimum Argon2 parameters (~19 MiB, t=2, ~0.5 s)"]
fn owasp_min_params_produce_output() {
let mut out = [0u8; 32];
argon2id(
b"passphrase",
b"saltsaltsaltsalt",
Argon2Params::OWASP_MIN,
&mut out,
)
.unwrap();
assert!(out.iter().any(|&b| b != 0));
}