#![cfg(feature = "keystore")]
use chrono::Datelike;
use std::path::PathBuf;
use tempfile::tempdir;
use wecanencrypt::{
add_uid, create_key, create_key_simple, decrypt_bytes_from_store, encrypt_bytes_from_store,
encrypt_bytes_to_multiple_from_store, get_pub_key, parse_key_bytes, revoke_uid,
sign_bytes_detached, sign_bytes_detached_from_store, update_password,
verify_bytes_detached_from_store, CipherSuite, KeyStore, SubkeyFlags,
};
const TEST_PASSWORD: &str = "test-password-123";
fn test_files_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("files")
}
fn store_dir() -> PathBuf {
test_files_dir().join("store")
}
fn read_file(path: &PathBuf) -> Vec<u8> {
std::fs::read(path).unwrap_or_else(|_| panic!("Failed to read file: {:?}", path))
}
fn create_test_key(uid: &str) -> (Vec<u8>, String) {
let key = create_key_simple(TEST_PASSWORD, &[uid]).unwrap();
(key.secret_key.to_vec(), key.fingerprint)
}
#[test]
fn test_keystore_create() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
assert!(db_path.exists());
drop(store);
}
#[test]
fn test_keystore_import_and_export_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Test <test@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
let retrieved = store.export_key(&fingerprint).unwrap();
assert!(!retrieved.is_empty());
let info = parse_key_bytes(&retrieved, true).unwrap();
assert_eq!(info.fingerprint, fingerprint);
}
#[test]
fn test_keystore_import_secret_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Secret <secret@example.com>");
store.import_key(&secret_key).unwrap();
let retrieved = store.export_key(&fingerprint).unwrap();
let info = parse_key_bytes(&retrieved, true).unwrap();
assert!(info.is_secret);
}
#[test]
fn test_keystore_contains() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Test <test@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
assert!(!store.contains(&fingerprint).unwrap());
store.import_key(public_key.as_bytes()).unwrap();
assert!(store.contains(&fingerprint).unwrap());
assert!(!store
.contains("DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF")
.unwrap());
}
#[test]
fn test_keystore_delete_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Delete <delete@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
assert!(store.contains(&fingerprint).unwrap());
store.delete_key(&fingerprint).unwrap();
assert!(!store.contains(&fingerprint).unwrap());
}
#[test]
fn test_keystore_list_keys() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, fp1) = create_test_key("User1 <user1@example.com>");
let (key2, fp2) = create_test_key("User2 <user2@example.com>");
let (key3, fp3) = create_test_key("User3 <user3@example.com>");
store
.import_key(get_pub_key(&key1).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key2).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key3).unwrap().as_bytes())
.unwrap();
let certs = store.list_keys().unwrap();
assert_eq!(certs.len(), 3);
let fingerprints: Vec<_> = certs.iter().map(|c| c.fingerprint.as_str()).collect();
assert!(fingerprints.contains(&fp1.as_str()));
assert!(fingerprints.contains(&fp2.as_str()));
assert!(fingerprints.contains(&fp3.as_str()));
}
#[test]
fn test_keystore_search_by_uid() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, _) = create_test_key("Alice <alice@example.com>");
let (key2, _) = create_test_key("Bob <bob@example.com>");
let (key3, _) = create_test_key("Alice Work <alice@work.com>");
store
.import_key(get_pub_key(&key1).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key2).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key3).unwrap().as_bytes())
.unwrap();
let results = store.search_by_uid("alice").unwrap();
assert_eq!(results.len(), 2);
let results = store.search_by_uid("bob").unwrap();
assert_eq!(results.len(), 1);
let results = store.search_by_uid("charlie").unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_keystore_find_by_key_id() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("KeyID <keyid@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
let info = parse_key_bytes(public_key.as_bytes(), true).unwrap();
let key_id = &info.key_id;
let retrieved = store.find_by_key_id(key_id).unwrap();
assert!(retrieved.is_some());
let key_data = retrieved.unwrap();
let retrieved_info = parse_key_bytes(&key_data, true).unwrap();
assert_eq!(retrieved_info.fingerprint, fingerprint);
}
#[test]
fn test_keystore_update_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let key = create_key_simple(TEST_PASSWORD, &["Original <original@example.com>"]).unwrap();
store.import_key(key.secret_key.as_slice()).unwrap();
let updated_key =
wecanencrypt::add_uid(&key.secret_key, "Added <added@example.com>", TEST_PASSWORD).unwrap();
store.update_key(&key.fingerprint, &updated_key).unwrap();
let retrieved = store.export_key(&key.fingerprint).unwrap();
let info = parse_key_bytes(&retrieved, true).unwrap();
assert_eq!(info.user_ids.len(), 2);
}
#[test]
fn test_keystore_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let (secret_key, fingerprint) = create_test_key("Persist <persist@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
{
let store = KeyStore::open(&db_path).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
}
{
let store = KeyStore::open(&db_path).unwrap();
assert!(store.contains(&fingerprint).unwrap());
}
}
#[test]
fn test_keystore_import_duplicate() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, _) = create_test_key("Duplicate <dup@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
let result = store.import_key(public_key.as_bytes());
assert!(result.is_ok());
let certs = store.list_keys().unwrap();
assert_eq!(certs.len(), 1);
}
#[test]
fn test_keystore_count() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
assert_eq!(store.count().unwrap(), 0);
let (key1, _) = create_test_key("User1 <user1@example.com>");
let (key2, _) = create_test_key("User2 <user2@example.com>");
store
.import_key(get_pub_key(&key1).unwrap().as_bytes())
.unwrap();
assert_eq!(store.count().unwrap(), 1);
store
.import_key(get_pub_key(&key2).unwrap().as_bytes())
.unwrap();
assert_eq!(store.count().unwrap(), 2);
}
#[test]
fn test_keystore_list_secret_keys() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, _) = create_test_key("Secret <secret@example.com>");
let (public_key_src, _) = create_test_key("Public <public@example.com>");
let public_key = get_pub_key(&public_key_src).unwrap();
store.import_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
let secret_keys = store.list_secret_keys().unwrap();
assert_eq!(secret_keys.len(), 1);
assert!(secret_keys[0].is_secret);
let public_keys = store.list_public_keys().unwrap();
assert_eq!(public_keys.len(), 1);
assert!(!public_keys[0].is_secret);
}
#[test]
fn test_keystore_get_nonexistent_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let result = store.export_key("A4F388BBB194925AE301F844C52B42177857DD79");
assert!(result.is_err());
}
#[test]
fn test_keystore_delete_nonexistent_key() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let result = store.delete_key("A4F388BBB194925AE301F844C52B42177857DD79");
assert!(result.is_err());
}
#[test]
fn test_keystore_in_memory() {
let store = KeyStore::open_in_memory().unwrap();
assert!(store.path().is_none());
assert_eq!(store.count().unwrap(), 0);
let (secret_key, fingerprint) = create_test_key("InMemory <inmemory@example.com>");
store.import_key(&secret_key).unwrap();
assert_eq!(store.count().unwrap(), 1);
assert!(store.contains(&fingerprint).unwrap());
}
#[test]
fn test_keystore_search_by_email() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, _) = create_test_key("Alice <alice@example.com>");
let (key2, _) = create_test_key("Bob <bob@work.com>");
let (key3, _) = create_test_key("Alice Work <alice@work.com>");
store
.import_key(get_pub_key(&key1).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key2).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key3).unwrap().as_bytes())
.unwrap();
let results = store.search_by_email("alice@example.com").unwrap();
assert_eq!(results.len(), 1);
let results = store.search_by_email("alice@work.com").unwrap();
assert_eq!(results.len(), 1);
let results = store.search_by_email("charlie@example.com").unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_keystore_export_armored() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Armored <armored@example.com>");
store.import_key(&secret_key).unwrap();
let armored = store.export_key_armored(&fingerprint).unwrap();
assert!(armored.starts_with("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
assert!(armored.contains("-----END PGP PUBLIC KEY BLOCK-----"));
}
#[test]
fn test_keystore_get_key_info() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Info <info@example.com>");
store.import_key(&secret_key).unwrap();
let info = store.get_key_info(&fingerprint).unwrap();
assert_eq!(info.fingerprint, fingerprint);
assert!(info.is_secret);
assert_eq!(info.user_ids.len(), 1);
assert!(info.user_ids[0].value.contains("info@example.com"));
}
#[test]
fn test_keystore_list_fingerprints() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, fp1) = create_test_key("User1 <user1@example.com>");
let (key2, fp2) = create_test_key("User2 <user2@example.com>");
store
.import_key(get_pub_key(&key1).unwrap().as_bytes())
.unwrap();
store
.import_key(get_pub_key(&key2).unwrap().as_bytes())
.unwrap();
let fingerprints = store.list_fingerprints().unwrap();
assert_eq!(fingerprints.len(), 2);
assert!(fingerprints.contains(&fp1));
assert!(fingerprints.contains(&fp2));
}
#[test]
fn test_keystore_import_from_file() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let key_path = store_dir().join("public.asc");
let fingerprint = store.import_key_file(&key_path).unwrap();
assert!(!fingerprint.is_empty());
assert!(store.contains(&fingerprint).unwrap());
}
#[test]
fn test_keystore_password_change() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Password <password@example.com>");
store.import_key(&secret_key).unwrap();
let key_data = store.export_key(&fingerprint).unwrap();
let new_password = "new-password-456";
let updated_key = update_password(&key_data, TEST_PASSWORD, new_password).unwrap();
store.update_key(&fingerprint, &updated_key).unwrap();
let updated_key = store.export_key(&fingerprint).unwrap();
let signature = sign_bytes_detached(&updated_key, b"test data", new_password).unwrap();
assert!(signature.contains("-----BEGIN PGP SIGNATURE-----"));
let result = sign_bytes_detached(&updated_key, b"test data", TEST_PASSWORD);
assert!(result.is_err());
}
#[test]
fn test_keystore_encrypt_decrypt_multiple_recipients() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, fp1) = create_test_key("Recipient1 <r1@example.com>");
let (key2, fp2) = create_test_key("Recipient2 <r2@example.com>");
store.import_key(&key1).unwrap();
store.import_key(&key2).unwrap();
let message = b"Message for multiple recipients";
let ciphertext =
encrypt_bytes_to_multiple_from_store(&store, &[&fp1, &fp2], message, true).unwrap();
let decrypted1 = decrypt_bytes_from_store(&store, &fp1, &ciphertext, TEST_PASSWORD).unwrap();
let decrypted2 = decrypt_bytes_from_store(&store, &fp2, &ciphertext, TEST_PASSWORD).unwrap();
assert_eq!(decrypted1, message);
assert_eq!(decrypted2, message);
}
#[test]
fn test_keystore_sign_verify_wrong_data_fails() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Signer <signer@example.com>");
store.import_key(&secret_key).unwrap();
let signature =
sign_bytes_detached_from_store(&store, &fingerprint, b"original data", TEST_PASSWORD)
.unwrap();
let valid = verify_bytes_detached_from_store(
&store,
&fingerprint,
b"original data",
signature.as_bytes(),
)
.unwrap();
assert!(valid);
let valid = verify_bytes_detached_from_store(
&store,
&fingerprint,
b"modified data",
signature.as_bytes(),
)
.unwrap();
assert!(!valid);
}
#[test]
fn test_keystore_add_and_revoke_uid() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Original <original@example.com>");
store.import_key(&secret_key).unwrap();
let key_data = store.export_key(&fingerprint).unwrap();
let with_new_uid = add_uid(&key_data, "Added <added@example.com>", TEST_PASSWORD).unwrap();
store.update_key(&fingerprint, &with_new_uid).unwrap();
let info = store.get_key_info(&fingerprint).unwrap();
assert_eq!(info.user_ids.len(), 2);
let updated_key = store.export_key(&fingerprint).unwrap();
let with_revoked =
revoke_uid(&updated_key, "Added <added@example.com>", TEST_PASSWORD).unwrap();
store.update_key(&fingerprint, &with_revoked).unwrap();
let info = store.get_key_info(&fingerprint).unwrap();
assert!(!info.user_ids.is_empty());
}
#[test]
fn test_keystore_add_uid_to_public_key_fails() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Public <public@example.com>");
let public_key = get_pub_key(&secret_key).unwrap();
store.import_key(public_key.as_bytes()).unwrap();
let key_data = store.export_key(&fingerprint).unwrap();
let result = add_uid(&key_data, "New <new@example.com>", TEST_PASSWORD);
assert!(result.is_err());
}
#[test]
fn test_keystore_key_without_uid_fails() {
let result = create_key(
TEST_PASSWORD,
&[], CipherSuite::Cv25519,
None,
None,
None,
SubkeyFlags::all(),
false,
true,
);
assert!(result.is_err());
}
#[test]
fn test_keystore_key_with_multiple_uids() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let uids = &[
"Primary <primary@example.com>",
"secondary@example.com",
"Another Name",
];
let key = create_key_simple(TEST_PASSWORD, uids).unwrap();
store.import_key(&key.secret_key).unwrap();
let info = store.get_key_info(&key.fingerprint).unwrap();
assert_eq!(info.user_ids.len(), 3);
let results = store.search_by_uid("primary").unwrap();
assert_eq!(results.len(), 1);
let results = store.search_by_uid("secondary").unwrap();
assert_eq!(results.len(), 1);
let results = store.search_by_uid("Another").unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn test_keystore_creation_expiration_times() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let key_path = store_dir().join("pgp_keys.asc");
let fingerprint = store.import_key_file(&key_path).unwrap();
let info = store.get_key_info(&fingerprint).unwrap();
assert!(info.creation_time.year() == 2017);
assert!(info.expiration_time.is_some());
let exp = info.expiration_time.unwrap();
assert!(exp.year() == 2020);
}
#[test]
fn test_keystore_encrypt_decrypt_from_store() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("Store <store@example.com>");
store.import_key(&secret_key).unwrap();
let message = b"Secret message via store";
let ciphertext = encrypt_bytes_from_store(&store, &fingerprint, message, true).unwrap();
assert!(String::from_utf8_lossy(&ciphertext).starts_with("-----BEGIN PGP MESSAGE-----"));
let decrypted =
decrypt_bytes_from_store(&store, &fingerprint, &ciphertext, TEST_PASSWORD).unwrap();
assert_eq!(decrypted, message);
}
#[test]
fn test_keystore_with_fixture_files() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let files = [
store_dir().join("public.asc"),
store_dir().join("hellopublic.asc"),
store_dir().join("pgp_keys.asc"),
];
for path in &files {
store.import_key_file(path).unwrap();
}
assert_eq!(store.count().unwrap(), 3);
let certs = store.list_keys().unwrap();
assert_eq!(certs.len(), 3);
}
#[test]
fn test_keystore_update_key_merges_correctly() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let old_key_path = store_dir().join("pgp_keys.asc");
let fingerprint = store.import_key_file(&old_key_path).unwrap();
let old_info = store.get_key_info(&fingerprint).unwrap();
let new_key_path = store_dir().join("kushal_updated_key.asc");
let new_key_data = read_file(&new_key_path);
store.update_key(&fingerprint, &new_key_data).unwrap();
let new_info = store.get_key_info(&fingerprint).unwrap();
assert_eq!(new_info.fingerprint, old_info.fingerprint);
}
#[test]
fn test_keystore_encrypt_decrypt_file() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("FileTest <filetest@example.com>");
store.import_key(&secret_key).unwrap();
let input_path = dir.path().join("input.txt");
let encrypted_path = dir.path().join("encrypted.gpg");
let decrypted_path = dir.path().join("decrypted.txt");
std::fs::write(&input_path, b"Test data for file encryption").unwrap();
wecanencrypt::encrypt_file_from_store(&store, &fingerprint, &input_path, &encrypted_path, true)
.unwrap();
assert!(encrypted_path.exists());
wecanencrypt::decrypt_file_from_store(
&store,
&fingerprint,
&encrypted_path,
&decrypted_path,
TEST_PASSWORD,
)
.unwrap();
let decrypted = std::fs::read(&decrypted_path).unwrap();
assert_eq!(decrypted, b"Test data for file encryption");
}
#[test]
fn test_keystore_sign_verify_file_detached() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fingerprint) = create_test_key("SignFile <signfile@example.com>");
store.import_key(&secret_key).unwrap();
let data_path = dir.path().join("data.txt");
let sig_path = dir.path().join("data.txt.sig");
std::fs::write(&data_path, b"Data to be signed").unwrap();
let signature = wecanencrypt::sign_file_detached_from_store(
&store,
&fingerprint,
&data_path,
TEST_PASSWORD,
)
.unwrap();
assert!(signature.starts_with("-----BEGIN PGP SIGNATURE-----"));
std::fs::write(&sig_path, signature.as_bytes()).unwrap();
let valid =
wecanencrypt::verify_file_detached_from_store(&store, &fingerprint, &data_path, &sig_path)
.unwrap();
assert!(valid);
}
#[test]
fn test_keystore_encrypt_file_multiple_recipients() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let (key1, fp1) = create_test_key("Recipient1 <r1@example.com>");
let (key2, fp2) = create_test_key("Recipient2 <r2@example.com>");
store.import_key(&key1).unwrap();
store.import_key(&key2).unwrap();
let input_path = dir.path().join("input.txt");
let encrypted_path = dir.path().join("encrypted.gpg");
let decrypted1_path = dir.path().join("decrypted1.txt");
let decrypted2_path = dir.path().join("decrypted2.txt");
std::fs::write(&input_path, b"Multi-recipient test").unwrap();
wecanencrypt::encrypt_file_to_multiple_from_store(
&store,
&[&fp1, &fp2],
&input_path,
&encrypted_path,
true,
)
.unwrap();
wecanencrypt::decrypt_file_from_store(
&store,
&fp1,
&encrypted_path,
&decrypted1_path,
TEST_PASSWORD,
)
.unwrap();
wecanencrypt::decrypt_file_from_store(
&store,
&fp2,
&encrypted_path,
&decrypted2_path,
TEST_PASSWORD,
)
.unwrap();
let decrypted1 = std::fs::read(&decrypted1_path).unwrap();
let decrypted2 = std::fs::read(&decrypted2_path).unwrap();
assert_eq!(decrypted1, b"Multi-recipient test");
assert_eq!(decrypted2, b"Multi-recipient test");
}
#[test]
fn test_keystore_schema_upgrade() {
let dir = tempdir().unwrap();
let old_db_path = store_dir().join("oldjce.db");
let new_db_path = dir.path().join("jce.db");
std::fs::copy(&old_db_path, &new_db_path).unwrap();
let store = KeyStore::open(&new_db_path).unwrap();
let certs = store.list_keys().unwrap();
let count = store.count().unwrap();
assert_eq!(certs.len(), count);
}
#[test]
fn test_keystore_keyids() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let key_path = store_dir().join("kushal_updated_key.asc");
let fp = store.import_key_file(&key_path).unwrap();
let info = store.get_key_info(&fp).unwrap();
assert_eq!(info.key_id, "D8219C8C43F6C5E1");
}
#[test]
fn test_keystore_deletion_cleans_subkeys() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let store = KeyStore::open(&db_path).unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
assert!(store.contains(&fp).unwrap());
let info = store.get_key_info(&fp).unwrap();
assert!(!info.subkeys.is_empty(), "Key should have subkeys");
store.delete_key(&fp).unwrap();
assert!(!store.contains(&fp).unwrap());
assert_eq!(store.count().unwrap(), 0);
}
#[test]
fn test_keystore_save_and_get_card_keys() {
let store = KeyStore::open_in_memory().unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"signature",
"aabbccdd",
)
.unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"encryption",
"eeff0011",
)
.unwrap();
store
.save_card_key(
&fp,
"0006:99999999",
"99999999",
Some("Yubico AB"),
"signature",
"22334455",
)
.unwrap();
let card_keys = store.get_card_keys(&fp).unwrap();
assert_eq!(card_keys.len(), 3);
let first = &card_keys[0];
assert_eq!(first.card_ident, "0006:12345678");
assert_eq!(first.card_serial, "12345678");
assert_eq!(first.card_manufacturer, Some("Yubico AB".to_string()));
assert_eq!(first.slot, "encryption");
assert_eq!(first.slot_fingerprint, "eeff0011");
}
#[test]
fn test_keystore_card_keys_replace_on_duplicate() {
let store = KeyStore::open_in_memory().unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"signature",
"aabbccdd",
)
.unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"signature",
"newfingerprint",
)
.unwrap();
let card_keys = store.get_card_keys(&fp).unwrap();
assert_eq!(card_keys.len(), 1);
assert_eq!(card_keys[0].slot_fingerprint, "newfingerprint");
}
#[test]
fn test_keystore_remove_card_keys_for_card() {
let store = KeyStore::open_in_memory().unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"signature",
"aabbccdd",
)
.unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"encryption",
"eeff0011",
)
.unwrap();
store
.save_card_key(
&fp,
"0006:99999999",
"99999999",
Some("Yubico AB"),
"signature",
"22334455",
)
.unwrap();
store.remove_card_keys_for_card("0006:12345678").unwrap();
let card_keys = store.get_card_keys(&fp).unwrap();
assert_eq!(card_keys.len(), 1);
assert_eq!(card_keys[0].card_ident, "0006:99999999");
}
#[test]
fn test_keystore_card_keys_cascade_delete() {
let store = KeyStore::open_in_memory().unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
store
.save_card_key(
&fp,
"0006:12345678",
"12345678",
Some("Yubico AB"),
"signature",
"aabbccdd",
)
.unwrap();
store.delete_key(&fp).unwrap();
let fp2 = store.import_key(&key_data).unwrap();
let card_keys = store.get_card_keys(&fp2).unwrap();
assert_eq!(card_keys.len(), 0);
}
#[test]
fn test_keystore_card_keys_empty_result() {
let store = KeyStore::open_in_memory().unwrap();
let key_path = store_dir().join("public.asc");
let key_data = read_file(&key_path);
let fp = store.import_key(&key_data).unwrap();
let card_keys = store.get_card_keys(&fp).unwrap();
assert!(card_keys.is_empty());
}
#[test]
fn test_keystore_bad_path() {
let result = KeyStore::open("/nonexistent/path/to/database.db");
assert!(result.is_err());
}
#[test]
fn test_keystore_encrypt_bytes_to_file() {
use wecanencrypt::decrypt_bytes_from_store;
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let encrypted_path = dir.path().join("encrypted.pgp");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key, fp) = create_test_key("FileEnc <fileenc@example.com>");
store.import_key(&secret_key).unwrap();
let plaintext = b"Bytes to file encryption test";
let ciphertext = wecanencrypt::encrypt_bytes_from_store(&store, &fp, plaintext, true).unwrap();
std::fs::write(&encrypted_path, &ciphertext).unwrap();
let encrypted_data = std::fs::read(&encrypted_path).unwrap();
let decrypted = decrypt_bytes_from_store(&store, &fp, &encrypted_data, TEST_PASSWORD).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_keystore_encrypt_bytes_to_file_multiple_recipients() {
use wecanencrypt::decrypt_bytes_from_store;
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let encrypted_path = dir.path().join("encrypted.pgp");
let store = KeyStore::open(&db_path).unwrap();
let (secret_key1, fp1) = create_test_key("Multi1 <multi1@example.com>");
let (secret_key2, fp2) = create_test_key("Multi2 <multi2@example.com>");
store.import_key(&secret_key1).unwrap();
store.import_key(&secret_key2).unwrap();
let plaintext = b"Multi-recipient bytes to file";
let ciphertext = wecanencrypt::encrypt_bytes_to_multiple_from_store(
&store,
&[fp1.as_str(), fp2.as_str()],
plaintext,
true,
)
.unwrap();
std::fs::write(&encrypted_path, &ciphertext).unwrap();
let encrypted_data = std::fs::read(&encrypted_path).unwrap();
let decrypted1 =
decrypt_bytes_from_store(&store, &fp1, &encrypted_data, TEST_PASSWORD).unwrap();
let decrypted2 =
decrypt_bytes_from_store(&store, &fp2, &encrypted_data, TEST_PASSWORD).unwrap();
assert_eq!(decrypted1, plaintext);
assert_eq!(decrypted2, plaintext);
}
#[test]
fn test_keystore_certify_uid() {
use wecanencrypt::{certify_key, CertificationType};
let store = KeyStore::open_in_memory().unwrap();
let (signer_key, signer_fp) = create_test_key("Signer <signer@example.com>");
let (target_key, target_fp) = create_test_key("Target <target@example.com>");
store.import_key(&signer_key).unwrap();
store.import_key(&target_key).unwrap();
let target_pub = store.export_key(&target_fp).unwrap();
let signer_sec = store.export_key(&signer_fp).unwrap();
let certified = certify_key(
&signer_sec,
&target_pub,
CertificationType::Persona,
Some(&["Target <target@example.com>"]),
TEST_PASSWORD,
)
.unwrap();
store.update_key(&target_fp, &certified).unwrap();
let updated_key = store.export_key(&target_fp).unwrap();
let info = parse_key_bytes(&updated_key, true).unwrap();
let target_uid = info
.user_ids
.iter()
.find(|u| u.value == "Target <target@example.com>")
.expect("Target UID should exist");
assert!(
!target_uid.certifications.is_empty(),
"Target UID should have certifications after being certified"
);
}