#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use super::super::*;
use tempfile::TempDir;
use tokio::time::{timeout, Duration};
use fn_dsa::KeyPairGenerator as _;
use signature::Keypair as _;
#[tokio::test]
async fn test_agent_creation() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path.clone());
assert_eq!(agent.socket_path, socket_path);
assert_eq!(agent.max_keys, 100);
assert!(agent.keys.read().await.is_empty());
assert!(agent.lock_passphrase.read().await.is_none());
}
#[tokio::test]
async fn test_add_and_list_keys() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key, public_key) = generate_test_falcon_keys();
let result = agent.add_key(
PqAlgorithm::Falcon512,
private_key.clone(),
public_key.clone(),
"test@example.com".to_string(),
None
).await;
assert!(result.is_ok());
let keys = agent.list_keys().await.unwrap();
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].comment, "test@example.com");
assert_eq!(keys[0].algorithm, PqAlgorithm::Falcon512);
}
#[tokio::test]
async fn test_key_expiration() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key, public_key) = generate_test_falcon_keys();
agent.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key,
"expiring@example.com".to_string(),
Some(1)
).await.unwrap();
assert_eq!(agent.list_keys().await.unwrap().len(), 1);
tokio::time::sleep(Duration::from_secs(2)).await;
agent.cleanup_expired_keys().await;
assert_eq!(agent.list_keys().await.unwrap().len(), 0);
}
#[tokio::test]
async fn test_remove_key() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key1, public_key1) = generate_test_falcon_keys();
let (private_key2, public_key2) = generate_test_falcon_keys();
agent.add_key(
PqAlgorithm::Falcon512,
private_key1,
public_key1.clone(),
"key1@example.com".to_string(),
None
).await.unwrap();
agent.add_key(
PqAlgorithm::Falcon512,
private_key2,
public_key2.clone(),
"key2@example.com".to_string(),
None
).await.unwrap();
assert_eq!(agent.list_keys().await.unwrap().len(), 2);
agent.remove_key(public_key1).await.unwrap();
let keys = agent.list_keys().await.unwrap();
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].comment, "key2@example.com");
}
#[tokio::test]
async fn test_remove_all_keys() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
for i in 0..5 {
let (private_key, public_key) = generate_test_falcon_keys();
agent.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key,
format!("key{}@example.com", i),
None
).await.unwrap();
}
assert_eq!(agent.list_keys().await.unwrap().len(), 5);
agent.remove_all_keys().await.unwrap();
assert_eq!(agent.list_keys().await.unwrap().len(), 0);
}
#[tokio::test]
async fn test_lock_unlock() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key, public_key) = generate_test_falcon_keys();
agent.add_key(
PqAlgorithm::Falcon512,
private_key.clone(),
public_key.clone(),
"test@example.com".to_string(),
None
).await.unwrap();
#[cfg(test)]
const TEST_PASSPHRASE: &str = "test_passphrase_only_for_unit_tests";
agent.lock(TEST_PASSPHRASE.to_string()).await.unwrap();
assert!(agent.list_keys().await.is_err());
assert!(agent.add_key(
PqAlgorithm::Falcon512,
private_key.clone(),
public_key.clone(),
"new@example.com".to_string(),
None
).await.is_err());
assert!(agent.unlock("wrong".to_string()).await.is_err());
agent.unlock(TEST_PASSPHRASE.to_string()).await.unwrap();
assert!(agent.list_keys().await.is_ok());
}
#[tokio::test]
async fn test_sign_with_key() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key, public_key) = generate_test_falcon_keys();
agent.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key.clone(),
"signer@example.com".to_string(),
None
).await.unwrap();
let data = b"Hello, quantum world!";
let signature = agent.sign_with_key(public_key.clone(), data.to_vec()).await.unwrap();
assert!(!signature.is_empty());
let (_, fake_public) = generate_test_falcon_keys();
assert!(agent.sign_with_key(fake_public, data.to_vec()).await.is_err());
}
#[tokio::test]
async fn test_max_keys_limit() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let mut agent = QsshAgent::new(socket_path);
agent.max_keys = 3;
for i in 0..3 {
let (private_key, public_key) = generate_test_falcon_keys();
assert!(agent.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key,
format!("key{}@example.com", i),
None
).await.is_ok());
}
let (private_key, public_key) = generate_test_falcon_keys();
assert!(agent.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key,
"overflow@example.com".to_string(),
None
).await.is_err());
}
#[tokio::test]
async fn test_protocol_messages() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let test_cases = vec![
(AgentMessage::ListKeys, true),
(AgentMessage::RemoveAllKeys, true),
(AgentMessage::Lock { passphrase: "test".to_string() }, true),
(AgentMessage::Unlock { passphrase: "test".to_string() }, true),
];
for (message, should_succeed) in test_cases {
let response = agent.handle_message(message).await;
assert_eq!(response.is_ok(), should_succeed);
}
}
#[tokio::test]
async fn test_concurrent_operations() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = Arc::new(QsshAgent::new(socket_path));
let mut handles = vec![];
for i in 0..10 {
let agent_clone = agent.clone();
let handle = tokio::spawn(async move {
let (private_key, public_key) = generate_test_falcon_keys();
agent_clone.add_key(
PqAlgorithm::Falcon512,
private_key,
public_key,
format!("concurrent{}@example.com", i),
None
).await
});
handles.push(handle);
}
for handle in handles {
assert!(handle.await.unwrap().is_ok());
}
assert_eq!(agent.list_keys().await.unwrap().len(), 10);
}
#[tokio::test]
async fn test_socket_communication() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = Arc::new(QsshAgent::new(socket_path.clone()));
let agent_clone = agent.clone();
let server_handle = tokio::spawn(async move {
agent_clone.start().await
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = AgentClient::with_socket(socket_path);
let result = timeout(Duration::from_secs(1), client.list_keys()).await;
assert!(result.is_ok());
drop(client);
server_handle.abort();
}
#[tokio::test]
async fn test_fingerprint_generation() {
let (_, public_key) = generate_test_falcon_keys();
let fingerprint1 = generate_fingerprint(&public_key);
let fingerprint2 = generate_fingerprint(&public_key);
assert_eq!(fingerprint1, fingerprint2);
let (_, public_key2) = generate_test_falcon_keys();
let fingerprint3 = generate_fingerprint(&public_key2);
assert_ne!(fingerprint1, fingerprint3);
}
#[tokio::test]
async fn test_sphincs_keys() {
let temp_dir = TempDir::new().unwrap();
let socket_path = temp_dir.path().join("test.sock");
let agent = QsshAgent::new(socket_path);
let (private_key, public_key) = generate_test_sphincs_keys();
let result = agent.add_key(
PqAlgorithm::SphincsPlus,
private_key,
public_key.clone(),
"sphincs@example.com".to_string(),
None
).await;
assert!(result.is_ok());
let keys = agent.list_keys().await.unwrap();
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].algorithm, PqAlgorithm::SphincsPlus);
}
fn generate_test_falcon_keys() -> (Vec<u8>, Vec<u8>) {
let mut sk = vec![0u8; fn_dsa::sign_key_size(fn_dsa::FN_DSA_LOGN_512)];
let mut pk = vec![0u8; fn_dsa::vrfy_key_size(fn_dsa::FN_DSA_LOGN_512)];
fn_dsa::KeyPairGeneratorStandard::default()
.keygen(fn_dsa::FN_DSA_LOGN_512, &mut aes_gcm::aead::OsRng, &mut sk, &mut pk);
(sk, pk)
}
fn generate_test_sphincs_keys() -> (Vec<u8>, Vec<u8>) {
let sk = slh_dsa::SigningKey::<slh_dsa::Sha2_128s>::new(&mut aes_gcm::aead::OsRng);
let pk = sk.verifying_key().clone();
(sk.to_bytes().to_vec(), pk.to_bytes().to_vec())
}
fn generate_fingerprint(public_key: &[u8]) -> String {
use sha2::{Sha256, Digest};
use base64::Engine;
let mut hasher = Sha256::new();
hasher.update(public_key);
let hash = hasher.finalize();
format!("SHA256:{}", base64::engine::general_purpose::STANDARD.encode(hash))
}
}