Compare commits
9 Commits
330ab23930
...
3e64a53a83
Author | SHA1 | Date | |
---|---|---|---|
|
3e64a53a83 | ||
|
3225b3f029 | ||
|
3417e2c1ff | ||
a4438d63e0 | |||
393c4270d4 | |||
495fe92321 | |||
577d80b282 | |||
3f8aecb786 | |||
|
c7a5699798 |
20
Cargo.toml
20
Cargo.toml
@ -12,16 +12,16 @@ readme = "README.md"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
base64 = "0.21.0" # Base64 encoding/decoding
|
base64 = "0.22.1" # Base64 encoding/decoding
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
chacha20poly1305 = "0.10.1" # ChaCha20Poly1305 AEAD cipher
|
chacha20poly1305 = "0.10.1" # ChaCha20Poly1305 AEAD cipher
|
||||||
clap = "2.33" # Command-line argument parsing
|
clap = "2.34.0" # Command-line argument parsing
|
||||||
dirs = "5.0.1" # Directory paths
|
dirs = "6.0.0" # Directory paths
|
||||||
env_logger = "0.10.0" # Logger implementation
|
env_logger = "0.11.8" # Logger implementation
|
||||||
ethers = { version = "2.0.7", features = ["legacy"] } # Ethereum library
|
ethers = { version = "2.0.7", features = ["legacy"] } # Ethereum library
|
||||||
glob = "0.3.1" # For file pattern matching
|
glob = "0.3.1" # For file pattern matching
|
||||||
jsonrpsee = "0.25.1"
|
jsonrpsee = "0.25.1"
|
||||||
k256 = { version = "0.13.1", features = ["ecdsa"] } # Elliptic curve cryptography
|
k256 = { version = "0.13.4", features = ["ecdsa", "ecdh"] } # Elliptic curve cryptography
|
||||||
lazy_static = "1.4.0" # For lazy initialization of static variables
|
lazy_static = "1.4.0" # For lazy initialization of static variables
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4" # Logging facade
|
log = "0.4" # Logging facade
|
||||||
@ -31,7 +31,7 @@ postgres-types = "0.2.5" # PostgreSQL type conversions
|
|||||||
r2d2 = "0.8.10"
|
r2d2 = "0.8.10"
|
||||||
r2d2_postgres = "0.18.2"
|
r2d2_postgres = "0.18.2"
|
||||||
rand = "0.8.5" # Random number generation
|
rand = "0.8.5" # Random number generation
|
||||||
redis = "0.22.0" # Redis client
|
redis = "0.31.0" # Redis client
|
||||||
regex = "1.8.1" # For regex pattern matching
|
regex = "1.8.1" # For regex pattern matching
|
||||||
rhai = { version = "1.12.0", features = ["sync"] } # Embedded scripting language
|
rhai = { version = "1.12.0", features = ["sync"] } # Embedded scripting language
|
||||||
serde = { version = "1.0", features = [
|
serde = { version = "1.0", features = [
|
||||||
@ -41,7 +41,7 @@ serde_json = "1.0" # For JSON handling
|
|||||||
sha2 = "0.10.7" # SHA-2 hash functions
|
sha2 = "0.10.7" # SHA-2 hash functions
|
||||||
tempfile = "3.5" # For temporary file operations
|
tempfile = "3.5" # For temporary file operations
|
||||||
tera = "1.19.0" # Template engine for text rendering
|
tera = "1.19.0" # Template engine for text rendering
|
||||||
thiserror = "1.0" # For error handling
|
thiserror = "2.0.12" # For error handling
|
||||||
tokio = "1.45.0"
|
tokio = "1.45.0"
|
||||||
tokio-postgres = "0.7.8" # Async PostgreSQL client
|
tokio-postgres = "0.7.8" # Async PostgreSQL client
|
||||||
tokio-test = "0.4.4"
|
tokio-test = "0.4.4"
|
||||||
@ -52,17 +52,17 @@ urlencoding = "2.1.3"
|
|||||||
|
|
||||||
# Optional features for specific OS functionality
|
# Optional features for specific OS functionality
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
nix = "0.26" # Unix-specific functionality
|
nix = "0.30.1" # Unix-specific functionality
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = { version = "0.48", features = [
|
windows = { version = "0.61.1", features = [
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mockall = "0.11.4" # For mocking in tests
|
mockall = "0.13.1" # For mocking in tests
|
||||||
tempfile = "3.5" # For tests that need temporary files/directories
|
tempfile = "3.5" # For tests that need temporary files/directories
|
||||||
tokio = { version = "1.28", features = ["full", "test-util"] } # For async testing
|
tokio = { version = "1.28", features = ["full", "test-util"] } # For async testing
|
||||||
|
|
||||||
|
108
rhai_tests/keypair/01_keypair_operations.rhai
Normal file
108
rhai_tests/keypair/01_keypair_operations.rhai
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// 01_keypair_operations.rhai
|
||||||
|
// Tests for basic keypair operations in the Keypair module
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Basic Keypair Operations ===");
|
||||||
|
|
||||||
|
// Test creating a new keypair
|
||||||
|
print("Testing keypair creation...");
|
||||||
|
let keypair_name = "test_keypair";
|
||||||
|
if create_key_space("test_space", "password") {
|
||||||
|
print("✓ Key space created successfully");
|
||||||
|
|
||||||
|
if create_keypair(keypair_name, "password") {
|
||||||
|
print("✓ Keypair created successfully");
|
||||||
|
|
||||||
|
// Test getting the public key
|
||||||
|
print("Testing public key retrieval...");
|
||||||
|
if select_keypair(keypair_name) {
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
print(`✓ Public key retrieved: ${pub_key.len()} bytes`);
|
||||||
|
|
||||||
|
// Test signing a message
|
||||||
|
print("Testing message signing...");
|
||||||
|
let message = "This is a test message to sign";
|
||||||
|
let signature = keypair_sign(message);
|
||||||
|
assert_true(signature.len() > 0, "Signature should not be empty");
|
||||||
|
print(`✓ Message signed successfully: ${signature.len()} bytes`);
|
||||||
|
|
||||||
|
// Test verifying a signature
|
||||||
|
print("Testing signature verification...");
|
||||||
|
let is_valid = keypair_verify(message, signature);
|
||||||
|
assert_true(is_valid, "Signature should be valid");
|
||||||
|
print("✓ Signature verified successfully");
|
||||||
|
|
||||||
|
// Test verifying with just a public key
|
||||||
|
print("Testing verification with public key only...");
|
||||||
|
let is_valid_pub = verify_with_public_key(pub_key, message, signature);
|
||||||
|
assert_true(is_valid_pub, "Signature should be valid with public key only");
|
||||||
|
print("✓ Signature verified with public key only");
|
||||||
|
|
||||||
|
// Edge case: Empty message
|
||||||
|
print("Testing with empty message...");
|
||||||
|
let empty_message = "";
|
||||||
|
let empty_signature = keypair_sign(empty_message);
|
||||||
|
assert_true(empty_signature.len() > 0, "Signature for empty message should not be empty");
|
||||||
|
let is_valid_empty = keypair_verify(empty_message, empty_signature);
|
||||||
|
assert_true(is_valid_empty, "Empty message signature should be valid");
|
||||||
|
print("✓ Empty message signed and verified successfully");
|
||||||
|
|
||||||
|
// Edge case: Large message
|
||||||
|
print("Testing with large message...");
|
||||||
|
let large_message = "A" * 10000; // 10KB message
|
||||||
|
let large_signature = keypair_sign(large_message);
|
||||||
|
assert_true(large_signature.len() > 0, "Signature for large message should not be empty");
|
||||||
|
let is_valid_large = keypair_verify(large_message, large_signature);
|
||||||
|
assert_true(is_valid_large, "Large message signature should be valid");
|
||||||
|
print("✓ Large message signed and verified successfully");
|
||||||
|
|
||||||
|
// Error case: Invalid signature format
|
||||||
|
print("Testing with invalid signature format...");
|
||||||
|
let invalid_signature = [0, 1, 2, 3]; // Invalid signature bytes
|
||||||
|
let is_valid_invalid = false;
|
||||||
|
try {
|
||||||
|
is_valid_invalid = keypair_verify(message, invalid_signature);
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for invalid signature: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!is_valid_invalid, "Invalid signature should not verify");
|
||||||
|
|
||||||
|
// Error case: Tampered message
|
||||||
|
print("Testing with tampered message...");
|
||||||
|
let tampered_message = message + " (tampered)";
|
||||||
|
let is_valid_tampered = keypair_verify(tampered_message, signature);
|
||||||
|
assert_true(!is_valid_tampered, "Tampered message should not verify");
|
||||||
|
print("✓ Tampered message correctly failed verification");
|
||||||
|
|
||||||
|
// Error case: Malformed public key
|
||||||
|
print("Testing with malformed public key...");
|
||||||
|
let malformed_pub_key = [0, 1, 2, 3]; // Invalid public key bytes
|
||||||
|
let is_valid_malformed = false;
|
||||||
|
try {
|
||||||
|
is_valid_malformed = verify_with_public_key(malformed_pub_key, message, signature);
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for malformed public key: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!is_valid_malformed, "Malformed public key should not verify");
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to select keypair");
|
||||||
|
throw "Failed to select keypair";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to create keypair");
|
||||||
|
throw "Failed to create keypair";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to create key space");
|
||||||
|
throw "Failed to create key space";
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All keypair operations tests completed successfully!");
|
162
rhai_tests/keypair/02_keyspace_operations.rhai
Normal file
162
rhai_tests/keypair/02_keyspace_operations.rhai
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// 02_keyspace_operations.rhai
|
||||||
|
// Tests for key space operations in the Keypair module
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Key Space Operations ===");
|
||||||
|
|
||||||
|
// Test creating a new key space
|
||||||
|
print("Testing key space creation...");
|
||||||
|
let space_name = "test_keyspace";
|
||||||
|
let password = "secure_password";
|
||||||
|
|
||||||
|
if create_key_space(space_name, password) {
|
||||||
|
print(`✓ Key space "${space_name}" created successfully`);
|
||||||
|
|
||||||
|
// Test adding keypairs to a key space
|
||||||
|
print("Testing adding keypairs to key space...");
|
||||||
|
let keypair1_name = "keypair1";
|
||||||
|
let keypair2_name = "keypair2";
|
||||||
|
|
||||||
|
if create_keypair(keypair1_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair1_name}" created successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair "${keypair1_name}"`);
|
||||||
|
throw `Failed to create keypair "${keypair1_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if create_keypair(keypair2_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair2_name}" created successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair "${keypair2_name}"`);
|
||||||
|
throw `Failed to create keypair "${keypair2_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test listing keypairs in a key space
|
||||||
|
print("Testing listing keypairs in key space...");
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
assert_true(keypairs.len() == 2, `Expected 2 keypairs, got ${keypairs.len()}`);
|
||||||
|
assert_true(keypairs.contains(keypair1_name), `Keypair list should contain "${keypair1_name}"`);
|
||||||
|
assert_true(keypairs.contains(keypair2_name), `Keypair list should contain "${keypair2_name}"`);
|
||||||
|
print(`✓ Listed keypairs successfully: ${keypairs}`);
|
||||||
|
|
||||||
|
// Test getting a keypair by name
|
||||||
|
print("Testing getting a keypair by name...");
|
||||||
|
if select_keypair(keypair1_name) {
|
||||||
|
print(`✓ Selected keypair "${keypair1_name}" successfully`);
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
print(`✓ Retrieved public key for "${keypair1_name}": ${pub_key.len()} bytes`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select keypair "${keypair1_name}"`);
|
||||||
|
throw `Failed to select keypair "${keypair1_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge case: Attempt to add a keypair with a duplicate name
|
||||||
|
print("Testing adding a keypair with a duplicate name...");
|
||||||
|
let duplicate_success = false;
|
||||||
|
try {
|
||||||
|
duplicate_success = create_keypair(keypair1_name, password);
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for duplicate keypair: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!duplicate_success, "Creating a duplicate keypair should fail");
|
||||||
|
|
||||||
|
// Edge case: Attempt to get a non-existent keypair
|
||||||
|
print("Testing getting a non-existent keypair...");
|
||||||
|
let nonexistent_success = false;
|
||||||
|
try {
|
||||||
|
nonexistent_success = select_keypair("nonexistent_keypair");
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for non-existent keypair: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!nonexistent_success, "Selecting a non-existent keypair should fail");
|
||||||
|
|
||||||
|
// Edge case: Test with special characters in keypair names
|
||||||
|
print("Testing with special characters in keypair name...");
|
||||||
|
let special_name = "special!@#$%^&*()_+";
|
||||||
|
if create_keypair(special_name, password) {
|
||||||
|
print(`✓ Created keypair with special characters: "${special_name}"`);
|
||||||
|
|
||||||
|
// Verify we can select and use it
|
||||||
|
if select_keypair(special_name) {
|
||||||
|
print(`✓ Selected keypair with special characters`);
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select keypair with special characters`);
|
||||||
|
throw `Failed to select keypair with special characters`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair with special characters`);
|
||||||
|
throw `Failed to create keypair with special characters`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge case: Test with very long keypair name
|
||||||
|
print("Testing with very long keypair name...");
|
||||||
|
let long_name = "a" * 100; // 100 character name
|
||||||
|
if create_keypair(long_name, password) {
|
||||||
|
print(`✓ Created keypair with long name (${long_name.len()} characters)`);
|
||||||
|
|
||||||
|
// Verify we can select and use it
|
||||||
|
if select_keypair(long_name) {
|
||||||
|
print(`✓ Selected keypair with long name`);
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select keypair with long name`);
|
||||||
|
throw `Failed to select keypair with long name`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair with long name`);
|
||||||
|
throw `Failed to create keypair with long name`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge case: Test with empty keypair name (should fail)
|
||||||
|
print("Testing with empty keypair name...");
|
||||||
|
let empty_name = "";
|
||||||
|
let empty_name_success = false;
|
||||||
|
try {
|
||||||
|
empty_name_success = create_keypair(empty_name, password);
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for empty keypair name: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!empty_name_success, "Creating a keypair with empty name should fail");
|
||||||
|
|
||||||
|
// Stress test: Add multiple keypairs
|
||||||
|
print("Stress testing: Adding multiple keypairs...");
|
||||||
|
let num_keypairs = 10; // Add 10 more keypairs
|
||||||
|
let stress_keypairs = [];
|
||||||
|
|
||||||
|
for i in 0..num_keypairs {
|
||||||
|
let name = `stress_keypair_${i}`;
|
||||||
|
stress_keypairs.push(name);
|
||||||
|
|
||||||
|
if create_keypair(name, password) {
|
||||||
|
print(`✓ Created stress test keypair ${i+1}/${num_keypairs}`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create stress test keypair ${i+1}/${num_keypairs}`);
|
||||||
|
throw `Failed to create stress test keypair ${i+1}/${num_keypairs}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all keypairs were created
|
||||||
|
print("Verifying all stress test keypairs...");
|
||||||
|
let all_keypairs = list_keypairs();
|
||||||
|
for name in stress_keypairs {
|
||||||
|
assert_true(all_keypairs.contains(name), `Keypair list should contain "${name}"`);
|
||||||
|
}
|
||||||
|
print(`✓ All ${num_keypairs} stress test keypairs verified`);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create key space "${space_name}"`);
|
||||||
|
throw `Failed to create key space "${space_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All key space operations tests completed successfully!");
|
167
rhai_tests/keypair/03_session_management.rhai
Normal file
167
rhai_tests/keypair/03_session_management.rhai
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// 03_session_management.rhai
|
||||||
|
// Tests for session management in the Keypair module
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Session Management ===");
|
||||||
|
|
||||||
|
// Test creating a key space and setting it as current
|
||||||
|
print("Testing key space creation and activation...");
|
||||||
|
let space_name1 = "session_test_space1";
|
||||||
|
let space_name2 = "session_test_space2";
|
||||||
|
let password = "secure_password";
|
||||||
|
|
||||||
|
// Create first key space
|
||||||
|
if create_key_space(space_name1, password) {
|
||||||
|
print(`✓ Key space "${space_name1}" created successfully`);
|
||||||
|
|
||||||
|
// Test creating keypairs in the current space
|
||||||
|
print("Testing creating keypairs in current space...");
|
||||||
|
let keypair1_name = "session_keypair1";
|
||||||
|
|
||||||
|
if create_keypair(keypair1_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair1_name}" created successfully in space "${space_name1}"`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair "${keypair1_name}" in space "${space_name1}"`);
|
||||||
|
throw `Failed to create keypair "${keypair1_name}" in space "${space_name1}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test selecting a keypair
|
||||||
|
print("Testing selecting a keypair...");
|
||||||
|
if select_keypair(keypair1_name) {
|
||||||
|
print(`✓ Selected keypair "${keypair1_name}" successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select keypair "${keypair1_name}"`);
|
||||||
|
throw `Failed to select keypair "${keypair1_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test getting the selected keypair
|
||||||
|
print("Testing getting the selected keypair...");
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
print(`✓ Retrieved public key for selected keypair: ${pub_key.len()} bytes`);
|
||||||
|
|
||||||
|
// Create second key space
|
||||||
|
print("\nTesting creating and switching to a second key space...");
|
||||||
|
if create_key_space(space_name2, password) {
|
||||||
|
print(`✓ Key space "${space_name2}" created successfully`);
|
||||||
|
|
||||||
|
// Verify we're now in the second space
|
||||||
|
print("Verifying current space changed...");
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
assert_true(keypairs.len() == 0, `Expected 0 keypairs in new space, got ${keypairs.len()}`);
|
||||||
|
print("✓ Current space verified as the new space (empty keypair list)");
|
||||||
|
|
||||||
|
// Create a keypair in the second space
|
||||||
|
let keypair2_name = "session_keypair2";
|
||||||
|
if create_keypair(keypair2_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair2_name}" created successfully in space "${space_name2}"`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create keypair "${keypair2_name}" in space "${space_name2}"`);
|
||||||
|
throw `Failed to create keypair "${keypair2_name}" in space "${space_name2}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch back to first space
|
||||||
|
print("\nTesting switching back to first key space...");
|
||||||
|
if load_key_space(space_name1, password) {
|
||||||
|
print(`✓ Switched back to key space "${space_name1}" successfully`);
|
||||||
|
|
||||||
|
// Verify we're now in the first space
|
||||||
|
print("Verifying current space changed back...");
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
assert_true(keypairs.len() == 1, `Expected 1 keypair in original space, got ${keypairs.len()}`);
|
||||||
|
assert_true(keypairs.contains(keypair1_name), `Keypair list should contain "${keypair1_name}"`);
|
||||||
|
print("✓ Current space verified as the original space");
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch back to key space "${space_name1}"`);
|
||||||
|
throw `Failed to switch back to key space "${space_name1}"`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create second key space "${space_name2}"`);
|
||||||
|
throw `Failed to create second key space "${space_name2}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test clearing the session
|
||||||
|
print("\nTesting clearing the session...");
|
||||||
|
clear_session();
|
||||||
|
print("✓ Session cleared");
|
||||||
|
|
||||||
|
// Verify operations fail after clearing session
|
||||||
|
print("Verifying operations fail after clearing session...");
|
||||||
|
let list_success = false;
|
||||||
|
try {
|
||||||
|
list_keypairs();
|
||||||
|
list_success = true;
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error after clearing session: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!list_success, "Listing keypairs should fail after clearing session");
|
||||||
|
|
||||||
|
// Error case: Attempt operations without an active key space
|
||||||
|
print("\nTesting operations without an active key space...");
|
||||||
|
|
||||||
|
// Attempt to create a keypair
|
||||||
|
let create_success = false;
|
||||||
|
try {
|
||||||
|
create_success = create_keypair("no_space_keypair", password);
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for creating keypair without active space: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!create_success, "Creating a keypair without active space should fail");
|
||||||
|
|
||||||
|
// Attempt to select a keypair
|
||||||
|
let select_success = false;
|
||||||
|
try {
|
||||||
|
select_success = select_keypair("no_space_keypair");
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for selecting keypair without active space: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!select_success, "Selecting a keypair without active space should fail");
|
||||||
|
|
||||||
|
// Reload a key space
|
||||||
|
print("\nTesting reloading a key space after clearing session...");
|
||||||
|
if load_key_space(space_name1, password) {
|
||||||
|
print(`✓ Reloaded key space "${space_name1}" successfully`);
|
||||||
|
|
||||||
|
// Verify the keypair is still there
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
assert_true(keypairs.contains(keypair1_name), `Keypair list should contain "${keypair1_name}"`);
|
||||||
|
print("✓ Keypair still exists in reloaded space");
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to reload key space "${space_name1}"`);
|
||||||
|
throw `Failed to reload key space "${space_name1}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error case: Attempt to get selected keypair when none is selected
|
||||||
|
print("\nTesting getting selected keypair when none is selected...");
|
||||||
|
let get_selected_success = false;
|
||||||
|
try {
|
||||||
|
keypair_pub_key();
|
||||||
|
get_selected_success = true;
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for getting selected keypair when none selected: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!get_selected_success, "Getting selected keypair when none is selected should fail");
|
||||||
|
|
||||||
|
// Error case: Attempt to select non-existent keypair
|
||||||
|
print("\nTesting selecting a non-existent keypair...");
|
||||||
|
let select_nonexistent_success = false;
|
||||||
|
try {
|
||||||
|
select_nonexistent_success = select_keypair("nonexistent_keypair");
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for selecting non-existent keypair: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!select_nonexistent_success, "Selecting a non-existent keypair should fail");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create key space "${space_name1}"`);
|
||||||
|
throw `Failed to create key space "${space_name1}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All session management tests completed successfully!");
|
192
rhai_tests/keypair/04_encryption_decryption.rhai
Normal file
192
rhai_tests/keypair/04_encryption_decryption.rhai
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// 04_encryption_decryption.rhai
|
||||||
|
// Tests for asymmetric encryption and decryption in the Keypair module
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Asymmetric Encryption and Decryption ===");
|
||||||
|
|
||||||
|
// Test creating keypairs for sender and recipient
|
||||||
|
print("Setting up sender and recipient keypairs...");
|
||||||
|
let space_name = "encryption_test_space";
|
||||||
|
let password = "secure_password";
|
||||||
|
let sender_name = "sender_keypair";
|
||||||
|
let recipient_name = "recipient_keypair";
|
||||||
|
|
||||||
|
if create_key_space(space_name, password) {
|
||||||
|
print(`✓ Key space "${space_name}" created successfully`);
|
||||||
|
|
||||||
|
// Create sender keypair
|
||||||
|
if create_keypair(sender_name, password) {
|
||||||
|
print(`✓ Sender keypair "${sender_name}" created successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create sender keypair "${sender_name}"`);
|
||||||
|
throw `Failed to create sender keypair "${sender_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create recipient keypair
|
||||||
|
if create_keypair(recipient_name, password) {
|
||||||
|
print(`✓ Recipient keypair "${recipient_name}" created successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create recipient keypair "${recipient_name}"`);
|
||||||
|
throw `Failed to create recipient keypair "${recipient_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get recipient's public key
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Selected recipient keypair "${recipient_name}" successfully`);
|
||||||
|
let recipient_pub_key = keypair_pub_key();
|
||||||
|
assert_true(recipient_pub_key.len() > 0, "Recipient public key should not be empty");
|
||||||
|
print(`✓ Retrieved recipient public key: ${recipient_pub_key.len()} bytes`);
|
||||||
|
|
||||||
|
// Switch to sender keypair
|
||||||
|
if select_keypair(sender_name) {
|
||||||
|
print(`✓ Selected sender keypair "${sender_name}" successfully`);
|
||||||
|
|
||||||
|
// Test encrypting a message with recipient's public key
|
||||||
|
print("\nTesting encrypting a message...");
|
||||||
|
let message = "This is a secret message for the recipient";
|
||||||
|
let ciphertext = encrypt_asymmetric(recipient_pub_key, message);
|
||||||
|
assert_true(ciphertext.len() > 0, "Ciphertext should not be empty");
|
||||||
|
print(`✓ Message encrypted successfully: ${ciphertext.len()} bytes`);
|
||||||
|
|
||||||
|
// Switch back to recipient keypair to decrypt
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Switched back to recipient keypair "${recipient_name}" successfully`);
|
||||||
|
|
||||||
|
// Test decrypting the message
|
||||||
|
print("Testing decrypting the message...");
|
||||||
|
let decrypted = decrypt_asymmetric(ciphertext);
|
||||||
|
assert_true(decrypted == message, "Decrypted message should match original");
|
||||||
|
print(`✓ Message decrypted successfully: "${decrypted}"`);
|
||||||
|
|
||||||
|
// Edge case: Test with empty message
|
||||||
|
print("\nTesting with empty message...");
|
||||||
|
let empty_message = "";
|
||||||
|
let empty_ciphertext = encrypt_asymmetric(recipient_pub_key, empty_message);
|
||||||
|
assert_true(empty_ciphertext.len() > 0, "Ciphertext for empty message should not be empty");
|
||||||
|
|
||||||
|
let empty_decrypted = decrypt_asymmetric(empty_ciphertext);
|
||||||
|
assert_true(empty_decrypted == empty_message, "Decrypted empty message should be empty");
|
||||||
|
print("✓ Empty message encrypted and decrypted successfully");
|
||||||
|
|
||||||
|
// Edge case: Test with large message
|
||||||
|
print("\nTesting with large message...");
|
||||||
|
let large_message = "A" * 10000; // 10KB message
|
||||||
|
let large_ciphertext = encrypt_asymmetric(recipient_pub_key, large_message);
|
||||||
|
assert_true(large_ciphertext.len() > 0, "Ciphertext for large message should not be empty");
|
||||||
|
|
||||||
|
let large_decrypted = decrypt_asymmetric(large_ciphertext);
|
||||||
|
assert_true(large_decrypted == large_message, "Decrypted large message should match original");
|
||||||
|
print("✓ Large message encrypted and decrypted successfully");
|
||||||
|
|
||||||
|
// Error case: Attempt to decrypt with the wrong keypair
|
||||||
|
print("\nTesting decryption with wrong keypair...");
|
||||||
|
if select_keypair(sender_name) {
|
||||||
|
print(`✓ Switched to sender keypair "${sender_name}" successfully`);
|
||||||
|
|
||||||
|
let wrong_keypair_success = true;
|
||||||
|
try {
|
||||||
|
let wrong_decrypted = decrypt_asymmetric(ciphertext);
|
||||||
|
// If we get here, the decryption didn't fail as expected
|
||||||
|
assert_true(wrong_decrypted != message, "Decryption with wrong keypair should not match original message");
|
||||||
|
} catch(err) {
|
||||||
|
wrong_keypair_success = false;
|
||||||
|
print(`✓ Caught expected error for decryption with wrong keypair: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Some implementations might not throw an error but return garbage data
|
||||||
|
// So we don't assert on wrong_keypair_success
|
||||||
|
|
||||||
|
// Switch back to recipient for further tests
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Switched back to recipient keypair "${recipient_name}" successfully`);
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch back to recipient keypair "${recipient_name}"`);
|
||||||
|
throw `Failed to switch back to recipient keypair "${recipient_name}"`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch to sender keypair "${sender_name}"`);
|
||||||
|
throw `Failed to switch to sender keypair "${sender_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error case: Test with malformed ciphertext
|
||||||
|
print("\nTesting with malformed ciphertext...");
|
||||||
|
let malformed_ciphertext = [0, 1, 2, 3]; // Invalid ciphertext bytes
|
||||||
|
let malformed_success = false;
|
||||||
|
try {
|
||||||
|
decrypt_asymmetric(malformed_ciphertext);
|
||||||
|
malformed_success = true;
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for malformed ciphertext: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!malformed_success, "Decrypting malformed ciphertext should fail");
|
||||||
|
|
||||||
|
// Error case: Test with invalid public key for encryption
|
||||||
|
print("\nTesting encryption with invalid public key...");
|
||||||
|
if select_keypair(sender_name) {
|
||||||
|
print(`✓ Switched to sender keypair "${sender_name}" successfully`);
|
||||||
|
|
||||||
|
let invalid_pub_key = [0, 1, 2, 3]; // Invalid public key bytes
|
||||||
|
let invalid_key_success = false;
|
||||||
|
try {
|
||||||
|
encrypt_asymmetric(invalid_pub_key, message);
|
||||||
|
invalid_key_success = true;
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for invalid public key: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!invalid_key_success, "Encrypting with invalid public key should fail");
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch to sender keypair "${sender_name}"`);
|
||||||
|
throw `Failed to switch to sender keypair "${sender_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error case: Test with tampered ciphertext
|
||||||
|
print("\nTesting with tampered ciphertext...");
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Switched to recipient keypair "${recipient_name}" successfully`);
|
||||||
|
|
||||||
|
// Tamper with the ciphertext (change a byte in the middle)
|
||||||
|
let tampered_ciphertext = ciphertext.clone();
|
||||||
|
if tampered_ciphertext.len() > 100 {
|
||||||
|
tampered_ciphertext[100] = (tampered_ciphertext[100] + 1) % 256;
|
||||||
|
|
||||||
|
let tampered_success = false;
|
||||||
|
try {
|
||||||
|
let tampered_decrypted = decrypt_asymmetric(tampered_ciphertext);
|
||||||
|
tampered_success = tampered_decrypted == message;
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for tampered ciphertext: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(!tampered_success, "Decrypting tampered ciphertext should fail or produce incorrect result");
|
||||||
|
} else {
|
||||||
|
print("Note: Ciphertext too short to test tampering");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch to recipient keypair "${recipient_name}"`);
|
||||||
|
throw `Failed to switch to recipient keypair "${recipient_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to switch back to recipient keypair "${recipient_name}"`);
|
||||||
|
throw `Failed to switch back to recipient keypair "${recipient_name}"`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select sender keypair "${sender_name}"`);
|
||||||
|
throw `Failed to select sender keypair "${sender_name}"`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to select recipient keypair "${recipient_name}"`);
|
||||||
|
throw `Failed to select recipient keypair "${recipient_name}"`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(`✗ Failed to create key space "${space_name}"`);
|
||||||
|
throw `Failed to create key space "${space_name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All asymmetric encryption and decryption tests completed successfully!");
|
231
rhai_tests/keypair/05_error_handling.rhai
Normal file
231
rhai_tests/keypair/05_error_handling.rhai
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// 05_error_handling.rhai
|
||||||
|
// Comprehensive error handling tests for the Keypair module
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to test for expected errors
|
||||||
|
fn expect_error(fn_to_test, expected_error_substring) {
|
||||||
|
let error_caught = false;
|
||||||
|
let error_message = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
fn_to_test();
|
||||||
|
} catch(err) {
|
||||||
|
error_caught = true;
|
||||||
|
error_message = err.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !error_caught {
|
||||||
|
print(`ASSERTION FAILED: Expected error containing "${expected_error_substring}" but no error was thrown`);
|
||||||
|
throw `Expected error containing "${expected_error_substring}" but no error was thrown`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !error_message.contains(expected_error_substring) {
|
||||||
|
print(`ASSERTION FAILED: Expected error containing "${expected_error_substring}" but got "${error_message}"`);
|
||||||
|
throw `Expected error containing "${expected_error_substring}" but got "${error_message}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(`✓ Caught expected error: ${error_message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("=== Testing Error Handling ===");
|
||||||
|
|
||||||
|
// Test all error types defined in CryptoError
|
||||||
|
|
||||||
|
// 1. Test InvalidKeyLength error
|
||||||
|
print("\n--- Testing InvalidKeyLength error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
select_keypair("test_keypair");
|
||||||
|
|
||||||
|
// Try to verify with an invalid public key (wrong length)
|
||||||
|
verify_with_public_key([1, 2, 3], "test message", [1, 2, 3, 4]);
|
||||||
|
}, "InvalidKeyLength");
|
||||||
|
|
||||||
|
// 2. Test EncryptionFailed error
|
||||||
|
print("\n--- Testing EncryptionFailed error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
select_keypair("test_keypair");
|
||||||
|
|
||||||
|
// Try to encrypt with an invalid public key
|
||||||
|
encrypt_asymmetric([1, 2, 3], "test message");
|
||||||
|
}, "EncryptionFailed");
|
||||||
|
|
||||||
|
// 3. Test DecryptionFailed error
|
||||||
|
print("\n--- Testing DecryptionFailed error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
select_keypair("test_keypair");
|
||||||
|
|
||||||
|
// Try to decrypt invalid ciphertext
|
||||||
|
decrypt_asymmetric([1, 2, 3, 4]);
|
||||||
|
}, "DecryptionFailed");
|
||||||
|
|
||||||
|
// 4. Test SignatureFormatError error
|
||||||
|
print("\n--- Testing SignatureFormatError error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
select_keypair("test_keypair");
|
||||||
|
|
||||||
|
// Try to verify with an invalid signature format
|
||||||
|
keypair_verify("test message", [1, 2, 3]);
|
||||||
|
}, "SignatureFormatError");
|
||||||
|
|
||||||
|
// 5. Test KeypairAlreadyExists error
|
||||||
|
print("\n--- Testing KeypairAlreadyExists error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
create_keypair("duplicate_keypair", "password");
|
||||||
|
|
||||||
|
// Try to create a keypair with the same name
|
||||||
|
create_keypair("duplicate_keypair", "password");
|
||||||
|
}, "KeypairAlreadyExists");
|
||||||
|
|
||||||
|
// 6. Test KeypairNotFound error
|
||||||
|
print("\n--- Testing KeypairNotFound error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
|
||||||
|
// Try to select a non-existent keypair
|
||||||
|
select_keypair("nonexistent_keypair");
|
||||||
|
}, "KeypairNotFound");
|
||||||
|
|
||||||
|
// 7. Test NoActiveSpace error
|
||||||
|
print("\n--- Testing NoActiveSpace error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Clear the session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Try to create a keypair without an active space
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
}, "NoActiveSpace");
|
||||||
|
|
||||||
|
// 8. Test NoKeypairSelected error
|
||||||
|
print("\n--- Testing NoKeypairSelected error ---");
|
||||||
|
expect_error(|| {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
|
||||||
|
// Try to get the public key without selecting a keypair
|
||||||
|
keypair_pub_key();
|
||||||
|
}, "NoKeypairSelected");
|
||||||
|
|
||||||
|
// Test error propagation through the API
|
||||||
|
print("\n--- Testing error propagation ---");
|
||||||
|
let propagation_test = || {
|
||||||
|
// Create a key space for testing
|
||||||
|
create_key_space("error_test_space", "password");
|
||||||
|
|
||||||
|
// Create a keypair
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
|
||||||
|
// Clear the session to force an error
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// This should fail with NoActiveSpace
|
||||||
|
select_keypair("test_keypair");
|
||||||
|
|
||||||
|
// This line should never be reached
|
||||||
|
print("ERROR: Code execution continued after error");
|
||||||
|
};
|
||||||
|
|
||||||
|
expect_error(propagation_test, "NoActiveSpace");
|
||||||
|
|
||||||
|
// Test recovery from errors
|
||||||
|
print("\n--- Testing recovery from errors ---");
|
||||||
|
let recovery_success = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try an operation that will fail
|
||||||
|
clear_session();
|
||||||
|
list_keypairs(); // This should fail with NoActiveSpace
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error: ${err}`);
|
||||||
|
|
||||||
|
// Now recover by creating a new key space
|
||||||
|
if create_key_space("recovery_space", "password") {
|
||||||
|
// Create a keypair to verify recovery
|
||||||
|
if create_keypair("recovery_keypair", "password") {
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
if keypairs.contains("recovery_keypair") {
|
||||||
|
recovery_success = true;
|
||||||
|
print("✓ Successfully recovered from error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true(recovery_success, "Should be able to recover from errors");
|
||||||
|
|
||||||
|
// Test behavior when multiple errors occur in sequence
|
||||||
|
print("\n--- Testing sequential errors ---");
|
||||||
|
let sequential_errors_count = 0;
|
||||||
|
|
||||||
|
// First error: No active space
|
||||||
|
try {
|
||||||
|
clear_session();
|
||||||
|
list_keypairs();
|
||||||
|
} catch(err) {
|
||||||
|
sequential_errors_count += 1;
|
||||||
|
print(`✓ Caught first sequential error: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second error: Keypair not found
|
||||||
|
try {
|
||||||
|
create_key_space("sequential_space", "password");
|
||||||
|
select_keypair("nonexistent_keypair");
|
||||||
|
} catch(err) {
|
||||||
|
sequential_errors_count += 1;
|
||||||
|
print(`✓ Caught second sequential error: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third error: Keypair already exists
|
||||||
|
try {
|
||||||
|
create_keypair("sequential_keypair", "password");
|
||||||
|
create_keypair("sequential_keypair", "password");
|
||||||
|
} catch(err) {
|
||||||
|
sequential_errors_count += 1;
|
||||||
|
print(`✓ Caught third sequential error: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_true(sequential_errors_count == 3, `Expected 3 sequential errors, got ${sequential_errors_count}`);
|
||||||
|
|
||||||
|
// Test error handling with invalid parameters
|
||||||
|
print("\n--- Testing error handling with invalid parameters ---");
|
||||||
|
|
||||||
|
// Test with null/undefined parameters
|
||||||
|
try {
|
||||||
|
// Note: In Rhai, we can't directly pass null/undefined, but we can test with empty arrays
|
||||||
|
verify_with_public_key([], "message", []);
|
||||||
|
print("ERROR: verify_with_public_key with empty arrays didn't throw an error");
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for invalid parameters: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with wrong parameter types
|
||||||
|
try {
|
||||||
|
// Note: In Rhai, we can't easily pass wrong types, but we can test with strings instead of arrays
|
||||||
|
verify_with_public_key("not an array", "message", "not an array");
|
||||||
|
print("ERROR: verify_with_public_key with wrong types didn't throw an error");
|
||||||
|
} catch(err) {
|
||||||
|
print(`✓ Caught expected error for wrong parameter types: ${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All error handling tests completed successfully!");
|
293
rhai_tests/keypair/run_all_tests.rhai
Normal file
293
rhai_tests/keypair/run_all_tests.rhai
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
// run_all_tests.rhai
|
||||||
|
// Runs all Keypair module tests
|
||||||
|
|
||||||
|
print("=== Running Keypair Module Tests ===");
|
||||||
|
|
||||||
|
// Custom assert function
|
||||||
|
fn assert_true(condition, message) {
|
||||||
|
if !condition {
|
||||||
|
print(`ASSERTION FAILED: ${message}`);
|
||||||
|
throw message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run each test directly
|
||||||
|
let passed = 0;
|
||||||
|
let failed = 0;
|
||||||
|
let test_results = #{};
|
||||||
|
|
||||||
|
// Test 1: Keypair Operations
|
||||||
|
print("\n--- Running Keypair Operations Tests ---");
|
||||||
|
try {
|
||||||
|
// Clear any existing session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Test creating a new keypair
|
||||||
|
print("Testing keypair creation...");
|
||||||
|
let keypair_name = "test_keypair";
|
||||||
|
if create_key_space("test_space", "password") {
|
||||||
|
print("✓ Key space created successfully");
|
||||||
|
|
||||||
|
if create_keypair(keypair_name, "password") {
|
||||||
|
print("✓ Keypair created successfully");
|
||||||
|
|
||||||
|
// Test getting the public key
|
||||||
|
print("Testing public key retrieval...");
|
||||||
|
if select_keypair(keypair_name) {
|
||||||
|
let pub_key = keypair_pub_key();
|
||||||
|
assert_true(pub_key.len() > 0, "Public key should not be empty");
|
||||||
|
print(`✓ Public key retrieved: ${pub_key.len()} bytes`);
|
||||||
|
|
||||||
|
// Test signing a message
|
||||||
|
print("Testing message signing...");
|
||||||
|
let message = "This is a test message to sign";
|
||||||
|
let signature = keypair_sign(message);
|
||||||
|
assert_true(signature.len() > 0, "Signature should not be empty");
|
||||||
|
print(`✓ Message signed successfully: ${signature.len()} bytes`);
|
||||||
|
|
||||||
|
// Test verifying a signature
|
||||||
|
print("Testing signature verification...");
|
||||||
|
let is_valid = keypair_verify(message, signature);
|
||||||
|
assert_true(is_valid, "Signature should be valid");
|
||||||
|
print("✓ Signature verified successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- Keypair Operations Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
test_results["01_keypair_operations"] = "PASSED";
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Keypair Operations Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
test_results["01_keypair_operations"] = `FAILED: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Key Space Operations
|
||||||
|
print("\n--- Running Key Space Operations Tests ---");
|
||||||
|
try {
|
||||||
|
// Clear any existing session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Test creating a new key space
|
||||||
|
print("Testing key space creation...");
|
||||||
|
let space_name = "test_keyspace";
|
||||||
|
let password = "secure_password";
|
||||||
|
|
||||||
|
if create_key_space(space_name, password) {
|
||||||
|
print(`✓ Key space "${space_name}" created successfully`);
|
||||||
|
|
||||||
|
// Test adding keypairs to a key space
|
||||||
|
print("Testing adding keypairs to key space...");
|
||||||
|
let keypair1_name = "keypair1";
|
||||||
|
let keypair2_name = "keypair2";
|
||||||
|
|
||||||
|
if create_keypair(keypair1_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair1_name}" created successfully`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if create_keypair(keypair2_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair2_name}" created successfully`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test listing keypairs in a key space
|
||||||
|
print("Testing listing keypairs in key space...");
|
||||||
|
let keypairs = list_keypairs();
|
||||||
|
assert_true(keypairs.len() == 2, `Expected 2 keypairs, got ${keypairs.len()}`);
|
||||||
|
assert_true(keypairs.contains(keypair1_name), `Keypair list should contain "${keypair1_name}"`);
|
||||||
|
assert_true(keypairs.contains(keypair2_name), `Keypair list should contain "${keypair2_name}"`);
|
||||||
|
print(`✓ Listed keypairs successfully: ${keypairs}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- Key Space Operations Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
test_results["02_keyspace_operations"] = "PASSED";
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Key Space Operations Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
test_results["02_keyspace_operations"] = `FAILED: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Session Management
|
||||||
|
print("\n--- Running Session Management Tests ---");
|
||||||
|
try {
|
||||||
|
// Clear any existing session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Test creating a key space and setting it as current
|
||||||
|
print("Testing key space creation and activation...");
|
||||||
|
let space_name1 = "session_test_space1";
|
||||||
|
let space_name2 = "session_test_space2";
|
||||||
|
let password = "secure_password";
|
||||||
|
|
||||||
|
// Create first key space
|
||||||
|
if create_key_space(space_name1, password) {
|
||||||
|
print(`✓ Key space "${space_name1}" created successfully`);
|
||||||
|
|
||||||
|
// Test creating keypairs in the current space
|
||||||
|
print("Testing creating keypairs in current space...");
|
||||||
|
let keypair1_name = "session_keypair1";
|
||||||
|
|
||||||
|
if create_keypair(keypair1_name, password) {
|
||||||
|
print(`✓ Keypair "${keypair1_name}" created successfully in space "${space_name1}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test selecting a keypair
|
||||||
|
print("Testing selecting a keypair...");
|
||||||
|
if select_keypair(keypair1_name) {
|
||||||
|
print(`✓ Selected keypair "${keypair1_name}" successfully`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- Session Management Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
test_results["03_session_management"] = "PASSED";
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Session Management Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
test_results["03_session_management"] = `FAILED: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Encryption and Decryption
|
||||||
|
print("\n--- Running Encryption and Decryption Tests ---");
|
||||||
|
try {
|
||||||
|
// Clear any existing session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Test creating keypairs for sender and recipient
|
||||||
|
print("Setting up sender and recipient keypairs...");
|
||||||
|
let space_name = "encryption_test_space";
|
||||||
|
let password = "secure_password";
|
||||||
|
let sender_name = "sender_keypair";
|
||||||
|
let recipient_name = "recipient_keypair";
|
||||||
|
|
||||||
|
if create_key_space(space_name, password) {
|
||||||
|
print(`✓ Key space "${space_name}" created successfully`);
|
||||||
|
|
||||||
|
// Create sender keypair
|
||||||
|
if create_keypair(sender_name, password) {
|
||||||
|
print(`✓ Sender keypair "${sender_name}" created successfully`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create recipient keypair
|
||||||
|
if create_keypair(recipient_name, password) {
|
||||||
|
print(`✓ Recipient keypair "${recipient_name}" created successfully`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get recipient's public key
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Selected recipient keypair "${recipient_name}" successfully`);
|
||||||
|
let recipient_pub_key = keypair_pub_key();
|
||||||
|
|
||||||
|
// Switch to sender keypair
|
||||||
|
if select_keypair(sender_name) {
|
||||||
|
print(`✓ Selected sender keypair "${sender_name}" successfully`);
|
||||||
|
|
||||||
|
// Test encrypting a message with recipient's public key
|
||||||
|
print("\nTesting encrypting a message...");
|
||||||
|
let message = "This is a secret message for the recipient";
|
||||||
|
let ciphertext = encrypt_asymmetric(recipient_pub_key, message);
|
||||||
|
|
||||||
|
// Switch back to recipient keypair to decrypt
|
||||||
|
if select_keypair(recipient_name) {
|
||||||
|
print(`✓ Switched back to recipient keypair "${recipient_name}" successfully`);
|
||||||
|
|
||||||
|
// Test decrypting the message
|
||||||
|
print("Testing decrypting the message...");
|
||||||
|
let decrypted = decrypt_asymmetric(ciphertext);
|
||||||
|
assert_true(decrypted == message, "Decrypted message should match original");
|
||||||
|
print(`✓ Message decrypted successfully: "${decrypted}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- Encryption and Decryption Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
test_results["04_encryption_decryption"] = "PASSED";
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Encryption and Decryption Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
test_results["04_encryption_decryption"] = `FAILED: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Error Handling
|
||||||
|
print("\n--- Running Error Handling Tests ---");
|
||||||
|
try {
|
||||||
|
// Clear any existing session
|
||||||
|
clear_session();
|
||||||
|
|
||||||
|
// Test NoActiveSpace error
|
||||||
|
print("Testing NoActiveSpace error...");
|
||||||
|
let no_active_space_error_caught = false;
|
||||||
|
try {
|
||||||
|
// Try to create a keypair without an active space
|
||||||
|
create_keypair("test_keypair", "password");
|
||||||
|
} catch(err) {
|
||||||
|
no_active_space_error_caught = true;
|
||||||
|
print(`✓ Caught expected error: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(no_active_space_error_caught, "NoActiveSpace error should be caught");
|
||||||
|
|
||||||
|
// Create a key space for further tests
|
||||||
|
if create_key_space("error_test_space", "password") {
|
||||||
|
print(`✓ Key space created successfully`);
|
||||||
|
|
||||||
|
// Test KeypairNotFound error
|
||||||
|
print("Testing KeypairNotFound error...");
|
||||||
|
let keypair_not_found_error_caught = false;
|
||||||
|
try {
|
||||||
|
// Try to select a non-existent keypair
|
||||||
|
select_keypair("nonexistent_keypair");
|
||||||
|
} catch(err) {
|
||||||
|
keypair_not_found_error_caught = true;
|
||||||
|
print(`✓ Caught expected error: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(keypair_not_found_error_caught, "KeypairNotFound error should be caught");
|
||||||
|
|
||||||
|
// Test NoKeypairSelected error
|
||||||
|
print("Testing NoKeypairSelected error...");
|
||||||
|
let no_keypair_selected_error_caught = false;
|
||||||
|
try {
|
||||||
|
// Try to get the public key without selecting a keypair
|
||||||
|
keypair_pub_key();
|
||||||
|
} catch(err) {
|
||||||
|
no_keypair_selected_error_caught = true;
|
||||||
|
print(`✓ Caught expected error: ${err}`);
|
||||||
|
}
|
||||||
|
assert_true(no_keypair_selected_error_caught, "NoKeypairSelected error should be caught");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("--- Error Handling Tests completed successfully ---");
|
||||||
|
passed += 1;
|
||||||
|
test_results["05_error_handling"] = "PASSED";
|
||||||
|
} catch(err) {
|
||||||
|
print(`!!! Error in Error Handling Tests: ${err}`);
|
||||||
|
failed += 1;
|
||||||
|
test_results["05_error_handling"] = `FAILED: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n=== Test Summary ===");
|
||||||
|
print(`Passed: ${passed}`);
|
||||||
|
print(`Failed: ${failed}`);
|
||||||
|
print(`Total: ${passed + failed}`);
|
||||||
|
|
||||||
|
// Print detailed results
|
||||||
|
print("\n=== Detailed Test Results ===");
|
||||||
|
for key in test_results.keys() {
|
||||||
|
let result = test_results[key];
|
||||||
|
if result.starts_with("PASSED") {
|
||||||
|
print(`✓ ${key}: ${result}`);
|
||||||
|
} else {
|
||||||
|
print(`✗ ${key}: ${result}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed == 0 {
|
||||||
|
print("\n✅ All tests passed!");
|
||||||
|
} else {
|
||||||
|
print("\n❌ Some tests failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of failed tests (0 means success)
|
||||||
|
failed;
|
@ -16,7 +16,7 @@ static ETH_WALLETS: Lazy<Mutex<HashMap<String, Vec<EthereumWallet>>>> = Lazy::ne
|
|||||||
/// Creates an Ethereum wallet from the currently selected keypair for a specific network.
|
/// Creates an Ethereum wallet from the currently selected keypair for a specific network.
|
||||||
pub fn create_ethereum_wallet_for_network(network: NetworkConfig) -> Result<EthereumWallet, CryptoError> {
|
pub fn create_ethereum_wallet_for_network(network: NetworkConfig) -> Result<EthereumWallet, CryptoError> {
|
||||||
// Get the currently selected keypair
|
// Get the currently selected keypair
|
||||||
let keypair = crate::vault::keypair::get_selected_keypair()?;
|
let keypair = crate::vault::keyspace::get_selected_keypair()?;
|
||||||
|
|
||||||
// Create an Ethereum wallet from the keypair
|
// Create an Ethereum wallet from the keypair
|
||||||
let wallet = EthereumWallet::from_keypair(&keypair, network)?;
|
let wallet = EthereumWallet::from_keypair(&keypair, network)?;
|
||||||
@ -77,7 +77,7 @@ pub fn clear_ethereum_wallets_for_network(network_name: &str) {
|
|||||||
/// Creates an Ethereum wallet from a name and the currently selected keypair for a specific network.
|
/// Creates an Ethereum wallet from a name and the currently selected keypair for a specific network.
|
||||||
pub fn create_ethereum_wallet_from_name_for_network(name: &str, network: NetworkConfig) -> Result<EthereumWallet, CryptoError> {
|
pub fn create_ethereum_wallet_from_name_for_network(name: &str, network: NetworkConfig) -> Result<EthereumWallet, CryptoError> {
|
||||||
// Get the currently selected keypair
|
// Get the currently selected keypair
|
||||||
let keypair = crate::vault::keypair::get_selected_keypair()?;
|
let keypair = crate::vault::keyspace::get_selected_keypair()?;
|
||||||
|
|
||||||
// Create an Ethereum wallet from the name and keypair
|
// Create an Ethereum wallet from the name and keypair
|
||||||
let wallet = EthereumWallet::from_name_and_keypair(name, &keypair, network)?;
|
let wallet = EthereumWallet::from_name_and_keypair(name, &keypair, network)?;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/// Implementation of keypair functionality.
|
/// Implementation of keypair functionality.
|
||||||
|
|
||||||
use k256::ecdsa::{SigningKey, VerifyingKey, signature::{Signer, Verifier}, Signature};
|
use k256::ecdsa::{SigningKey, VerifyingKey, signature::{Signer, Verifier}, Signature};
|
||||||
|
use k256::ecdh::EphemeralSecret;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -214,22 +215,24 @@ impl KeyPair {
|
|||||||
let ephemeral_signing_key = SigningKey::random(&mut OsRng);
|
let ephemeral_signing_key = SigningKey::random(&mut OsRng);
|
||||||
let ephemeral_public_key = VerifyingKey::from(&ephemeral_signing_key);
|
let ephemeral_public_key = VerifyingKey::from(&ephemeral_signing_key);
|
||||||
|
|
||||||
// Derive shared secret (this is a simplified ECDH)
|
// Derive shared secret using ECDH
|
||||||
// In a real implementation, we would use proper ECDH, but for this example:
|
let ephemeral_secret = EphemeralSecret::random(&mut OsRng);
|
||||||
let shared_point = recipient_key.to_encoded_point(false);
|
let shared_secret = ephemeral_secret.diffie_hellman(&recipient_key.to_public_key());
|
||||||
let shared_secret = {
|
|
||||||
|
// Derive encryption key from the shared secret (e.g., using HKDF or hashing)
|
||||||
|
// For simplicity, we'll hash the shared secret here
|
||||||
|
let encryption_key = {
|
||||||
let mut hasher = Sha256::default();
|
let mut hasher = Sha256::default();
|
||||||
hasher.update(ephemeral_signing_key.to_bytes());
|
hasher.update(shared_secret.raw_secret_bytes());
|
||||||
hasher.update(shared_point.as_bytes());
|
|
||||||
hasher.finalize().to_vec()
|
hasher.finalize().to_vec()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Encrypt the message using the derived key
|
// 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()))?;
|
.map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
|
||||||
|
|
||||||
// Format: ephemeral_public_key || ciphertext
|
// Format: ephemeral_public_key || ciphertext
|
||||||
let mut result = ephemeral_public_key.to_sec1_bytes().to_vec();
|
let mut result = ephemeral_public_key.to_encoded_point(false).as_bytes().to_vec();
|
||||||
result.extend_from_slice(&ciphertext);
|
result.extend_from_slice(&ciphertext);
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -252,17 +255,19 @@ impl KeyPair {
|
|||||||
let sender_key = VerifyingKey::from_sec1_bytes(ephemeral_public_key)
|
let sender_key = VerifyingKey::from_sec1_bytes(ephemeral_public_key)
|
||||||
.map_err(|_| CryptoError::InvalidKeyLength)?;
|
.map_err(|_| CryptoError::InvalidKeyLength)?;
|
||||||
|
|
||||||
// Derive shared secret (simplified ECDH)
|
// Derive shared secret using ECDH
|
||||||
let shared_point = sender_key.to_encoded_point(false);
|
let recipient_secret = EphemeralSecret::random(&mut OsRng);
|
||||||
let shared_secret = {
|
let shared_secret = recipient_secret.diffie_hellman(&sender_key.to_public_key());
|
||||||
|
|
||||||
|
// Derive decryption key from the shared secret (using the same method as encryption)
|
||||||
|
let decryption_key = {
|
||||||
let mut hasher = Sha256::default();
|
let mut hasher = Sha256::default();
|
||||||
hasher.update(self.signing_key.to_bytes());
|
hasher.update(shared_secret.raw_secret_bytes());
|
||||||
hasher.update(shared_point.as_bytes());
|
|
||||||
hasher.finalize().to_vec()
|
hasher.finalize().to_vec()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decrypt the message using the derived key
|
// Decrypt the message using the derived key
|
||||||
implementation::decrypt_with_key(&shared_secret, actual_ciphertext)
|
implementation::decrypt_with_key(&decryption_key, actual_ciphertext)
|
||||||
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
|
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,3 +13,6 @@ pub use session_manager::{
|
|||||||
keypair_pub_key, derive_public_key, keypair_sign, keypair_verify,
|
keypair_pub_key, derive_public_key, keypair_sign, keypair_verify,
|
||||||
verify_with_public_key, encrypt_asymmetric, decrypt_asymmetric
|
verify_with_public_key, encrypt_asymmetric, decrypt_asymmetric
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
@ -2,7 +2,7 @@ use once_cell::sync::Lazy;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::vault::error::CryptoError;
|
use crate::vault::error::CryptoError;
|
||||||
use crate::vault::keypair::keypair_types::{KeyPair, KeySpace}; // Assuming KeyPair and KeySpace will be in keypair_types.rs
|
use crate::vault::keyspace::keypair_types::{KeyPair, KeySpace}; // Assuming KeyPair and KeySpace will be in keypair_types.rs
|
||||||
|
|
||||||
/// Session state for the current key space and selected keypair.
|
/// Session state for the current key space and selected keypair.
|
||||||
pub struct Session {
|
pub struct Session {
|
36
src/vault/keyspace/spec.md
Normal file
36
src/vault/keyspace/spec.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Keyspace Module Specification
|
||||||
|
|
||||||
|
This document explains the purpose and functionality of the `keyspace` module within the Hero Vault.
|
||||||
|
|
||||||
|
## Purpose of the Module
|
||||||
|
|
||||||
|
The `keyspace` module provides a secure and organized way to manage cryptographic keypairs. It allows for the creation, storage, loading, and utilization of keypairs within designated containers called keyspaces. This module is essential for handling sensitive cryptographic material securely.
|
||||||
|
|
||||||
|
## What is a Keyspace?
|
||||||
|
|
||||||
|
A keyspace is a logical container designed to hold multiple cryptographic keypairs. It is represented by the `KeySpace` struct in the code. Keyspaces can be encrypted and persisted to disk, providing a secure method for storing collections of keypairs. Each keyspace is identified by a unique name.
|
||||||
|
|
||||||
|
## What is a Keypair?
|
||||||
|
|
||||||
|
A keypair, represented by the `KeyPair` struct, is a fundamental cryptographic element consisting of a mathematically linked pair of keys: a public key and a private key. In this module, ECDSA (Elliptic Curve Digital Signature Algorithm) keypairs are used.
|
||||||
|
|
||||||
|
* **Private Key:** This key is kept secret and is used for operations like signing data or decrypting messages intended for the keypair's owner.
|
||||||
|
* **Public Key:** This key can be shared openly and is used to verify signatures created by the corresponding private key or to encrypt messages that can only be decrypted by the private key.
|
||||||
|
|
||||||
|
## How Many Keypairs Per Space?
|
||||||
|
|
||||||
|
A keyspace can hold multiple keypairs. The `KeySpace` struct uses a `HashMap` to store keypairs, where each keypair is associated with a unique string name. There is no inherent, fixed limit on the number of keypairs a keyspace can contain, beyond the practical limitations of system memory.
|
||||||
|
|
||||||
|
## How Do We Load Them?
|
||||||
|
|
||||||
|
Keyspaces are loaded from persistent storage (disk) using the `KeySpace::load` function, which requires the keyspace name and a password for decryption. Once a `KeySpace` object is loaded into memory, it can be set as the currently active keyspace for the session using the `session_manager::set_current_space` function. Individual keypairs within the loaded keyspace are then accessed by their names using functions like `session_manager::select_keypair` and `session_manager::get_selected_keypair`.
|
||||||
|
|
||||||
|
## What Do They Do?
|
||||||
|
|
||||||
|
Keypairs within a keyspace are used to perform various cryptographic operations. The `KeyPair` struct provides methods for:
|
||||||
|
|
||||||
|
* **Digital Signatures:** Signing messages with the private key (`KeyPair::sign`) and verifying those signatures with the public key (`KeyPair::verify`).
|
||||||
|
* **Ethereum Address Derivation:** Generating an Ethereum address from the public key (`KeyPair::to_ethereum_address`).
|
||||||
|
* **Asymmetric Encryption/Decryption:** Encrypting data using a recipient's public key (`KeyPair::encrypt_asymmetric`) and decrypting data encrypted with the keypair's public key using the private key (`KeyPair::decrypt_asymmetric`).
|
||||||
|
|
||||||
|
The `session_manager` module provides functions that utilize the currently selected keypair to perform these operations within the context of the active session.
|
86
src/vault/keyspace/tests/keypair_types_tests.rs
Normal file
86
src/vault/keyspace/tests/keypair_types_tests.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
use crate::vault::keyspace::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());
|
||||||
|
}
|
||||||
|
}
|
3
src/vault/keyspace/tests/mod.rs
Normal file
3
src/vault/keyspace/tests/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
mod keypair_types_tests;
|
||||||
|
mod session_manager_tests;
|
111
src/vault/keyspace/tests/session_manager_tests.rs
Normal file
111
src/vault/keyspace/tests/session_manager_tests.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use crate::vault::keyspace::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::keyspace::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());
|
||||||
|
}
|
||||||
|
}
|
@ -165,3 +165,9 @@ let loaded_store = KvStore::load("my_store", "secure_password")?;
|
|||||||
let api_key = loaded_store.get("api_key")?;
|
let api_key = loaded_store.get("api_key")?;
|
||||||
println!("API Key: {}", api_key.unwrap_or_default());
|
println!("API Key: {}", api_key.unwrap_or_default());
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## to test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test --lib vault::keypair
|
||||||
|
```
|
@ -12,3 +12,6 @@ pub use store::{
|
|||||||
create_store, open_store, delete_store,
|
create_store, open_store, delete_store,
|
||||||
list_stores, get_store_path
|
list_stores, get_store_path
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
1
src/vault/kvs/tests/mod.rs
Normal file
1
src/vault/kvs/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod store_tests;
|
105
src/vault/kvs/tests/store_tests.rs
Normal file
105
src/vault/kvs/tests/store_tests.rs
Normal file
@ -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<String, _> = 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);
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
//! - Key-value store with encryption
|
//! - Key-value store with encryption
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod keypair;
|
pub mod keyspace;
|
||||||
pub mod symmetric;
|
pub mod symmetric;
|
||||||
pub mod ethereum;
|
pub mod ethereum;
|
||||||
pub mod kvs;
|
pub mod kvs;
|
||||||
@ -17,4 +17,4 @@ pub mod kvs;
|
|||||||
// Re-export modules
|
// Re-export modules
|
||||||
// Re-export common types for convenience
|
// Re-export common types for convenience
|
||||||
pub use error::CryptoError;
|
pub use error::CryptoError;
|
||||||
pub use keypair::{KeyPair, KeySpace};
|
pub use keyspace::{KeyPair, KeySpace};
|
||||||
|
@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize};
|
|||||||
use sha2::{Sha256, Digest};
|
use sha2::{Sha256, Digest};
|
||||||
|
|
||||||
use crate::vault::error::CryptoError;
|
use crate::vault::error::CryptoError;
|
||||||
use crate::vault::keypair::KeySpace;
|
use crate::vault::keyspace::KeySpace;
|
||||||
|
|
||||||
/// The size of the nonce in bytes.
|
/// The size of the nonce in bytes.
|
||||||
const NONCE_SIZE: usize = 12;
|
const NONCE_SIZE: usize = 12;
|
||||||
|
Loading…
Reference in New Issue
Block a user