feat: Update SAL Vault examples and documentation #24
@@ -1,64 +1,76 @@
 | 
				
			|||||||
# Hero Vault Cryptography Examples
 | 
					# SAL Vault Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This directory contains examples demonstrating the Hero Vault cryptography functionality integrated into the SAL project.
 | 
					This directory contains examples demonstrating the SAL Vault functionality.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Overview
 | 
					## Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Hero Vault provides cryptographic operations including:
 | 
					SAL Vault provides secure key management and cryptographic operations including:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Key space management (creation, loading, encryption, decryption)
 | 
					- Vault creation and management
 | 
				
			||||||
- Keypair management (creation, selection, listing)
 | 
					- KeySpace operations (encrypted key-value stores)
 | 
				
			||||||
- Digital signatures (signing and verification)
 | 
					- Symmetric key generation and operations
 | 
				
			||||||
- Symmetric encryption (key generation, encryption, decryption)
 | 
					- Asymmetric key operations (signing and verification)
 | 
				
			||||||
- Ethereum wallet functionality
 | 
					- Secure key derivation from passwords
 | 
				
			||||||
- Smart contract interactions
 | 
					 | 
				
			||||||
- Key-value store with encryption
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Example Files
 | 
					## Current Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `example.rhai` - Basic example demonstrating key management, signing, and encryption
 | 
					⚠️ **Note**: The vault module is currently being updated to use Lee's implementation.
 | 
				
			||||||
- `advanced_example.rhai` - Advanced example with error handling, conditional logic, and more complex operations
 | 
					The Rhai scripting integration is temporarily disabled while we adapt the examples
 | 
				
			||||||
- `key_persistence_example.rhai` - Demonstrates creating and saving a key space to disk
 | 
					to work with the new vault API.
 | 
				
			||||||
- `load_existing_space.rhai` - Shows how to load a previously created key space and use its keypairs
 | 
					 | 
				
			||||||
- `contract_example.rhai` - Demonstrates loading a contract ABI and interacting with smart contracts
 | 
					 | 
				
			||||||
- `agung_send_transaction.rhai` - Demonstrates sending native tokens on the Agung network
 | 
					 | 
				
			||||||
- `agung_contract_with_args.rhai` - Shows how to interact with contracts with arguments on Agung
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Running the Examples
 | 
					## Available Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can run the examples using the `herodo` tool that comes with the SAL project:
 | 
					- **Vault Management**: Create and manage vault instances
 | 
				
			||||||
 | 
					- **KeySpace Operations**: Open encrypted key-value stores within vaults
 | 
				
			||||||
 | 
					- **Symmetric Encryption**: Generate keys and encrypt/decrypt data
 | 
				
			||||||
 | 
					- **Asymmetric Operations**: Create keypairs, sign messages, verify signatures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					## Example Files (Legacy - Sameh's Implementation)
 | 
				
			||||||
# Run a single example
 | 
					 | 
				
			||||||
herodo --path example.rhai
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Run all examples using the provided script
 | 
					⚠️ **These examples are currently archived and use the previous vault implementation**:
 | 
				
			||||||
