diff --git a/Cargo.toml b/Cargo.toml index 8e5a396..9f28399 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ env_logger = "0.10.0" # Logger implementation ethers = { version = "2.0.7", features = ["legacy"] } # Ethereum library glob = "0.3.1" # For file pattern matching jsonrpsee = "0.25.1" -k256 = { version = "0.13.1", features = ["ecdsa"] } # Elliptic curve cryptography +k256 = { version = "0.13.1", features = ["ecdsa", "ecdh"] } # Elliptic curve cryptography lazy_static = "1.4.0" # For lazy initialization of static variables libc = "0.2" log = "0.4" # Logging facade diff --git a/src/vault/keypair/keypair_types.rs b/src/vault/keypair/keypair_types.rs index cdc5374..f7da6fa 100644 --- a/src/vault/keypair/keypair_types.rs +++ b/src/vault/keypair/keypair_types.rs @@ -214,18 +214,16 @@ impl KeyPair { let ephemeral_signing_key = SigningKey::random(&mut OsRng); let ephemeral_public_key = VerifyingKey::from(&ephemeral_signing_key); - // Derive shared secret (this is a simplified ECDH) - // In a real implementation, we would use proper ECDH, but for this example: - let shared_point = recipient_key.to_encoded_point(false); - let shared_secret = { - let mut hasher = Sha256::default(); - hasher.update(ephemeral_signing_key.to_bytes()); - hasher.update(shared_point.as_bytes()); - hasher.finalize().to_vec() - }; + // Derive shared secret using ECDH + let shared_secret_bytes = ephemeral_signing_key.diffie_hellman(&recipient_key); + // Derive encryption key from the shared secret (using a simple hash for this example) + let mut hasher = Sha256::default(); + hasher.update(shared_secret_bytes.as_bytes()); + let encryption_key = hasher.finalize().to_vec(); + // Encrypt the message using the derived key - let ciphertext = implementation::encrypt_with_key(&shared_secret, message) + let ciphertext = implementation::encrypt_with_key(&encryption_key, message) .map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?; // Format: ephemeral_public_key || ciphertext @@ -252,17 +250,16 @@ impl KeyPair { let sender_key = VerifyingKey::from_sec1_bytes(ephemeral_public_key) .map_err(|_| CryptoError::InvalidKeyLength)?; - // Derive shared secret (simplified ECDH) - let shared_point = sender_key.to_encoded_point(false); - let shared_secret = { - let mut hasher = Sha256::default(); - hasher.update(self.signing_key.to_bytes()); - hasher.update(shared_point.as_bytes()); - hasher.finalize().to_vec() - }; + // Derive shared secret using ECDH + let shared_secret_bytes = self.signing_key.diffie_hellman(&sender_key); + + // Derive encryption key from the shared secret (using the same simple hash) + let mut hasher = Sha256::default(); + hasher.update(shared_secret_bytes.as_bytes()); + let encryption_key = hasher.finalize().to_vec(); // Decrypt the message using the derived key - implementation::decrypt_with_key(&shared_secret, actual_ciphertext) + implementation::decrypt_with_key(&encryption_key, actual_ciphertext) .map_err(|e| CryptoError::DecryptionFailed(e.to_string())) } } diff --git a/src/vault/keypair/mod.rs b/src/vault/keypair/mod.rs index e29506b..d9ea317 100644 --- a/src/vault/keypair/mod.rs +++ b/src/vault/keypair/mod.rs @@ -13,3 +13,6 @@ pub use session_manager::{ keypair_pub_key, derive_public_key, keypair_sign, keypair_verify, verify_with_public_key, encrypt_asymmetric, decrypt_asymmetric }; + +#[cfg(test)] +mod tests; diff --git a/src/vault/keypair/tests/implementation_tests.rs b/src/vault/keypair/tests/implementation_tests.rs new file mode 100644 index 0000000..b62bb10 --- /dev/null +++ b/src/vault/keypair/tests/implementation_tests.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} \ No newline at end of file diff --git a/src/vault/keypair/tests/keypair_types_tests.rs b/src/vault/keypair/tests/keypair_types_tests.rs new file mode 100644 index 0000000..fe45775 --- /dev/null +++ b/src/vault/keypair/tests/keypair_types_tests.rs @@ -0,0 +1,86 @@ + +use crate::vault::keypair::keypair_types::{KeyPair, KeySpace}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_keypair_creation() { + let keypair = KeyPair::new("test_keypair"); + assert_eq!(keypair.name, "test_keypair"); + // Basic check that keys are generated (they should have non-zero length) + assert!(!keypair.pub_key().is_empty()); + } + + #[test] + fn test_keypair_sign_and_verify() { + let keypair = KeyPair::new("test_keypair"); + let message = b"This is a test message"; + let signature = keypair.sign(message); + assert!(!signature.is_empty()); + + let is_valid = keypair.verify(message, &signature).expect("Verification failed"); + assert!(is_valid); + + // Test with a wrong message + let wrong_message = b"This is a different message"; + let is_valid_wrong = keypair.verify(wrong_message, &signature).expect("Verification failed with wrong message"); + assert!(!is_valid_wrong); + } + + #[test] + fn test_verify_with_public_key() { + let keypair = KeyPair::new("test_keypair"); + let message = b"Another test message"; + let signature = keypair.sign(message); + let public_key = keypair.pub_key(); + + let is_valid = KeyPair::verify_with_public_key(&public_key, message, &signature).expect("Verification with public key failed"); + assert!(is_valid); + + // Test with a wrong public key + let wrong_keypair = KeyPair::new("wrong_keypair"); + let wrong_public_key = wrong_keypair.pub_key(); + let is_valid_wrong_key = KeyPair::verify_with_public_key(&wrong_public_key, message, &signature).expect("Verification with wrong public key failed"); + assert!(!is_valid_wrong_key); + } + + #[test] + fn test_asymmetric_encryption_decryption() { + // Sender's keypair + let sender_keypair = KeyPair::new("sender"); + let sender_public_key = sender_keypair.pub_key(); + + // Recipient's keypair + let recipient_keypair = KeyPair::new("recipient"); + let recipient_public_key = recipient_keypair.pub_key(); + + let message = b"This is a secret message"; + + // Sender encrypts for recipient + let ciphertext = sender_keypair.encrypt_asymmetric(&recipient_public_key, message).expect("Encryption failed"); + assert!(!ciphertext.is_empty()); + + // Recipient decrypts + let decrypted_message = recipient_keypair.decrypt_asymmetric(&ciphertext).expect("Decryption failed"); + assert_eq!(decrypted_message, message); + + // Test decryption with wrong keypair + let wrong_keypair = KeyPair::new("wrong_recipient"); + let result = wrong_keypair.decrypt_asymmetric(&ciphertext); + assert!(result.is_err()); + } + + #[test] + fn test_keyspace_add_keypair() { + let mut space = KeySpace::new("test_space"); + space.add_keypair("keypair1").expect("Failed to add keypair1"); + assert_eq!(space.keypairs.len(), 1); + assert!(space.keypairs.contains_key("keypair1")); + + // Test adding a duplicate keypair + let result = space.add_keypair("keypair1"); + assert!(result.is_err()); + } +} \ No newline at end of file diff --git a/src/vault/keypair/tests/mod.rs b/src/vault/keypair/tests/mod.rs new file mode 100644 index 0000000..d24426c --- /dev/null +++ b/src/vault/keypair/tests/mod.rs @@ -0,0 +1,3 @@ +mod implementation_tests; +mod keypair_types_tests; +mod session_manager_tests; \ No newline at end of file diff --git a/src/vault/keypair/tests/session_manager_tests.rs b/src/vault/keypair/tests/session_manager_tests.rs new file mode 100644 index 0000000..416c671 --- /dev/null +++ b/src/vault/keypair/tests/session_manager_tests.rs @@ -0,0 +1,111 @@ +use crate::vault::keypair::session_manager::{ + clear_session, create_keypair, create_space, get_current_space, get_selected_keypair, + list_keypairs, select_keypair, set_current_space, SESSION, +}; +use crate::vault::keypair::keypair_types::KeySpace; + +// Helper function to clear the session before each test +fn setup_test() { + clear_session(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_and_get_space() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + let space = get_current_space().expect("Failed to get current space"); + assert_eq!(space.name, "test_space"); + } + + #[test] + fn test_set_current_space() { + setup_test(); + let space = KeySpace::new("another_space"); + set_current_space(space.clone()).expect("Failed to set current space"); + let current_space = get_current_space().expect("Failed to get current space"); + assert_eq!(current_space.name, "another_space"); + } + + #[test] + fn test_clear_session() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + clear_session(); + let result = get_current_space(); + assert!(result.is_err()); + } + + #[test] + fn test_create_and_select_keypair() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + create_keypair("test_keypair").expect("Failed to create keypair"); + let keypair = get_selected_keypair().expect("Failed to get selected keypair"); + assert_eq!(keypair.name, "test_keypair"); + + select_keypair("test_keypair").expect("Failed to select keypair"); + let selected_keypair = get_selected_keypair().expect("Failed to get selected keypair after select"); + assert_eq!(selected_keypair.name, "test_keypair"); + } + + #[test] + fn test_list_keypairs() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + create_keypair("keypair1").expect("Failed to create keypair1"); + create_keypair("keypair2").expect("Failed to create keypair2"); + + let keypairs = list_keypairs().expect("Failed to list keypairs"); + assert_eq!(keypairs.len(), 2); + assert!(keypairs.contains(&"keypair1".to_string())); + assert!(keypairs.contains(&"keypair2".to_string())); + } + + #[test] + fn test_create_keypair_no_active_space() { + setup_test(); + let result = create_keypair("test_keypair"); + assert!(result.is_err()); + } + + #[test] + fn test_select_keypair_no_active_space() { + setup_test(); + let result = select_keypair("test_keypair"); + assert!(result.is_err()); + } + + #[test] + fn test_select_nonexistent_keypair() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + let result = select_keypair("nonexistent_keypair"); + assert!(result.is_err()); + } + + #[test] + fn test_get_selected_keypair_no_active_space() { + setup_test(); + let result = get_selected_keypair(); + assert!(result.is_err()); + } + + #[test] + fn test_get_selected_keypair_no_keypair_selected() { + setup_test(); + create_space("test_space").expect("Failed to create space"); + let result = get_selected_keypair(); + assert!(result.is_err()); + } + + #[test] + fn test_list_keypairs_no_active_space() { + setup_test(); + let result = list_keypairs(); + assert!(result.is_err()); + } +} diff --git a/src/vault/kvs/README.md b/src/vault/kvs/README.md index 89d6d2e..431a24c 100644 --- a/src/vault/kvs/README.md +++ b/src/vault/kvs/README.md @@ -165,3 +165,9 @@ let loaded_store = KvStore::load("my_store", "secure_password")?; let api_key = loaded_store.get("api_key")?; println!("API Key: {}", api_key.unwrap_or_default()); ``` + +## to test + +```bash +cargo test --lib vault::keypair +``` \ No newline at end of file diff --git a/src/vault/kvs/mod.rs b/src/vault/kvs/mod.rs index 4ab3770..90e85b9 100644 --- a/src/vault/kvs/mod.rs +++ b/src/vault/kvs/mod.rs @@ -12,3 +12,6 @@ pub use store::{ create_store, open_store, delete_store, list_stores, get_store_path }; + +#[cfg(test)] +mod tests; diff --git a/src/vault/kvs/store.rs b/src/vault/kvs/store.rs index 74c9c6f..f30ab85 100644 --- a/src/vault/kvs/store.rs +++ b/src/vault/kvs/store.rs @@ -355,7 +355,7 @@ impl KvStore { // Save to disk self.save()?; - Ok(()) + Ok(()) } /// Gets the name of the store. diff --git a/src/vault/kvs/tests/mod.rs b/src/vault/kvs/tests/mod.rs new file mode 100644 index 0000000..668dbed --- /dev/null +++ b/src/vault/kvs/tests/mod.rs @@ -0,0 +1 @@ +mod store_tests; \ No newline at end of file diff --git a/src/vault/kvs/tests/store_tests.rs b/src/vault/kvs/tests/store_tests.rs new file mode 100644 index 0000000..5a972bf --- /dev/null +++ b/src/vault/kvs/tests/store_tests.rs @@ -0,0 +1,105 @@ +use crate::vault::kvs::store::{create_store, delete_store, open_store, KvStore}; +use std::path::PathBuf; + +// Helper function to generate a unique store name for each test +fn generate_test_store_name() -> String { + use rand::Rng; + let random_string: String = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(10) + .map(char::from) + .collect(); + format!("test_store_{}", random_string) +} + +// Helper function to clean up test stores +fn cleanup_test_store(name: &str) { + let _ = delete_store(name); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_and_open_store() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + assert_eq!(store.name(), store_name); + assert!(!store.is_encrypted()); + + let opened_store = open_store(&store_name, None).expect("Failed to open store"); + assert_eq!(opened_store.name(), store_name); + assert!(!opened_store.is_encrypted()); + + cleanup_test_store(&store_name); + } + + #[test] + fn test_set_and_get_value() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + + store.set("key1", &"value1").expect("Failed to set value"); + let value: String = store.get("key1").expect("Failed to get value"); + assert_eq!(value, "value1"); + + cleanup_test_store(&store_name); + } + + #[test] + fn test_delete_value() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + + store.set("key1", &"value1").expect("Failed to set value"); + store.delete("key1").expect("Failed to delete value"); + let result: Result = store.get("key1"); + assert!(result.is_err()); + + cleanup_test_store(&store_name); + } + + #[test] + fn test_contains_key() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + + store.set("key1", &"value1").expect("Failed to set value"); + assert!(store.contains("key1").expect("Failed to check contains")); + assert!(!store.contains("key2").expect("Failed to check contains")); + + cleanup_test_store(&store_name); + } + + #[test] + fn test_list_keys() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + + store.set("key1", &"value1").expect("Failed to set value"); + store.set("key2", &"value2").expect("Failed to set value"); + + let keys = store.keys().expect("Failed to list keys"); + assert_eq!(keys.len(), 2); + assert!(keys.contains(&"key1".to_string())); + assert!(keys.contains(&"key2".to_string())); + + cleanup_test_store(&store_name); + } + + #[test] + fn test_clear_store() { + let store_name = generate_test_store_name(); + let store = create_store(&store_name, false, None).expect("Failed to create store"); + + store.set("key1", &"value1").expect("Failed to set value"); + store.set("key2", &"value2").expect("Failed to set value"); + + store.clear().expect("Failed to clear store"); + let keys = store.keys().expect("Failed to list keys after clear"); + assert!(keys.is_empty()); + + cleanup_test_store(&store_name); + } +} \ No newline at end of file