./run_examples.sh
 | 
					
 | 
				
			||||||
 | 
					- `_archive/example.rhai` - Basic example demonstrating key management, signing, and encryption
 | 
				
			||||||
 | 
					- `_archive/advanced_example.rhai` - Advanced example with error handling and complex operations
 | 
				
			||||||
 | 
					- `_archive/key_persistence_example.rhai` - Demonstrates creating and saving a key space to disk
 | 
				
			||||||
 | 
					- `_archive/load_existing_space.rhai` - Shows how to load a previously created key space
 | 
				
			||||||
 | 
					- `_archive/contract_example.rhai` - Demonstrates smart contract interactions (Ethereum)
 | 
				
			||||||
 | 
					- `_archive/agung_send_transaction.rhai` - Demonstrates Ethereum transactions on Agung network
 | 
				
			||||||
 | 
					- `_archive/agung_contract_with_args.rhai` - Shows contract interactions with arguments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Current Implementation (Lee's Vault)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The current vault implementation provides:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Create a new vault
 | 
				
			||||||
 | 
					let vault = Vault::new(&path).await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Open an encrypted keyspace
 | 
				
			||||||
 | 
					let keyspace = vault.open_keyspace("my_space", "password").await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Perform cryptographic operations
 | 
				
			||||||
 | 
					// (API documentation coming soon)
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Key Space Storage
 | 
					## Migration Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Key spaces are stored in the `~/.hero-vault/key-spaces/` directory by default. Each key space is stored in a separate JSON file named after the key space (e.g., `my_space.json`).
 | 
					- ✅ **Vault Core**: Lee's implementation is active
 | 
				
			||||||
 | 
					- ✅ **Archive**: Sameh's implementation preserved in `vault/_archive/`
 | 
				
			||||||
## Ethereum Functionality
 | 
					- ⏳ **Rhai Integration**: Being developed for Lee's implementation
 | 
				
			||||||
 | 
					- ⏳ **Examples**: Will be updated to use Lee's API
 | 
				
			||||||
The Hero Vault module provides comprehensive Ethereum wallet functionality:
 | 
					- ❌ **Ethereum Features**: Not available in Lee's implementation
 | 
				
			||||||
 | 
					 | 
				
			||||||
- Creating and managing wallets for different networks
 | 
					 | 
				
			||||||
- Sending ETH transactions
 | 
					 | 
				
			||||||
- Checking balances
 | 
					 | 
				
			||||||
- Interacting with smart contracts (read and write functions)
 | 
					 | 
				
			||||||
- Support for multiple networks (Ethereum, Gnosis, Peaq, Agung, etc.)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Security
 | 
					## Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Key spaces are encrypted with ChaCha20Poly1305 using a key derived from the provided password. The encryption ensures that the key material is secure at rest.
 | 
					The vault uses:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Best Practices
 | 
					- **ChaCha20Poly1305** for symmetric encryption
 | 
				
			||||||
 | 
					- **Password-based key derivation** for keyspace encryption
 | 
				
			||||||
 | 
					- **Secure key storage** with proper isolation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. **Use Strong Passwords**: Since the security of your key spaces depends on the strength of your passwords, use strong, unique passwords.
 | 
					## Next Steps
 | 
				
			||||||
2. **Backup Key Spaces**: Regularly backup your key spaces directory to prevent data loss.
 | 
					
 | 
				
			||||||
3. **Script Organization**: Split your scripts into logical units, with separate scripts for key creation and key usage.
 | 
					1. **Rhai Integration**: Implement Rhai bindings for Lee's vault
 | 
				
			||||||
4. **Error Handling**: Always check the return values of functions to ensure operations succeeded before proceeding.
 | 
					2. **New Examples**: Create examples using Lee's simpler API
 | 
				
			||||||
5. **Network Selection**: When working with Ethereum functionality, be explicit about which network you're targeting to avoid confusion.
 | 
					3. **Documentation**: Complete API documentation for Lee's implementation
 | 
				
			||||||
6. **Gas Management**: For Ethereum transactions, consider gas costs and set appropriate gas limits.
 | 
					4. **Migration Guide**: Provide guidance for users migrating from Sameh's implementation
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -96,8 +96,9 @@ pub use sal_text::rhai::register_text_module;
 | 
				
			|||||||
// Re-export net module
 | 
					// Re-export net module
 | 
				
			||||||
pub use sal_net::rhai::register_net_module;
 | 
					pub use sal_net::rhai::register_net_module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Re-export crypto module
 | 
					// Re-export crypto module - TEMPORARILY DISABLED
 | 
				
			||||||
pub use sal_vault::rhai::register_crypto_module;
 | 
					// TODO: Implement rhai module for Lee's vault implementation
 | 
				
			||||||
 | 
					// pub use sal_vault::rhai::register_crypto_module;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Re-export kubernetes module
 | 
					// Re-export kubernetes module
 | 
				
			||||||
pub use sal_kubernetes::rhai::register_kubernetes_module;
 | 
					pub use sal_kubernetes::rhai::register_kubernetes_module;
 | 
				
			||||||
@@ -158,8 +159,9 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // RFS module functions are now registered as part of sal_virt above
 | 
					    // RFS module functions are now registered as part of sal_virt above
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register Crypto module functions
 | 
					    // Register Crypto module functions - TEMPORARILY DISABLED
 | 
				
			||||||
    register_crypto_module(engine)?;
 | 
					    // TODO: Implement rhai module for Lee's vault implementation
 | 
				
			||||||
 | 
					    // register_crypto_module(engine)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register Kubernetes module functions
 | 
					    // Register Kubernetes module functions
 | 
				
			||||||
    register_kubernetes_module(engine)?;
 | 
					    register_kubernetes_module(engine)?;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -216,7 +216,7 @@ fn test_module_registration_functions() {
 | 
				
			|||||||
    assert!(sal_rhai::register_os_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_os_module(&mut engine).is_ok());
 | 
				
			||||||
    assert!(sal_rhai::register_process_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_process_module(&mut engine).is_ok());
 | 
				
			||||||
    assert!(sal_rhai::register_git_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_git_module(&mut engine).is_ok());
 | 
				
			||||||
    assert!(sal_rhai::register_crypto_module(&mut engine).is_ok());
 | 
					    // assert!(sal_rhai::register_crypto_module(&mut engine).is_ok()); // Temporarily disabled
 | 
				
			||||||
    assert!(sal_rhai::register_redisclient_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_redisclient_module(&mut engine).is_ok());
 | 
				
			||||||
    assert!(sal_rhai::register_postgresclient_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_postgresclient_module(&mut engine).is_ok());
 | 
				
			||||||
    assert!(sal_rhai::register_mycelium_module(&mut engine).is_ok());
 | 
					    assert!(sal_rhai::register_mycelium_module(&mut engine).is_ok());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,45 +3,28 @@ name = "sal-vault"
 | 
				
			|||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
authors = ["PlanetFirst <info@incubaid.com>"]
 | 
					authors = ["PlanetFirst <info@incubaid.com>"]
 | 
				
			||||||
description = "SAL Vault - Cryptographic functionality including key management, digital signatures, symmetric encryption, Ethereum wallets, and encrypted key-value store"
 | 
					description = "SAL Vault - Secure key management and cryptographic operations"
 | 
				
			||||||
repository = "https://git.threefold.info/herocode/sal"
 | 
					repository = "https://git.threefold.info/herocode/sal"
 | 
				
			||||||
license = "Apache-2.0"
 | 
					license = "Apache-2.0"
 | 
				
			||||||
 | 
					keywords = ["vault", "crypto", "keys", "security", "sal"]
 | 
				
			||||||
 | 
					categories = ["cryptography", "api-bindings"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[features]
 | 
				
			||||||
 | 
					# native = ["kv/native"]
 | 
				
			||||||
 | 
					# wasm = ["kv/web"]
 | 
				
			||||||
 | 
					# Features temporarily disabled due to external dependency issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
# Core cryptographic dependencies
 | 
					getrandom = { version = "0.3.3", features = ["wasm_js"] }
 | 
				
			||||||
 | 
					rand = "0.9.1"
 | 
				
			||||||
 | 
					# We need to pull v0.2.x to enable the "js" feature for wasm32 builds
 | 
				
			||||||
 | 
					getrandom_old = { package = "getrandom", version = "0.2.16", features = ["js"] }
 | 
				
			||||||
 | 
					serde = { version = "1.0.219", features = ["derive"] }
 | 
				
			||||||
 | 
					serde_json = "1.0.140"
 | 
				
			||||||
chacha20poly1305 = "0.10.1"
 | 
					chacha20poly1305 = "0.10.1"
 | 
				
			||||||
k256 = { version = "0.13.4", features = ["ecdsa", "ecdh"] }
 | 
					k256 = { version = "0.13.4", features = ["ecdh"] }
 | 
				
			||||||
sha2 = "0.10.7"
 | 
					sha2 = "0.10.9"
 | 
				
			||||||
rand = "0.8.5"
 | 
					# kv = { git = "https://git.ourworld.tf/samehabouelsaad/sal-modular", package = "kvstore", rev = "9dce815daa" }
 | 
				
			||||||
 | 
					# Temporarily disabled due to broken external dependencies
 | 
				
			||||||
# Ethereum dependencies
 | 
					bincode = { version = "2.0.1", features = ["serde"] }
 | 
				
			||||||
ethers = { version = "2.0.7", features = ["legacy"] }
 | 
					pbkdf2 = "0.12.2"
 | 
				
			||||||
hex = "0.4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Serialization and data handling
 | 
					 | 
				
			||||||
serde = { version = "1.0", features = ["derive"] }
 | 
					 | 
				
			||||||
serde_json = "1.0"
 | 
					 | 
				
			||||||
base64 = "0.22.1"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Error handling
 | 
					 | 
				
			||||||
thiserror = "2.0.12"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Async runtime and utilities
 | 
					 | 
				
			||||||
tokio = { version = "1.45.0", features = ["full"] }
 | 
					 | 
				
			||||||
once_cell = "1.18.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# File system utilities
 | 
					 | 
				
			||||||
dirs = "6.0.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Rhai scripting support
 | 
					 | 
				
			||||||
rhai = { version = "1.12.0", features = ["sync"] }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# UUID generation
 | 
					 | 
				
			||||||
uuid = { version = "1.16.0", features = ["v4"] }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Logging
 | 
					 | 
				
			||||||
log = "0.4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[dev-dependencies]
 | 
					 | 
				
			||||||
tempfile = "3.5"
 | 
					 | 
				
			||||||
tokio-test = "0.4.4"
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										218
									
								
								vault/README.md
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								vault/README.md
									
									
									
									
									
								
							@@ -1,166 +1,148 @@
 | 
				
			|||||||
# SAL Vault (`sal-vault`)
 | 
					# SAL Vault
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SAL Vault is a comprehensive cryptographic library that provides secure key management, digital signatures, symmetric encryption, Ethereum wallet functionality, and encrypted key-value storage.
 | 
					A secure, encrypted key-value store system for the System Abstraction Layer (SAL).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Installation
 | 
					## Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Add this to your `Cargo.toml`:
 | 
					SAL Vault provides a two-tiered encrypted storage system:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```toml
 | 
					1. **Vault**: A collection of encrypted keyspaces
 | 
				
			||||||
[dependencies]
 | 
					2. **KeySpace**: An individual encrypted key-value store within a vault
 | 
				
			||||||
sal-vault = "0.1.0"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Features
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Core Cryptographic Operations
 | 
					- **Secure Storage**: ChaCha20Poly1305 encryption for all data
 | 
				
			||||||
- **Symmetric Encryption**: ChaCha20Poly1305 AEAD cipher for secure data encryption
 | 
					- **Password-Based Encryption**: Keyspaces are encrypted using password-derived keys
 | 
				
			||||||
- **Key Derivation**: PBKDF2-based key derivation from passwords
 | 
					- **Cross-Platform**: Works on both native and WASM targets
 | 
				
			||||||
- **Digital Signatures**: ECDSA signing and verification using secp256k1 curves
 | 
					- **Async API**: Fully asynchronous operations
 | 
				
			||||||
- **Key Management**: Secure keypair generation and storage
 | 
					- **Type Safety**: Strong typing with comprehensive error handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Keyspace Management
 | 
					## Architecture
 | 
				
			||||||
- **Multiple Keyspaces**: Organize keys into separate, password-protected spaces
 | 
					 | 
				
			||||||
- **Session Management**: Secure session handling with automatic cleanup
 | 
					 | 
				
			||||||
- **Keypair Organization**: Named keypairs within keyspaces for easy management
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Ethereum Integration
 | 
					```
 | 
				
			||||||
- **Wallet Functionality**: Create and manage Ethereum wallets from keypairs
 | 
					Vault
 | 
				
			||||||
- **Transaction Signing**: Sign Ethereum transactions securely
 | 
					├── KeySpace 1 (encrypted with password A)
 | 
				
			||||||
- **Smart Contract Interaction**: Call read functions on smart contracts
 | 
					├── KeySpace 2 (encrypted with password B)
 | 
				
			||||||
- **Multi-Network Support**: Support for different Ethereum networks
 | 
					└── KeySpace N (encrypted with password N)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Key-Value Store
 | 
					Each keyspace is independently encrypted, allowing different access controls and security boundaries.
 | 
				
			||||||
- **Encrypted Storage**: Store key-value pairs with automatic encryption
 | 
					 | 
				
			||||||
- **Secure Persistence**: Data is encrypted before being written to disk
 | 
					 | 
				
			||||||
- **Type Safety**: Strongly typed storage and retrieval operations
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Rhai Scripting Integration
 | 
					 | 
				
			||||||
- **Complete API Exposure**: All vault functionality available in Rhai scripts
 | 
					 | 
				
			||||||
- **Session Management**: Script-accessible session and keyspace management
 | 
					 | 
				
			||||||
- **Cryptographic Operations**: Encryption, signing, and verification in scripts
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Usage
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Basic Cryptographic Operations
 | 
					### Creating a Vault
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```rust
 | 
					```rust
 | 
				
			||||||
use sal_vault::symmetric::implementation::{encrypt_symmetric, decrypt_symmetric, generate_symmetric_key};
 | 
					use sal_vault::{Vault, Error};
 | 
				
			||||||
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Generate a symmetric key
 | 
					#[tokio::main]
 | 
				
			||||||
let key = generate_symmetric_key();
 | 
					async fn main() -> Result<(), Error> {
 | 
				
			||||||
 | 
					    // Create a new vault at the specified path
 | 
				
			||||||
 | 
					    let vault = Vault::new(Path::new("./my_vault")).await?;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
// Encrypt data
 | 
					    // Open an encrypted keyspace
 | 
				
			||||||
let message = b"Hello, World!";
 | 
					    let keyspace = vault.open_keyspace("user_data", "secure_password").await?;
 | 
				
			||||||
let encrypted = encrypt_symmetric(&key, message)?;
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
// Decrypt data
 | 
					    // Use the keyspace for encrypted storage
 | 
				
			||||||
let decrypted = decrypt_symmetric(&key, &encrypted)?;
 | 
					    // (KeySpace API documentation coming soon)
 | 
				
			||||||
```
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
### Keyspace and Keypair Management
 | 
					    Ok(())
 | 
				
			||||||
 | 
					 | 
				
			||||||
```rust
 | 
					 | 
				
			||||||
use sal_vault::keyspace::{KeySpace, KeyPair};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Create a new keyspace
 | 
					 | 
				
			||||||
let mut keyspace = KeySpace::new("my_keyspace");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Add a keypair
 | 
					 | 
				
			||||||
keyspace.add_keypair("main_key")?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Sign data
 | 
					 | 
				
			||||||
if let Some(keypair) = keyspace.keypairs.get("main_key") {
 | 
					 | 
				
			||||||
    let message = b"Important message";
 | 
					 | 
				
			||||||
    let signature = keypair.sign(message);
 | 
					 | 
				
			||||||
    let is_valid = keypair.verify(message, &signature)?;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Ethereum Wallet Operations
 | 
					### WASM Support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The vault also supports WASM targets with browser-compatible storage:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```rust
 | 
					```rust
 | 
				
			||||||
use sal_vault::ethereum::wallet::EthereumWallet;
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
use sal_vault::ethereum::networks::NetworkConfig;
 | 
					async fn wasm_example() -> Result<(), Error> {
 | 
				
			||||||
 | 
					    let vault = Vault::new().await?; // No path needed for WASM
 | 
				
			||||||
// Create wallet from keypair
 | 
					    let keyspace = vault.open_keyspace("session_data", "password").await?;
 | 
				
			||||||
let network = NetworkConfig::mainnet();
 | 
					    Ok(())
 | 
				
			||||||
let wallet = EthereumWallet::from_keypair(&keypair, network)?;
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get wallet address
 | 
					 | 
				
			||||||
let address = wallet.address();
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Rhai Scripting
 | 
					## Security
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```rhai
 | 
					### Encryption
 | 
				
			||||||
// Create and manage keyspaces
 | 
					 | 
				
			||||||
create_key_space("personal", "secure_password");
 | 
					 | 
				
			||||||
select_keyspace("personal");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create and use keypairs
 | 
					- **Algorithm**: ChaCha20Poly1305 (AEAD)
 | 
				
			||||||
create_keypair("signing_key");
 | 
					- **Key Derivation**: PBKDF2 with secure parameters
 | 
				
			||||||
select_keypair("signing_key");
 | 
					- **Nonce Generation**: Cryptographically secure random nonces
 | 
				
			||||||
 | 
					- **Authentication**: Built-in authentication prevents tampering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Sign and verify data
 | 
					### Best Practices
 | 
				
			||||||
let message = "Important document";
 | 
					 | 
				
			||||||
let signature = sign(message);
 | 
					 | 
				
			||||||
let is_valid = verify(message, signature);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Symmetric encryption
 | 
					1. **Strong Passwords**: Use strong, unique passwords for each keyspace
 | 
				
			||||||
let key = generate_key();
 | 
					2. **Secure Storage**: Store vault files in secure locations
 | 
				
			||||||
let encrypted = encrypt(key, "secret data");
 | 
					3. **Access Control**: Limit filesystem access to vault directories
 | 
				
			||||||
let decrypted = decrypt(key, encrypted);
 | 
					4. **Backup Strategy**: Implement secure backup procedures
 | 
				
			||||||
```
 | 
					5. **Key Rotation**: Periodically change keyspace passwords
 | 
				
			||||||
 | 
					 | 
				
			||||||
## Security Features
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- **Memory Safety**: All sensitive data is handled securely in memory
 | 
					 | 
				
			||||||
- **Secure Random Generation**: Uses cryptographically secure random number generation
 | 
					 | 
				
			||||||
- **Password-Based Encryption**: Keyspaces are protected with password-derived keys
 | 
					 | 
				
			||||||
- **Session Isolation**: Each session maintains separate state and security context
 | 
					 | 
				
			||||||
- **Constant-Time Operations**: Critical operations use constant-time implementations
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Error Handling
 | 
					## Error Handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The library provides comprehensive error handling through the `CryptoError` enum:
 | 
					The vault uses a comprehensive error system:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```rust
 | 
					```rust
 | 
				
			||||||
use sal_vault::error::CryptoError;
 | 
					use sal_vault::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
match some_crypto_operation() {
 | 
					match vault.open_keyspace("test", "password").await {
 | 
				
			||||||
    Ok(result) => println!("Success: {:?}", result),
 | 
					    Ok(keyspace) => {
 | 
				
			||||||
    Err(CryptoError::InvalidKeyLength) => println!("Invalid key length provided"),
 | 
					        // Success - use the keyspace
 | 
				
			||||||
    Err(CryptoError::EncryptionFailed(msg)) => println!("Encryption failed: {}", msg),
 | 
					    }
 | 
				
			||||||
    Err(CryptoError::KeypairNotFound(name)) => println!("Keypair '{}' not found", name),
 | 
					    Err(Error::IOError(io_err)) => {
 | 
				
			||||||
    Err(e) => println!("Other error: {}", e),
 | 
					        // Handle I/O errors (file system issues)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Err(Error::CryptoError(crypto_err)) => {
 | 
				
			||||||
 | 
					        // Handle cryptographic errors (wrong password, corruption)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Err(other) => {
 | 
				
			||||||
 | 
					        // Handle other errors
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Testing
 | 
					## Migration from Previous Implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The package includes comprehensive tests covering all functionality:
 | 
					This vault implementation replaces the previous Ethereum-focused vault. Key differences:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					### What's New
 | 
				
			||||||
# Run all tests
 | 
					- ✅ Simpler, more focused API
 | 
				
			||||||
cargo test
 | 
					- ✅ Better cross-platform support
 | 
				
			||||||
 | 
					- ✅ Improved security model
 | 
				
			||||||
 | 
					- ✅ Cleaner error handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Run specific test categories
 | 
					### What's Changed
 | 
				
			||||||
cargo test crypto_tests
 | 
					- ❌ No Ethereum wallet functionality
 | 
				
			||||||
cargo test rhai_integration_tests
 | 
					- ❌ No smart contract integration
 | 
				
			||||||
```
 | 
					- ❌ No built-in signing operations
 | 
				
			||||||
 | 
					- ⏳ Rhai scripting integration (coming soon)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Note**: The Rhai integration tests use global state and are automatically serialized using a test mutex to prevent interference between parallel test runs.
 | 
					### Archived Implementation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Dependencies
 | 
					The previous implementation is preserved in `_archive/` for reference and potential feature extraction.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `chacha20poly1305`: Symmetric encryption
 | 
					## Development Status
 | 
				
			||||||
- `k256`: Elliptic curve cryptography
 | 
					
 | 
				
			||||||
- `ethers`: Ethereum functionality
 | 
					- ✅ **Core Vault**: Complete and functional
 | 
				
			||||||
- `serde`: Serialization support
 | 
					- ✅ **KeySpace Operations**: Basic implementation ready
 | 
				
			||||||
- `rhai`: Scripting integration
 | 
					- ✅ **Encryption**: Secure ChaCha20Poly1305 implementation
 | 
				
			||||||
- `tokio`: Async runtime support
 | 
					- ⏳ **Rhai Integration**: In development
 | 
				
			||||||
 | 
					- ⏳ **Extended API**: Additional convenience methods planned
 | 
				
			||||||
 | 
					- ⏳ **Documentation**: API docs being completed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When contributing to the vault module:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Maintain security-first approach
 | 
				
			||||||
 | 
					2. Ensure cross-platform compatibility
 | 
				
			||||||
 | 
					3. Add comprehensive tests for new features
 | 
				
			||||||
 | 
					4. Update documentation for API changes
 | 
				
			||||||
 | 
					5. Consider WASM compatibility for new features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## License
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Licensed under the Apache License, Version 2.0.
 | 
					This module is part of the SAL project and follows the same licensing terms.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								vault/_archive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								vault/_archive/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "sal-vault"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					authors = ["PlanetFirst <info@incubaid.com>"]
 | 
				
			||||||
 | 
					description = "SAL Vault - Cryptographic functionality including key management, digital signatures, symmetric encryption, Ethereum wallets, and encrypted key-value store"
 | 
				
			||||||
 | 
					repository = "https://git.threefold.info/herocode/sal"
 | 
				
			||||||
 | 
					license = "Apache-2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					# Core cryptographic dependencies
 | 
				
			||||||
 | 
					chacha20poly1305 = "0.10.1"
 | 
				
			||||||
 | 
					k256 = { version = "0.13.4", features = ["ecdsa", "ecdh"] }
 | 
				
			||||||
 | 
					sha2 = "0.10.7"
 | 
				
			||||||
 | 
					rand = "0.8.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ethereum dependencies
 | 
				
			||||||
 | 
					ethers = { version = "2.0.7", features = ["legacy"] }
 | 
				
			||||||
 | 
					hex = "0.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Serialization and data handling
 | 
				
			||||||
 | 
					serde = { version = "1.0", features = ["derive"] }
 | 
				
			||||||
 | 
					serde_json = "1.0"
 | 
				
			||||||
 | 
					base64 = "0.22.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Error handling
 | 
				
			||||||
 | 
					thiserror = "2.0.12"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Async runtime and utilities
 | 
				
			||||||
 | 
					tokio = { version = "1.45.0", features = ["full"] }
 | 
				
			||||||
 | 
					once_cell = "1.18.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# File system utilities
 | 
				
			||||||
 | 
					dirs = "6.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rhai scripting support
 | 
				
			||||||
 | 
					rhai = { version = "1.12.0", features = ["sync"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# UUID generation
 | 
				
			||||||
 | 
					uuid = { version = "1.16.0", features = ["v4"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Logging
 | 
				
			||||||
 | 
					log = "0.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					tempfile = "3.5"
 | 
				
			||||||
 | 
					tokio-test = "0.4.4"
 | 
				
			||||||
							
								
								
									
										166
									
								
								vault/_archive/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vault/_archive/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					# SAL Vault (`sal-vault`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SAL Vault is a comprehensive cryptographic library that provides secure key management, digital signatures, symmetric encryption, Ethereum wallet functionality, and encrypted key-value storage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add this to your `Cargo.toml`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```toml
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					sal-vault = "0.1.0"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Core Cryptographic Operations
 | 
				
			||||||
 | 
					- **Symmetric Encryption**: ChaCha20Poly1305 AEAD cipher for secure data encryption
 | 
				
			||||||
 | 
					- **Key Derivation**: PBKDF2-based key derivation from passwords
 | 
				
			||||||
 | 
					- **Digital Signatures**: ECDSA signing and verification using secp256k1 curves
 | 
				
			||||||
 | 
					- **Key Management**: Secure keypair generation and storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Keyspace Management
 | 
				
			||||||
 | 
					- **Multiple Keyspaces**: Organize keys into separate, password-protected spaces
 | 
				
			||||||
 | 
					- **Session Management**: Secure session handling with automatic cleanup
 | 
				
			||||||
 | 
					- **Keypair Organization**: Named keypairs within keyspaces for easy management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Ethereum Integration
 | 
				
			||||||
 | 
					- **Wallet Functionality**: Create and manage Ethereum wallets from keypairs
 | 
				
			||||||
 | 
					- **Transaction Signing**: Sign Ethereum transactions securely
 | 
				
			||||||
 | 
					- **Smart Contract Interaction**: Call read functions on smart contracts
 | 
				
			||||||
 | 
					- **Multi-Network Support**: Support for different Ethereum networks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Key-Value Store
 | 
				
			||||||
 | 
					- **Encrypted Storage**: Store key-value pairs with automatic encryption
 | 
				
			||||||
 | 
					- **Secure Persistence**: Data is encrypted before being written to disk
 | 
				
			||||||
 | 
					- **Type Safety**: Strongly typed storage and retrieval operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Rhai Scripting Integration
 | 
				
			||||||
 | 
					- **Complete API Exposure**: All vault functionality available in Rhai scripts
 | 
				
			||||||
 | 
					- **Session Management**: Script-accessible session and keyspace management
 | 
				
			||||||
 | 
					- **Cryptographic Operations**: Encryption, signing, and verification in scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Basic Cryptographic Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use sal_vault::symmetric::implementation::{encrypt_symmetric, decrypt_symmetric, generate_symmetric_key};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Generate a symmetric key
 | 
				
			||||||
 | 
					let key = generate_symmetric_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Encrypt data
 | 
				
			||||||
 | 
					let message = b"Hello, World!";
 | 
				
			||||||
 | 
					let encrypted = encrypt_symmetric(&key, message)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decrypt data
 | 
				
			||||||
 | 
					let decrypted = decrypt_symmetric(&key, &encrypted)?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Keyspace and Keypair Management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use sal_vault::keyspace::{KeySpace, KeyPair};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create a new keyspace
 | 
				
			||||||
 | 
					let mut keyspace = KeySpace::new("my_keyspace");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add a keypair
 | 
				
			||||||
 | 
					keyspace.add_keypair("main_key")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sign data
 | 
				
			||||||
 | 
					if let Some(keypair) = keyspace.keypairs.get("main_key") {
 | 
				
			||||||
 | 
					    let message = b"Important message";
 | 
				
			||||||
 | 
					    let signature = keypair.sign(message);
 | 
				
			||||||
 | 
					    let is_valid = keypair.verify(message, &signature)?;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Ethereum Wallet Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use sal_vault::ethereum::wallet::EthereumWallet;
 | 
				
			||||||
 | 
					use sal_vault::ethereum::networks::NetworkConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create wallet from keypair
 | 
				
			||||||
 | 
					let network = NetworkConfig::mainnet();
 | 
				
			||||||
 | 
					let wallet = EthereumWallet::from_keypair(&keypair, network)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get wallet address
 | 
				
			||||||
 | 
					let address = wallet.address();
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Rhai Scripting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rhai
 | 
				
			||||||
 | 
					// Create and manage keyspaces
 | 
				
			||||||
 | 
					create_key_space("personal", "secure_password");
 | 
				
			||||||
 | 
					select_keyspace("personal");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create and use keypairs
 | 
				
			||||||
 | 
					create_keypair("signing_key");
 | 
				
			||||||
 | 
					select_keypair("signing_key");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sign and verify data
 | 
				
			||||||
 | 
					let message = "Important document";
 | 
				
			||||||
 | 
					let signature = sign(message);
 | 
				
			||||||
 | 
					let is_valid = verify(message, signature);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Symmetric encryption
 | 
				
			||||||
 | 
					let key = generate_key();
 | 
				
			||||||
 | 
					let encrypted = encrypt(key, "secret data");
 | 
				
			||||||
 | 
					let decrypted = decrypt(key, encrypted);
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Security Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **Memory Safety**: All sensitive data is handled securely in memory
 | 
				
			||||||
 | 
					- **Secure Random Generation**: Uses cryptographically secure random number generation
 | 
				
			||||||
 | 
					- **Password-Based Encryption**: Keyspaces are protected with password-derived keys
 | 
				
			||||||
 | 
					- **Session Isolation**: Each session maintains separate state and security context
 | 
				
			||||||
 | 
					- **Constant-Time Operations**: Critical operations use constant-time implementations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Error Handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The library provides comprehensive error handling through the `CryptoError` enum:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					use sal_vault::error::CryptoError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					match some_crypto_operation() {
 | 
				
			||||||
 | 
					    Ok(result) => println!("Success: {:?}", result),
 | 
				
			||||||
 | 
					    Err(CryptoError::InvalidKeyLength) => println!("Invalid key length provided"),
 | 
				
			||||||
 | 
					    Err(CryptoError::EncryptionFailed(msg)) => println!("Encryption failed: {}", msg),
 | 
				
			||||||
 | 
					    Err(CryptoError::KeypairNotFound(name)) => println!("Keypair '{}' not found", name),
 | 
				
			||||||
 | 
					    Err(e) => println!("Other error: {}", e),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The package includes comprehensive tests covering all functionality:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Run all tests
 | 
				
			||||||
 | 
					cargo test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run specific test categories
 | 
				
			||||||
 | 
					cargo test crypto_tests
 | 
				
			||||||
 | 
					cargo test rhai_integration_tests
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Note**: The Rhai integration tests use global state and are automatically serialized using a test mutex to prevent interference between parallel test runs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `chacha20poly1305`: Symmetric encryption
 | 
				
			||||||
 | 
					- `k256`: Elliptic curve cryptography
 | 
				
			||||||
 | 
					- `ethers`: Ethereum functionality
 | 
				
			||||||
 | 
					- `serde`: Serialization support
 | 
				
			||||||
 | 
					- `rhai`: Scripting integration
 | 
				
			||||||
 | 
					- `tokio`: Async runtime support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0.
 | 
				
			||||||
							
								
								
									
										160
									
								
								vault/_archive/src/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vault/_archive/src/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					# Hero Vault Cryptography Module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Hero Vault module provides comprehensive cryptographic functionality for the SAL project, including key management, digital signatures, symmetric encryption, Ethereum wallet operations, and a secure key-value store.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Module Structure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Hero Vault module is organized into several submodules:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `error.rs` - Error types for cryptographic operations
 | 
				
			||||||
 | 
					- `keypair/` - ECDSA keypair management functionality
 | 
				
			||||||
 | 
					- `symmetric/` - Symmetric encryption using ChaCha20Poly1305
 | 
				
			||||||
 | 
					- `ethereum/` - Ethereum wallet and smart contract functionality
 | 
				
			||||||
 | 
					- `kvs/` - Encrypted key-value store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Key Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Key Space Management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides functionality for creating, loading, and managing key spaces. A key space is a secure container for cryptographic keys, which can be encrypted and stored on disk.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Create a new key space
 | 
				
			||||||
 | 
					let space = KeySpace::new("my_space", "secure_password")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Save the key space to disk
 | 
				
			||||||
 | 
					space.save()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Load a key space from disk
 | 
				
			||||||
 | 
					let loaded_space = KeySpace::load("my_space", "secure_password")?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Keypair Management
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides functionality for creating, selecting, and using ECDSA keypairs for digital signatures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Create a new keypair in the active key space
 | 
				
			||||||
 | 
					let keypair = space.create_keypair("my_keypair", "secure_password")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Select a keypair for use
 | 
				
			||||||
 | 
					space.select_keypair("my_keypair")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// List all keypairs in the active key space
 | 
				
			||||||
 | 
					let keypairs = space.list_keypairs()?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Digital Signatures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides functionality for signing and verifying messages using ECDSA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Sign a message using the selected keypair
 | 
				
			||||||
 | 
					let signature = space.sign("This is a message to sign")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Verify a signature
 | 
				
			||||||
 | 
					let is_valid = space.verify("This is a message to sign", &signature)?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Symmetric Encryption
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides functionality for symmetric encryption using ChaCha20Poly1305.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Generate a new symmetric key
 | 
				
			||||||
 | 
					let key = space.generate_key()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Encrypt a message
 | 
				
			||||||
 | 
					let encrypted = space.encrypt(&key, "This is a secret message")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decrypt a message
 | 
				
			||||||
 | 
					let decrypted = space.decrypt(&key, &encrypted)?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Ethereum Wallet Functionality
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides comprehensive Ethereum wallet functionality, including:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Creating and managing wallets for different networks
 | 
				
			||||||
 | 
					- Sending ETH transactions
 | 
				
			||||||
 | 
					- Checking balances
 | 
				
			||||||
 | 
					- Interacting with smart contracts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Create an Ethereum wallet
 | 
				
			||||||
 | 
					let wallet = EthereumWallet::new(keypair)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get the wallet address
 | 
				
			||||||
 | 
					let address = wallet.get_address()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send ETH
 | 
				
			||||||
 | 
					let tx_hash = wallet.send_eth("0x1234...", "1000000000000000")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check balance
 | 
				
			||||||
 | 
					let balance = wallet.get_balance("0x1234...")?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Smart Contract Interactions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides functionality for interacting with smart contracts on EVM-based blockchains.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Load a contract ABI
 | 
				
			||||||
 | 
					let contract = Contract::new(provider, "0x1234...", abi)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Call a read-only function
 | 
				
			||||||
 | 
					let result = contract.call_read("balanceOf", vec!["0x5678..."])?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Call a write function
 | 
				
			||||||
 | 
					let tx_hash = contract.call_write("transfer", vec!["0x5678...", "1000"])?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Key-Value Store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module provides an encrypted key-value store for securely storing sensitive data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```rust
 | 
				
			||||||
 | 
					// Create a new store
 | 
				
			||||||
 | 
					let store = KvStore::new("my_store", "secure_password")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set a value
 | 
				
			||||||
 | 
					store.set("api_key", "secret_api_key")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get a value
 | 
				
			||||||
 | 
					let api_key = store.get("api_key")?;
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Error Handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module uses a comprehensive error type (`CryptoError`) for handling errors that can occur during cryptographic operations:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `InvalidKeyLength` - Invalid key length
 | 
				
			||||||
 | 
					- `EncryptionFailed` - Encryption failed
 | 
				
			||||||
 | 
					- `DecryptionFailed` - Decryption failed
 | 
				
			||||||
 | 
					- `SignatureFormatError` - Signature format error
 | 
				
			||||||
 | 
					- `KeypairAlreadyExists` - Keypair already exists
 | 
				
			||||||
 | 
					- `KeypairNotFound` - Keypair not found
 | 
				
			||||||
 | 
					- `NoActiveSpace` - No active key space
 | 
				
			||||||
 | 
					- `NoKeypairSelected` - No keypair selected
 | 
				
			||||||
 | 
					- `SerializationError` - Serialization error
 | 
				
			||||||
 | 
					- `InvalidAddress` - Invalid address format
 | 
				
			||||||
 | 
					- `ContractError` - Smart contract error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Ethereum Networks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module supports multiple Ethereum networks, including:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Gnosis Chain
 | 
				
			||||||
 | 
					- Peaq Network
 | 
				
			||||||
 | 
					- Agung Network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Security Considerations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Key spaces are encrypted with ChaCha20Poly1305 using a key derived from the provided password
 | 
				
			||||||
 | 
					- Private keys are never stored in plaintext
 | 
				
			||||||
 | 
					- The module uses secure random number generation for key creation
 | 
				
			||||||
 | 
					- All cryptographic operations use well-established libraries and algorithms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For examples of how to use the Hero Vault module, see the `examples/hero_vault` directory.
 | 
				
			||||||
							
								
								
									
										53
									
								
								vault/_archive/src/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vault/_archive/src/error.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					//! Error types for cryptographic operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use thiserror::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Errors that can occur during cryptographic operations
 | 
				
			||||||
 | 
					#[derive(Error, Debug)]
 | 
				
			||||||
 | 
					pub enum CryptoError {
 | 
				
			||||||
 | 
					    /// Invalid key length
 | 
				
			||||||
 | 
					    #[error("Invalid key length")]
 | 
				
			||||||
 | 
					    InvalidKeyLength,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Encryption failed
 | 
				
			||||||
 | 
					    #[error("Encryption failed: {0}")]
 | 
				
			||||||
 | 
					    EncryptionFailed(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Decryption failed
 | 
				
			||||||
 | 
					    #[error("Decryption failed: {0}")]
 | 
				
			||||||
 | 
					    DecryptionFailed(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Signature format error
 | 
				
			||||||
 | 
					    #[error("Signature format error: {0}")]
 | 
				
			||||||
 | 
					    SignatureFormatError(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Keypair already exists
 | 
				
			||||||
 | 
					    #[error("Keypair already exists: {0}")]
 | 
				
			||||||
 | 
					    KeypairAlreadyExists(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Keypair not found
 | 
				
			||||||
 | 
					    #[error("Keypair not found: {0}")]
 | 
				
			||||||
 | 
					    KeypairNotFound(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// No active key space
 | 
				
			||||||
 | 
					    #[error("No active key space")]
 | 
				
			||||||
 | 
					    NoActiveSpace,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// No keypair selected
 | 
				
			||||||
 | 
					    #[error("No keypair selected")]
 | 
				
			||||||
 | 
					    NoKeypairSelected,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Serialization error
 | 
				
			||||||
 | 
					    #[error("Serialization error: {0}")]
 | 
				
			||||||
 | 
					    SerializationError(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Invalid address format
 | 
				
			||||||
 | 
					    #[error("Invalid address format: {0}")]
 | 
				
			||||||
 | 
					    InvalidAddress(String),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Smart contract error
 | 
				
			||||||
 | 
					    #[error("Smart contract error: {0}")]
 | 
				
			||||||
 | 
					    ContractError(String),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Note: Error conversion to main SAL crate will be handled at the integration level
 | 
				
			||||||
							
								
								
									
										23
									
								
								vault/_archive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vault/_archive/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					//! SAL Vault: Cryptographic functionality for SAL
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! This package provides cryptographic operations including:
 | 
				
			||||||
 | 
					//! - Key space management (creation, loading, encryption, decryption)
 | 
				
			||||||
 | 
					//! - Key pair management (ECDSA)
 | 
				
			||||||
 | 
					//! - Digital signatures (signing and verification)
 | 
				
			||||||
 | 
					//! - Symmetric encryption (ChaCha20Poly1305)
 | 
				
			||||||
 | 
					//! - Ethereum wallet functionality
 | 
				
			||||||
 | 
					//! - Key-value store with encryption
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod error;
 | 
				
			||||||
 | 
					pub mod ethereum;
 | 
				
			||||||
 | 
					pub mod keyspace;
 | 
				
			||||||
 | 
					pub mod kvs;
 | 
				
			||||||
 | 
					pub mod symmetric;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rhai integration module
 | 
				
			||||||
 | 
					pub mod rhai;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Re-export modules
 | 
				
			||||||
 | 
					// Re-export common types for convenience
 | 
				
			||||||
 | 
					pub use error::CryptoError;
 | 
				
			||||||
 | 
					pub use keyspace::{KeyPair, KeySpace};
 | 
				
			||||||
@@ -1,53 +1,109 @@
 | 
				
			|||||||
//! Error types for cryptographic operations
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					/// Errors encountered while using the vault
 | 
				
			||||||
use thiserror::Error;
 | 
					pub enum Error {
 | 
				
			||||||
 | 
					    /// An error during cryptographic operations
 | 
				
			||||||
/// Errors that can occur during cryptographic operations
 | 
					    Crypto(CryptoError),
 | 
				
			||||||
#[derive(Error, Debug)]
 | 
					    /// An error while performing an I/O operation
 | 
				
			||||||
pub enum CryptoError {
 | 
					    IOError(std::io::Error),
 | 
				
			||||||
    /// Invalid key length
 | 
					    /// A corrupt keyspace is returned if a keyspace can't be decrypted
 | 
				
			||||||
    #[error("Invalid key length")]
 | 
					    CorruptKeyspace,
 | 
				
			||||||
    InvalidKeyLength,
 | 
					    /// An error in the used key value store (temporarily disabled)
 | 
				
			||||||
 | 
					    // KV(kv::error::KVError),
 | 
				
			||||||
    /// Encryption failed
 | 
					    /// An error while encoding/decoding the keyspace.
 | 
				
			||||||
    #[error("Encryption failed: {0}")]
 | 
					    Coding,
 | 
				
			||||||
    EncryptionFailed(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Decryption failed
 | 
					 | 
				
			||||||
    #[error("Decryption failed: {0}")]
 | 
					 | 
				
			||||||
    DecryptionFailed(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Signature format error
 | 
					 | 
				
			||||||
    #[error("Signature format error: {0}")]
 | 
					 | 
				
			||||||
    SignatureFormatError(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Keypair already exists
 | 
					 | 
				
			||||||
    #[error("Keypair already exists: {0}")]
 | 
					 | 
				
			||||||
    KeypairAlreadyExists(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Keypair not found
 | 
					 | 
				
			||||||
    #[error("Keypair not found: {0}")]
 | 
					 | 
				
			||||||
    KeypairNotFound(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// No active key space
 | 
					 | 
				
			||||||
    #[error("No active key space")]
 | 
					 | 
				
			||||||
    NoActiveSpace,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// No keypair selected
 | 
					 | 
				
			||||||
    #[error("No keypair selected")]
 | 
					 | 
				
			||||||
    NoKeypairSelected,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Serialization error
 | 
					 | 
				
			||||||
    #[error("Serialization error: {0}")]
 | 
					 | 
				
			||||||
    SerializationError(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Invalid address format
 | 
					 | 
				
			||||||
    #[error("Invalid address format: {0}")]
 | 
					 | 
				
			||||||
    InvalidAddress(String),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Smart contract error
 | 
					 | 
				
			||||||
    #[error("Smart contract error: {0}")]
 | 
					 | 
				
			||||||
    ContractError(String),
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Note: Error conversion to main SAL crate will be handled at the integration level
 | 
					impl core::fmt::Display for Error {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Error::Crypto(e) => f.write_fmt(format_args!("crypto: {e}")),
 | 
				
			||||||
 | 
					            Error::IOError(e) => f.write_fmt(format_args!("io: {e}")),
 | 
				
			||||||
 | 
					            Error::CorruptKeyspace => f.write_str("corrupt keyspace"),
 | 
				
			||||||
 | 
					            // Error::KV(e) => f.write_fmt(format_args!("kv: {e}")),
 | 
				
			||||||
 | 
					            Error::Coding => f.write_str("keyspace coding failed"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::error::Error for Error {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					/// Errors generated by the vault or keys.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// These errors are intentionally vague to avoid issues such as padding oracles.
 | 
				
			||||||
 | 
					pub enum CryptoError {
 | 
				
			||||||
 | 
					    /// Key size is not valid for this type of key
 | 
				
			||||||
 | 
					    InvalidKeySize,
 | 
				
			||||||
 | 
					    /// Something went wrong while trying to encrypt data
 | 
				
			||||||
 | 
					    EncryptionFailed,
 | 
				
			||||||
 | 
					    /// Something went wrong while trying to decrypt data
 | 
				
			||||||
 | 
					    DecryptionFailed,
 | 
				
			||||||
 | 
					    /// Something went wrong while trying to sign a message
 | 
				
			||||||
 | 
					    SigningError,
 | 
				
			||||||
 | 
					    /// The signature is invalid for this message and public key
 | 
				
			||||||
 | 
					    SignatureFailed,
 | 
				
			||||||
 | 
					    /// The signature does not have the expected size
 | 
				
			||||||
 | 
					    InvalidSignatureSize,
 | 
				
			||||||
 | 
					    /// Trying to load a key which is not the expected format,
 | 
				
			||||||
 | 
					    InvalidKey,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::fmt::Display for CryptoError {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            CryptoError::InvalidKeySize => f.write_str("provided key is not the correct size"),
 | 
				
			||||||
 | 
					            CryptoError::EncryptionFailed => f.write_str("encryption failure"),
 | 
				
			||||||
 | 
					            CryptoError::DecryptionFailed => f.write_str("decryption failure"),
 | 
				
			||||||
 | 
					            CryptoError::SigningError => f.write_str("signature generation failure"),
 | 
				
			||||||
 | 
					            CryptoError::SignatureFailed => f.write_str("signature verification failure"),
 | 
				
			||||||
 | 
					            CryptoError::InvalidSignatureSize => {
 | 
				
			||||||
 | 
					                f.write_str("provided signature does not have the expected size")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            CryptoError::InvalidKey => f.write_str("the provided bytes are not a valid key"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl core::error::Error for CryptoError {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<CryptoError> for Error {
 | 
				
			||||||
 | 
					    fn from(value: CryptoError) -> Self {
 | 
				
			||||||
 | 
					        Self::Crypto(value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<std::io::Error> for Error {
 | 
				
			||||||
 | 
					    fn from(value: std::io::Error) -> Self {
 | 
				
			||||||
 | 
					        Self::IOError(value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// impl From<kv::error::KVError> for Error {
 | 
				
			||||||
 | 
					//     fn from(value: kv::error::KVError) -> Self {
 | 
				
			||||||
 | 
					//         Self::KV(value)
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<bincode::error::DecodeError> for Error {
 | 
				
			||||||
 | 
					    fn from(_: bincode::error::DecodeError) -> Self {
 | 
				
			||||||
 | 
					        Self::Coding
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<bincode::error::EncodeError> for Error {
 | 
				
			||||||
 | 
					    fn from(_: bincode::error::EncodeError) -> Self {
 | 
				
			||||||
 | 
					        Self::Coding
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<k256::ecdsa::Error> for CryptoError {
 | 
				
			||||||
 | 
					    fn from(_: k256::ecdsa::Error) -> Self {
 | 
				
			||||||
 | 
					        Self::InvalidKey
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<k256::elliptic_curve::Error> for CryptoError {
 | 
				
			||||||
 | 
					    fn from(_: k256::elliptic_curve::Error) -> Self {
 | 
				
			||||||
 | 
					        Self::InvalidKey
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								vault/src/key.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vault/src/key.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					use asymmetric::AsymmetricKeypair;
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use signature::SigningKeypair;
 | 
				
			||||||
 | 
					use symmetric::SymmetricKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod asymmetric;
 | 
				
			||||||
 | 
					pub mod signature;
 | 
				
			||||||
 | 
					pub mod symmetric;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
 | 
				
			||||||
 | 
					pub enum KeyType {
 | 
				
			||||||
 | 
					    /// The key can be used for symmetric key encryption
 | 
				
			||||||
 | 
					    Symmetric,
 | 
				
			||||||
 | 
					    /// The key can be used for asymmetric encryption
 | 
				
			||||||
 | 
					    Asymmetric,
 | 
				
			||||||
 | 
					    /// The key can be used for digital signatures
 | 
				
			||||||
 | 
					    Signature,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Key holds generic information about a key
 | 
				
			||||||
 | 
					#[derive(Clone, Deserialize, Serialize)]
 | 
				
			||||||
 | 
					pub struct Key {
 | 
				
			||||||
 | 
					    /// The mode of the key
 | 
				
			||||||
 | 
					    mode: KeyType,
 | 
				
			||||||
 | 
					    /// Raw bytes of the key
 | 
				
			||||||
 | 
					    raw_key: Vec<u8>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Key {
 | 
				
			||||||
 | 
					    /// Try to downcast this `Key` to a [`SymmetricKey`]
 | 
				
			||||||
 | 
					    pub fn as_symmetric(&self) -> Option<SymmetricKey> {
 | 
				
			||||||
 | 
					        if matches!(self.mode, KeyType::Symmetric) {
 | 
				
			||||||
 | 
					            SymmetricKey::from_bytes(&self.raw_key).ok()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Try to downcast this `Key` to an [`AsymmetricKeypair`]
 | 
				
			||||||
 | 
					    pub fn as_asymmetric(&self) -> Option<AsymmetricKeypair> {
 | 
				
			||||||
 | 
					        if matches!(self.mode, KeyType::Asymmetric) {
 | 
				
			||||||
 | 
					            AsymmetricKeypair::from_bytes(&self.raw_key).ok()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Try to downcast this `Key` to a [`SigningKeypair`]
 | 
				
			||||||
 | 
					    pub fn as_signing(&self) -> Option<SigningKeypair> {
 | 
				
			||||||
 | 
					        if matches!(self.mode, KeyType::Signature) {
 | 
				
			||||||
 | 
					            SigningKeypair::from_bytes(&self.raw_key).ok()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            None
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<SymmetricKey> for Key {
 | 
				
			||||||
 | 
					    fn from(value: SymmetricKey) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mode: KeyType::Symmetric,
 | 
				
			||||||
 | 
					            raw_key: Vec::from(value.as_raw_bytes()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<AsymmetricKeypair> for Key {
 | 
				
			||||||
 | 
					    fn from(value: AsymmetricKeypair) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mode: KeyType::Asymmetric,
 | 
				
			||||||
 | 
					            raw_key: value.as_raw_private_key(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<SigningKeypair> for Key {
 | 
				
			||||||
 | 
					    fn from(value: SigningKeypair) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            mode: KeyType::Signature,
 | 
				
			||||||
 | 
					            raw_key: value.as_raw_private_key(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										161
									
								
								vault/src/key/asymmetric.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vault/src/key/asymmetric.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					//! An implementation of asymmetric cryptography using SECP256k1 ECDH with ChaCha20Poly1305
 | 
				
			||||||
 | 
					//! for the actual encryption.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use k256::{SecretKey, ecdh::diffie_hellman, elliptic_curve::sec1::ToEncodedPoint};
 | 
				
			||||||
 | 
					use sha2::Sha256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{error::CryptoError, key::symmetric::SymmetricKey};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A keypair for use in asymmetric encryption operations.
 | 
				
			||||||
 | 
					pub struct AsymmetricKeypair {
 | 
				
			||||||
 | 
					    /// Private part of the key
 | 
				
			||||||
 | 
					    private: SecretKey,
 | 
				
			||||||
 | 
					    /// Public part of the key
 | 
				
			||||||
 | 
					    public: k256::PublicKey,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The public key part of an asymmetric keypair.
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct PublicKey(k256::PublicKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl AsymmetricKeypair {
 | 
				
			||||||
 | 
					    /// Generates a new random keypair
 | 
				
			||||||
 | 
					    pub fn new() -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        let mut raw_private = [0u8; 32];
 | 
				
			||||||
 | 
					        rand::fill(&mut raw_private);
 | 
				
			||||||
 | 
					        let sk = SecretKey::from_slice(&raw_private)
 | 
				
			||||||
 | 
					            .expect("Key is provided generated with fixed valid size");
 | 
				
			||||||
 | 
					        let pk = sk.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            private: sk,
 | 
				
			||||||
 | 
					            public: pk,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create a new key from existing bytes.
 | 
				
			||||||
 | 
					    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        if bytes.len() == 32 {
 | 
				
			||||||
 | 
					            let sk = SecretKey::from_slice(&bytes).expect("Key was checked to be a valid size");
 | 
				
			||||||
 | 
					            let pk = sk.public_key();
 | 
				
			||||||
 | 
					            Ok(Self {
 | 
				
			||||||
 | 
					                private: sk,
 | 
				
			||||||
 | 
					                public: pk,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(CryptoError::InvalidKeySize)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// View the raw bytes of the private key of this keypair.
 | 
				
			||||||
 | 
					    pub(crate) fn as_raw_private_key(&self) -> Vec<u8> {
 | 
				
			||||||
 | 
					        self.private.as_scalar_primitive().to_bytes().to_vec()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get the public part of this keypair.
 | 
				
			||||||
 | 
					    pub fn public_key(&self) -> PublicKey {
 | 
				
			||||||
 | 
					        PublicKey(self.public.clone())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Encrypt data for a receiver. First a shared secret is derived using the own private key and
 | 
				
			||||||
 | 
					    /// the receivers public key. Then, this shared secret is used for symmetric encryption of the
 | 
				
			||||||
 | 
					    /// plaintext. The receiver can decrypt this by generating the same shared secret, using his
 | 
				
			||||||
 | 
					    /// own private key and our public key.
 | 
				
			||||||
 | 
					    pub fn encrypt(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        remote_key: &PublicKey,
 | 
				
			||||||
 | 
					        plaintext: &[u8],
 | 
				
			||||||
 | 
					    ) -> Result<Vec<u8>, CryptoError> {
 | 
				
			||||||
 | 
					        let mut symmetric_key = [0u8; 32];
 | 
				
			||||||
 | 
					        diffie_hellman(self.private.to_nonzero_scalar(), remote_key.0.as_affine())
 | 
				
			||||||
 | 
					            .extract::<Sha256>(None)
 | 
				
			||||||
 | 
					            .expand(&[], &mut symmetric_key)
 | 
				
			||||||
 | 
					            .map_err(|_| CryptoError::InvalidKeySize)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sym_key = SymmetricKey::from_bytes(&symmetric_key)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sym_key.encrypt(plaintext)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Decrypt data from a sender. The remote key must be the public key of the keypair used by
 | 
				
			||||||
 | 
					    /// the sender to encrypt this message.
 | 
				
			||||||
 | 
					    pub fn decrypt(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        remote_key: &PublicKey,
 | 
				
			||||||
 | 
					        ciphertext: &[u8],
 | 
				
			||||||
 | 
					    ) -> Result<Vec<u8>, CryptoError> {
 | 
				
			||||||
 | 
					        let mut symmetric_key = [0u8; 32];
 | 
				
			||||||
 | 
					        diffie_hellman(self.private.to_nonzero_scalar(), remote_key.0.as_affine())
 | 
				
			||||||
 | 
					            .extract::<Sha256>(None)
 | 
				
			||||||
 | 
					            .expand(&[], &mut symmetric_key)
 | 
				
			||||||
 | 
					            .map_err(|_| CryptoError::InvalidKeySize)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sym_key = SymmetricKey::from_bytes(&symmetric_key)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sym_key.decrypt(ciphertext)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PublicKey {
 | 
				
			||||||
 | 
					    /// Import a public key from raw bytes
 | 
				
			||||||
 | 
					    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        Ok(Self(k256::PublicKey::from_sec1_bytes(bytes)?))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get the raw bytes of this `PublicKey`, which can be transferred to another party.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The public key is SEC-1 encoded and compressed.
 | 
				
			||||||
 | 
					    pub fn as_bytes(&self) -> Box<[u8]> {
 | 
				
			||||||
 | 
					        self.0.to_encoded_point(true).to_bytes()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    /// Export a public key and import it later
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn import_public_key() {
 | 
				
			||||||
 | 
					        let kp = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					        let pk1 = kp.public_key();
 | 
				
			||||||
 | 
					        let pk_bytes = pk1.as_bytes();
 | 
				
			||||||
 | 
					        let pk2 = super::PublicKey::from_bytes(&pk_bytes).expect("Can import public key");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pk1, pk2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /// Make sure 2 random keypairs derive the same shared secret (and thus encryption key), by
 | 
				
			||||||
 | 
					    /// encrypting a random message, decrypting it, and verifying it matches.
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn encrypt_and_decrypt() {
 | 
				
			||||||
 | 
					        let kp1 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					        let kp2 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pk1 = kp1.public_key();
 | 
				
			||||||
 | 
					        let pk2 = kp2.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is a random message to encrypt and decrypt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let enc = kp1.encrypt(&pk2, message).expect("Can encrypt message");
 | 
				
			||||||
 | 
					        let dec = kp2.decrypt(&pk1, &enc).expect("Can decrypt message");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(message.as_slice(), dec.as_slice());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Use a different public key for decrypting than the expected one, this should fail the
 | 
				
			||||||
 | 
					    /// decryption process as we use AEAD encryption with the symmetric key.
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn decrypt_with_wrong_key() {
 | 
				
			||||||
 | 
					        let kp1 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					        let kp2 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					        let kp3 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pk2 = kp2.public_key();
 | 
				
			||||||
 | 
					        let pk3 = kp3.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is a random message to encrypt and decrypt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let enc = kp1.encrypt(&pk2, message).expect("Can encrypt message");
 | 
				
			||||||
 | 
					        let dec = kp2.decrypt(&pk3, &enc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(dec.is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										142
									
								
								vault/src/key/signature.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								vault/src/key/signature.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					//! An implementation of digitial signatures using secp256k1 ECDSA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use k256::ecdsa::{
 | 
				
			||||||
 | 
					    Signature, SigningKey, VerifyingKey,
 | 
				
			||||||
 | 
					    signature::{Signer, Verifier},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::error::CryptoError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct SigningKeypair {
 | 
				
			||||||
 | 
					    sk: SigningKey,
 | 
				
			||||||
 | 
					    vk: VerifyingKey,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct PublicKey(VerifyingKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SigningKeypair {
 | 
				
			||||||
 | 
					    /// Generates a new random keypair
 | 
				
			||||||
 | 
					    pub fn new() -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        let mut raw_private = [0u8; 32];
 | 
				
			||||||
 | 
					        rand::fill(&mut raw_private);
 | 
				
			||||||
 | 
					        let sk = SigningKey::from_slice(&raw_private)
 | 
				
			||||||
 | 
					            .expect("Key is provided generated with fixed valid size");
 | 
				
			||||||
 | 
					        let vk = sk.verifying_key().to_owned();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self { sk, vk })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create a new key from existing bytes.
 | 
				
			||||||
 | 
					    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        if bytes.len() == 32 {
 | 
				
			||||||
 | 
					            let sk = SigningKey::from_slice(&bytes).expect("Key was checked to be a valid size");
 | 
				
			||||||
 | 
					            let vk = sk.verifying_key().to_owned();
 | 
				
			||||||
 | 
					            Ok(Self { sk, vk })
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(CryptoError::InvalidKeySize)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// View the raw bytes of the private key of this keypair.
 | 
				
			||||||
 | 
					    pub(crate) fn as_raw_private_key(&self) -> Vec<u8> {
 | 
				
			||||||
 | 
					        self.sk.as_nonzero_scalar().to_bytes().to_vec()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get the public part of this keypair.
 | 
				
			||||||
 | 
					    pub fn public_key(&self) -> PublicKey {
 | 
				
			||||||
 | 
					        PublicKey(self.vk)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sign data with the private key of this `SigningKeypair`. Other parties can use the public
 | 
				
			||||||
 | 
					    /// key to verify the signature. The generated signature is a detached signature.
 | 
				
			||||||
 | 
					    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
				
			||||||
 | 
					        let sig: Signature = self.sk.sign(message);
 | 
				
			||||||
 | 
					        Ok(sig.to_vec())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PublicKey {
 | 
				
			||||||
 | 
					    /// Import a public key from raw bytes
 | 
				
			||||||
 | 
					    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
				
			||||||
 | 
					        Ok(Self(VerifyingKey::from_sec1_bytes(bytes)?))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get the raw bytes of this `PublicKey`, which can be transferred to another party.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The public key is SEC-1 encoded and compressed.
 | 
				
			||||||
 | 
					    pub fn as_bytes(&self) -> Box<[u8]> {
 | 
				
			||||||
 | 
					        self.0.to_encoded_point(true).to_bytes()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn verify_signature(&self, message: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
 | 
				
			||||||
 | 
					        let sig = Signature::from_slice(sig).map_err(|_| CryptoError::InvalidKeySize)?;
 | 
				
			||||||
 | 
					        self.0
 | 
				
			||||||
 | 
					            .verify(message, &sig)
 | 
				
			||||||
 | 
					            .map_err(|_| CryptoError::SignatureFailed)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Generate a key, get the public key, export the bytes of said public key, import them again
 | 
				
			||||||
 | 
					    /// as a public key, and verify the keys match. This make sure public keys can be exchanged.
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn recover_public_key() {
 | 
				
			||||||
 | 
					        let sk = super::SigningKeypair::new().expect("Can generate new key");
 | 
				
			||||||
 | 
					        let pk = sk.public_key();
 | 
				
			||||||
 | 
					        let pk_bytes = pk.as_bytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let pk2 = super::PublicKey::from_bytes(&pk_bytes).expect("Can import public key");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(pk, pk2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sign a message and validate the signature with the public key. Together with the above test
 | 
				
			||||||
 | 
					    /// this makes sure a remote system can receive our public key and validate messages we sign.
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn validate_signature() {
 | 
				
			||||||
 | 
					        let sk = super::SigningKeypair::new().expect("Can generate new key");
 | 
				
			||||||
 | 
					        let pk = sk.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is an arbitrary message we want to sign";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sig = sk.sign(message).expect("Message can be signed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(pk.verify_signature(message, &sig).is_ok());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Make sure a signature which is tampered with does not pass signature validation
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn corrupt_signature_does_not_validate() {
 | 
				
			||||||
 | 
					        let sk = super::SigningKeypair::new().expect("Can generate new key");
 | 
				
			||||||
 | 
					        let pk = sk.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is an arbitrary message we want to sign";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut sig = sk.sign(message).expect("Message can be signed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Tamper with the sig
 | 
				
			||||||
 | 
					        sig[0] = sig[0].wrapping_add(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(pk.verify_signature(message, &sig).is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Make sure a valid signature does not work for a message which has been modified
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn tampered_message_does_not_validate() {
 | 
				
			||||||
 | 
					        let sk = super::SigningKeypair::new().expect("Can generate new key");
 | 
				
			||||||
 | 
					        let pk = sk.public_key();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is an arbitrary message we want to sign";
 | 
				
			||||||
 | 
					        let mut message_clone = message.to_vec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let sig = sk.sign(message).expect("Message can be signed");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Modify the message
 | 
				
			||||||
 | 
					        message_clone[0] = message[0].wrapping_add(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(pk.verify_signature(&message_clone, &sig).is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										151
									
								
								vault/src/key/symmetric.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vault/src/key/symmetric.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					//! An implementation of symmetric keys for ChaCha20Poly1305 encryption.
 | 
				
			||||||
 | 
					//!
 | 
				
			||||||
 | 
					//! The ciphertext is authenticated.
 | 
				
			||||||
 | 
					//! The 12-byte nonce is appended to the generated ciphertext.
 | 
				
			||||||
 | 
					//! Keys are 32 bytes in size.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce, aead::Aead};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::error::CryptoError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct SymmetricKey([u8; 32]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Size of a nonce in ChaCha20Poly1305.
 | 
				
			||||||
 | 
					const NONCE_SIZE: usize = 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SymmetricKey {
 | 
				
			||||||
 | 
					    /// Generate a new random SymmetricKey.
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        let mut key = [0u8; 32];
 | 
				
			||||||
 | 
					        rand::fill(&mut key);
 | 
				
			||||||
 | 
					        Self(key)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Create a new key from existing bytes.
 | 
				
			||||||
 | 
					    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<SymmetricKey, CryptoError> {
 | 
				
			||||||
 | 
					        if bytes.len() == 32 {
 | 
				
			||||||
 | 
					            let mut key = [0u8; 32];
 | 
				
			||||||
 | 
					            key.copy_from_slice(bytes);
 | 
				
			||||||
 | 
					            Ok(SymmetricKey(key))
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Err(CryptoError::InvalidKeySize)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// View the raw bytes of this key
 | 
				
			||||||
 | 
					    pub(crate) fn as_raw_bytes(&self) -> &[u8; 32] {
 | 
				
			||||||
 | 
					        &self.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Encrypt a plaintext with the key. A nonce is generated and appended to the end of the
 | 
				
			||||||
 | 
					    /// message.
 | 
				
			||||||
 | 
					    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
				
			||||||
 | 
					        // Create cipher
 | 
				
			||||||
 | 
					        let cipher = ChaCha20Poly1305::new_from_slice(&self.0)
 | 
				
			||||||
 | 
					            .expect("Key is a fixed 32 byte array so size is always ok");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Generate random nonce
 | 
				
			||||||
 | 
					        let mut nonce_bytes = [0u8; NONCE_SIZE];
 | 
				
			||||||
 | 
					        rand::fill(&mut nonce_bytes);
 | 
				
			||||||
 | 
					        let nonce = Nonce::from_slice(&nonce_bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Encrypt message
 | 
				
			||||||
 | 
					        let mut ciphertext = cipher
 | 
				
			||||||
 | 
					            .encrypt(nonce, plaintext)
 | 
				
			||||||
 | 
					            .map_err(|_| CryptoError::EncryptionFailed)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Append nonce to ciphertext
 | 
				
			||||||
 | 
					        ciphertext.extend_from_slice(&nonce_bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(ciphertext)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Decrypts a ciphertext with appended nonce.
 | 
				
			||||||
 | 
					    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
				
			||||||
 | 
					        // Check if ciphertext is long enough to contain a nonce
 | 
				
			||||||
 | 
					        if ciphertext.len() <= NONCE_SIZE {
 | 
				
			||||||
 | 
					            return Err(CryptoError::DecryptionFailed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Extract nonce from the end of ciphertext
 | 
				
			||||||
 | 
					        let ciphertext_len = ciphertext.len() - NONCE_SIZE;
 | 
				
			||||||
 | 
					        let nonce_bytes = &ciphertext[ciphertext_len..];
 | 
				
			||||||
 | 
					        let ciphertext = &ciphertext[0..ciphertext_len];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create cipher
 | 
				
			||||||
 | 
					        let cipher = ChaCha20Poly1305::new_from_slice(&self.0)
 | 
				
			||||||
 | 
					            .expect("Key is a fixed 32 byte array so size is always ok");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let nonce = Nonce::from_slice(nonce_bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Decrypt message
 | 
				
			||||||
 | 
					        cipher
 | 
				
			||||||
 | 
					            .decrypt(nonce, ciphertext)
 | 
				
			||||||
 | 
					            .map_err(|_| CryptoError::DecryptionFailed)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Derives a new symmetric key from a password.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Derivation is done using pbkdf2 with Sha256 hashing.
 | 
				
			||||||
 | 
					    pub fn derive_from_password(password: &str) -> Self {
 | 
				
			||||||
 | 
					        /// Salt to use for PBKDF2. This needs to be consistent accross runs to generate the same
 | 
				
			||||||
 | 
					        /// key. Additionally, it does not really matter what this is, as long as its unique.
 | 
				
			||||||
 | 
					        const SALT: &[u8; 10] = b"vault_salt";
 | 
				
			||||||
 | 
					        /// Amount of rounds to use for key generation. More rounds => more cpu time. Changing this
 | 
				
			||||||
 | 
					        /// also chagnes the generated keys.
 | 
				
			||||||
 | 
					        const ROUNDS: u32 = 100_000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut key = [0; 32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pbkdf2::pbkdf2_hmac::<sha2::Sha256>(password.as_bytes(), SALT, ROUNDS, &mut key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Self(key)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Using the same password derives the same key
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn same_password_derives_same_key() {
 | 
				
			||||||
 | 
					        const EXPECTED_KEY: [u8; 32] = [
 | 
				
			||||||
 | 
					            4, 179, 233, 202, 225, 70, 211, 200, 7, 73, 115, 1, 85, 149, 90, 42, 160, 68, 16, 106,
 | 
				
			||||||
 | 
					            136, 19, 197, 195, 153, 145, 179, 21, 37, 13, 37, 90,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					        const PASSWORD: &str = "test123";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let key = super::SymmetricKey::derive_from_password(PASSWORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(key.0, EXPECTED_KEY);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Make sure an encrypted value with some key can be decrypted with the same key
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn can_decrypt() {
 | 
				
			||||||
 | 
					        let key = super::SymmetricKey::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is a message to decrypt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let enc = key.encrypt(message).expect("Can encrypt message");
 | 
				
			||||||
 | 
					        let dec = key.decrypt(&enc).expect("Can decrypt message");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(message.as_slice(), dec.as_slice());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Make sure a value encrypted with one key can't be decrypted with a different key. Since we
 | 
				
			||||||
 | 
					    /// use AEAD encryption we will notice this when trying to decrypt
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn different_key_cant_decrypt() {
 | 
				
			||||||
 | 
					        let key1 = super::SymmetricKey::new();
 | 
				
			||||||
 | 
					        let key2 = super::SymmetricKey::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let message = b"this is a message to decrypt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let enc = key1.encrypt(message).expect("Can encrypt message");
 | 
				
			||||||
 | 
					        let dec = key2.decrypt(&enc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(dec.is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										151
									
								
								vault/src/keyspace.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vault/src/keyspace.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					// #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					// mod fallback;
 | 
				
			||||||
 | 
					// #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					// mod wasm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    error::Error,
 | 
				
			||||||
 | 
					    key::{symmetric::SymmetricKey, Key},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// use kv::KVStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configuration to use for bincode en/decoding.
 | 
				
			||||||
 | 
					const BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Temporarily using simple file-based storage instead of external KV store
 | 
				
			||||||
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					use std::fs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					// use kv::native::NativeStore;
 | 
				
			||||||
 | 
					// #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					// use kv::wasm::WasmStore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const KEYSPACE_NAME: &str = "vault_keyspace";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A keyspace represents a group of stored cryptographic keys. The storage is encrypted, a
 | 
				
			||||||
 | 
					/// password must be provided when opening the KeySpace to decrypt the keys.
 | 
				
			||||||
 | 
					pub struct KeySpace {
 | 
				
			||||||
 | 
					    /// Path to the keyspace file (native only)
 | 
				
			||||||
 | 
					    #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					    path: std::path::PathBuf,
 | 
				
			||||||
 | 
					    /// Storage name for WASM
 | 
				
			||||||
 | 
					    #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    /// A collection of all keys stored in the KeySpace, in decrypted form.
 | 
				
			||||||
 | 
					    keys: HashMap<String, Key>,
 | 
				
			||||||
 | 
					    /// The encryption key used to encrypt/decrypt this keyspace.
 | 
				
			||||||
 | 
					    encryption_key: SymmetricKey,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Wasm32 constructor
 | 
				
			||||||
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					impl KeySpace {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Non-wasm constructor
 | 
				
			||||||
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					impl KeySpace {
 | 
				
			||||||
 | 
					    /// Open the keyspace at the provided path using the given key for encryption.
 | 
				
			||||||
 | 
					    pub async fn open(path: &Path, encryption_key: SymmetricKey) -> Result<Self, Error> {
 | 
				
			||||||
 | 
					        let keyspace_file = path.join(KEYSPACE_NAME);
 | 
				
			||||||
 | 
					        let mut ks = Self {
 | 
				
			||||||
 | 
					            path: keyspace_file,
 | 
				
			||||||
 | 
					            keys: HashMap::new(),
 | 
				
			||||||
 | 
					            encryption_key,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        ks.load_keyspace().await?;
 | 
				
			||||||
 | 
					        Ok(ks)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					impl KeySpace {
 | 
				
			||||||
 | 
					    pub async fn open(name: &str, encryption_key: SymmetricKey) -> Result<Self, Error> {
 | 
				
			||||||
 | 
					        let mut ks = Self {
 | 
				
			||||||
 | 
					            name: name.to_string(),
 | 
				
			||||||
 | 
					            keys: HashMap::new(),
 | 
				
			||||||
 | 
					            encryption_key,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        ks.load_keyspace().await?;
 | 
				
			||||||
 | 
					        Ok(ks)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Exposed methods, platform independant
 | 
				
			||||||
 | 
					impl KeySpace {
 | 
				
			||||||
 | 
					    /// Get a [`Key`] previously stored under the provided name.
 | 
				
			||||||
 | 
					    pub async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
 | 
				
			||||||
 | 
					        Ok(self.keys.get(key).cloned())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Store a [`Key`] under the provided name.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This overwrites the existing key if one is already stored with the same name.
 | 
				
			||||||
 | 
					    pub async fn set(&mut self, key: String, value: Key) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        self.keys.insert(key, value);
 | 
				
			||||||
 | 
					        self.save_keyspace().await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Delete the [`Key`] stored under the provided name.
 | 
				
			||||||
 | 
					    pub async fn delete(&mut self, key: &str) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        self.keys.remove(key);
 | 
				
			||||||
 | 
					        self.save_keyspace().await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterate over all stored [`keys`](Key) in the KeySpace
 | 
				
			||||||
 | 
					    pub async fn iter(&self) -> Result<impl Iterator<Item = (&String, &Key)>, Error> {
 | 
				
			||||||
 | 
					        Ok(self.keys.iter())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Encrypt all keys and save them to the underlying store
 | 
				
			||||||
 | 
					    async fn save_keyspace(&self) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        let encoded_keys = bincode::serde::encode_to_vec(&self.keys, BINCODE_CONFIG)?;
 | 
				
			||||||
 | 
					        let value = self.encryption_key.encrypt(&encoded_keys)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            fs::write(&self.path, &value)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // For WASM, we'll use localStorage or similar
 | 
				
			||||||
 | 
					            // This is a placeholder - would need proper WASM storage implementation
 | 
				
			||||||
 | 
					            log::warn!("WASM storage not yet implemented");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Loads the encrypted keyspace from the underlying storage
 | 
				
			||||||
 | 
					    async fn load_keyspace(&mut self) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if !self.path.exists() {
 | 
				
			||||||
 | 
					                // Keyspace doesn't exist yet, nothing to do here
 | 
				
			||||||
 | 
					                return Ok(());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let ks = fs::read(&self.path)?;
 | 
				
			||||||
 | 
					            let raw = self.encryption_key.decrypt(&ks)?;
 | 
				
			||||||
 | 
					            let (decoded_keys, _): (HashMap<String, Key>, _) =
 | 
				
			||||||
 | 
					                bincode::serde::decode_from_slice(&raw, BINCODE_CONFIG)?;
 | 
				
			||||||
 | 
					            self.keys = decoded_keys;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // For WASM, we'll use localStorage or similar
 | 
				
			||||||
 | 
					            // This is a placeholder - would need proper WASM storage implementation
 | 
				
			||||||
 | 
					            log::warn!("WASM storage not yet implemented");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								vault/src/keyspace/fallback.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vault/src/keyspace/fallback.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					use std::{collections::HashMap, io::Write, path::PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    error::Error,
 | 
				
			||||||
 | 
					    key::{Key, symmetric::SymmetricKey},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Magic value used as header in decrypted keyspace files.
 | 
				
			||||||
 | 
					const KEYSPACE_MAGIC: [u8; 14] = [
 | 
				
			||||||
 | 
					    118, 97, 117, 108, 116, 95, 107, 101, 121, 115, 112, 97, 99, 101,
 | 
				
			||||||
 | 
					]; //"vault_keyspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A KeySpace using the filesystem as storage
 | 
				
			||||||
 | 
					pub struct KeySpace {
 | 
				
			||||||
 | 
					    /// Path to file on disk
 | 
				
			||||||
 | 
					    path: PathBuf,
 | 
				
			||||||
 | 
					    /// Decrypted keys held in the store
 | 
				
			||||||
 | 
					    keystore: HashMap<String, Key>,
 | 
				
			||||||
 | 
					    /// The encryption key used to encrypt/decrypt the storage.
 | 
				
			||||||
 | 
					    encryption_key: SymmetricKey,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl KeySpace {
 | 
				
			||||||
 | 
					    /// Opens the `KeySpace`. If it does not exist, it will be created. The provided encryption key
 | 
				
			||||||
 | 
					    /// will be used for Encrypting and Decrypting the content of the KeySpace.
 | 
				
			||||||
 | 
					    async fn open(path: PathBuf, encryption_key: SymmetricKey) -> Result<Self, Error> {
 | 
				
			||||||
 | 
					        /// If the path does not exist, create it first and write the encrypted magic header
 | 
				
			||||||
 | 
					        if !path.exists() {
 | 
				
			||||||
 | 
					            // Since we checked path does not exist, the only errors here can be actual IO errors
 | 
				
			||||||
 | 
					            // (unless something else creates the same file at the same time).
 | 
				
			||||||
 | 
					            let mut file = std::fs::File::create_new(path)?;
 | 
				
			||||||
 | 
					            let content = encryption_key.encrypt(&KEYSPACE_MAGIC)?;
 | 
				
			||||||
 | 
					            file.write_all(&content)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Load file, try to decrypt, verify magic header, deserialize keystore
 | 
				
			||||||
 | 
					        let mut file = std::fs::File::open(path)?;
 | 
				
			||||||
 | 
					        let mut buffer = Vec::new();
 | 
				
			||||||
 | 
					        file.read_to_end(&mut buffer)?;
 | 
				
			||||||
 | 
					        if buffer.len() < KEYSPACE_MAGIC.len() {
 | 
				
			||||||
 | 
					            return Err(Error::CorruptKeyspace);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if buffer[..KEYSPACE_MAGIC.len()] != KEYSPACE_MAGIC {
 | 
				
			||||||
 | 
					            return Err(Error::CorruptKeyspace);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: Actual deserialization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a [`Key`] previously stored under the provided name.
 | 
				
			||||||
 | 
					    async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Store a [`Key`] under the provided name.
 | 
				
			||||||
 | 
					    async fn set(&self, key: &str, value: Key) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Delete the [`Key`] stored under the provided name.
 | 
				
			||||||
 | 
					    async fn delete(&self, key: &str) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterate over all stored [`keys`](Key) in the KeySpace
 | 
				
			||||||
 | 
					    async fn iter(&self) -> Result<impl Iterator<Item = (String, Key)>, Error> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								vault/src/keyspace/wasm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vault/src/keyspace/wasm.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					use crate::{error::Error, key::Key};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// KeySpace represents an IndexDB keyspace
 | 
				
			||||||
 | 
					pub struct KeySpace {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl KeySpace {
 | 
				
			||||||
 | 
					    /// Get a [`Key`] previously stored under the provided name.
 | 
				
			||||||
 | 
					    async fn get(&self, key: &str) -> Result<Option<Key>, Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Store a [`Key`] under the provided name.
 | 
				
			||||||
 | 
					    async fn set(&self, key: &str, value: Key) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Delete the [`Key`] stored under the provided name.
 | 
				
			||||||
 | 
					    async fn delete(&self, key: &str) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        todo!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Iterate over all stored [`keys`](Key) in the KeySpace
 | 
				
			||||||
 | 
					    async fn iter(&self) -> Result<impl Iterator<Item = (String, Key)>, Error> {
 | 
				
			||||||
 | 
					        todo!()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,23 +1,51 @@
 | 
				
			|||||||
//! SAL Vault: Cryptographic functionality for SAL
 | 
					 | 
				
			||||||
//!
 | 
					 | 
				
			||||||
//! This package provides cryptographic operations including:
 | 
					 | 
				
			||||||
//! - Key space management (creation, loading, encryption, decryption)
 | 
					 | 
				
			||||||
//! - Key pair management (ECDSA)
 | 
					 | 
				
			||||||
//! - Digital signatures (signing and verification)
 | 
					 | 
				
			||||||
//! - Symmetric encryption (ChaCha20Poly1305)
 | 
					 | 
				
			||||||
//! - Ethereum wallet functionality
 | 
					 | 
				
			||||||
//! - Key-value store with encryption
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub mod error;
 | 
					pub mod error;
 | 
				
			||||||
pub mod ethereum;
 | 
					pub mod key;
 | 
				
			||||||
pub mod keyspace;
 | 
					pub mod keyspace;
 | 
				
			||||||
pub mod kvs;
 | 
					 | 
				
			||||||
pub mod symmetric;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Rhai integration module
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
pub mod rhai;
 | 
					use std::path::{Path, PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Re-export modules
 | 
					use crate::{error::Error, key::symmetric::SymmetricKey, keyspace::KeySpace};
 | 
				
			||||||
// Re-export common types for convenience
 | 
					
 | 
				
			||||||
pub use error::CryptoError;
 | 
					/// Vault is a 2 tiered key-value store. That is, it is a collection of [`spaces`](KeySpace), where
 | 
				
			||||||
pub use keyspace::{KeyPair, KeySpace};
 | 
					/// each [`space`](KeySpace) is itself an encrypted key-value store
 | 
				
			||||||
 | 
					pub struct Vault {
 | 
				
			||||||
 | 
					    #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					    path: PathBuf,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					impl Vault {
 | 
				
			||||||
 | 
					    /// Create a new store at the given path, creating the path if it does not exist yet.
 | 
				
			||||||
 | 
					    pub async fn new(path: &Path) -> Result<Self, Error> {
 | 
				
			||||||
 | 
					        if path.exists() {
 | 
				
			||||||
 | 
					            if !path.is_dir() {
 | 
				
			||||||
 | 
					                return Err(Error::IOError(std::io::Error::new(
 | 
				
			||||||
 | 
					                    std::io::ErrorKind::InvalidInput,
 | 
				
			||||||
 | 
					                    "expected directory",
 | 
				
			||||||
 | 
					                )));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            std::fs::create_dir_all(path)?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(Self {
 | 
				
			||||||
 | 
					            path: path.to_path_buf(),
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Vault {
 | 
				
			||||||
 | 
					    /// Open a keyspace with the given name
 | 
				
			||||||
 | 
					    pub async fn open_keyspace(&self, name: &str, password: &str) -> Result<KeySpace, Error> {
 | 
				
			||||||
 | 
					        let encryption_key = SymmetricKey::derive_from_password(password);
 | 
				
			||||||
 | 
					        #[cfg(not(target_arch = "wasm32"))]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let path = self.path.join(name);
 | 
				
			||||||
 | 
					            KeySpace::open(&path, encryption_key).await
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #[cfg(target_arch = "wasm32")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            KeySpace::open(name, encryption_key).await
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user