6 stable releases
| 2.0.0 | Jan 11, 2026 |
|---|---|
| 1.1.0 | Jan 10, 2026 |
| 1.0.3 | Jan 3, 2026 |
| 1.0.2 | Dec 27, 2025 |
| 1.0.0 | Nov 15, 2025 |
#407 in Cryptography
77KB
1.5K
SLoC
Hana Vault Format
A secure, production-ready Rust library for storing SSH credentials, hosts, and sensitive data using an encrypted SQLite vault format.
Features
- In-Memory SQLite Storage - Database lives in RAM and is never written to disk in plaintext
- Binary Encryption - Export/import vaults as encrypted bytes (perfect for network transmission)
- Autonomous Migrations - Schema versioning with automatic migration system
- Multiple Auth Types - Support for username/password, RSA, OpenSSH, Ed25519, ECDSA, and certificate-based authentication
- Host Management - Store hosts with startup commands, environment variables, and custom encodings
- Industry-Standard Crypto - AES-256-GCM with Argon2id key derivation
- Zero Plaintext - Raw SQLite database never exposed - only encrypted formats allowed
- Memory Safety - Sensitive data automatically zeroized on drop
Security Features
Multi-Layer Security
-
In-Memory SQLite Database
- Database exists only in RAM, never on disk
- Isolated from filesystem exposure
- Automatic cleanup on process termination
-
Binary Format Encryption
- AES-256-GCM for authenticated encryption
- Argon2id for password-based key derivation (defaults: 64 MiB memory, 3 iterations, parallelism 4)
- SHA-256 checksums for integrity verification
- Random salt and nonce for each encryption operation
- Security Best Practices
- No plaintext SQLite ever written to disk
- In-memory database only
- Zeroization of sensitive data
- Foreign key constraints for data integrity
- Prepared statements to prevent SQL injection
Quick Start
use hana_vault::{Vault, Host, Credential};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new vault
let vault = Vault::new("secure_password")?;
// Add a host with environment variables
let mut host = Host::new("Production Server".to_string(), "prod.example.com".to_string(), 22);
host.add_env_var("NODE_ENV".to_string(), "production".to_string());
host = host.with_startup_command("source ~/.profile".to_string());
host = host.with_username("deploy".to_string());
vault.add_host(&host)?;
// Add SSH key credentials
let cred = Credential::new_ssh_key(
"Deploy Key".to_string(),
hana_vault::CredentialType::OpenSsh,
"-----BEGIN OPENSSH PRIVATE KEY-----\n...".to_string(),
Some("ssh-ed25519 AAAAC3...".to_string()),
None, // Optional passphrase
);
vault.add_credential(&cred)?;
// Link credential to host
vault.link_credential_to_host(host.id, cred.id, true)?;
// Export as encrypted bytes (e.g., to save to file or send to server)
let encrypted_bytes = vault.export_to_bytes()?;
std::fs::write("vault.hev", &encrypted_bytes)?;
Ok(())
}
Usage Examples
Loading Vaults
use hana_vault::Vault;
use std::path::Path;
// Load from encrypted file (manual)
let encrypted_bytes = std::fs::read("vault.hev")?;
let vault = Vault::load_from_bytes(&encrypted_bytes, "password")?;
// Load from encrypted bytes (e.g., received from server)
let vault = Vault::load_from_bytes(&encrypted_bytes, "password")?;
Reading Vault ID
You can read the unique identifier of a vault without decrypting it (useful for system keyrings):
use hana_vault::Vault;
let encrypted_bytes = std::fs::read("vault.hev")?;
let vault_id = Vault::get_id_from_bytes(&encrypted_bytes)?;
println!("Vault ID: {}", vault_id);
Managing Hosts
use hana_vault::Host;
// Create a host
let mut host = Host::new("Web Server".to_string(), "web.example.com".to_string(), 22);
// Add startup command
host = host.with_startup_command("cd /var/www && source env.sh".to_string());
// Set custom encoding
host = host.with_encoding("UTF-8".to_string());
// Add environment variables
host.add_env_var("PATH".to_string(), "/usr/local/bin:/usr/bin".to_string());
host.add_env_var("PORT".to_string(), "3000".to_string());
vault.add_host(&host)?;
// List all hosts
let hosts = vault.list_hosts()?;
// Get specific host
let host = vault.get_host(host_id)?;
// Update host
host.port = 2222;
vault.update_host(&host)?;
// Delete host
vault.delete_host(host_id)?;
Managing Credentials
use hana_vault::{Credential, CredentialType};
// Username/Password credentials
let cred = Credential::new_username_password(
"Admin Login".to_string(),
"admin".to_string(),
"super_secret_password".to_string(),
);
vault.add_credential(&cred)?;
// RSA key credentials
let rsa_cred = Credential::new_ssh_key(
"RSA Key".to_string(),
CredentialType::Rsa,
"-----BEGIN RSA PRIVATE KEY-----\n...".to_string(),
Some("ssh-rsa AAAAB3...".to_string()),
Some("key_passphrase".to_string()),
);
vault.add_credential(&rsa_cred)?;
// Ed25519 key credentials
let ed25519_cred = Credential::new_ssh_key(
"Ed25519 Key".to_string(),
CredentialType::Ed25519,
"-----BEGIN OPENSSH PRIVATE KEY-----\n...".to_string(),
Some("ssh-ed25519 AAAAC3...".to_string()),
None,
);
vault.add_credential(&ed25519_cred)?;
// List all credentials
let credentials = vault.list_credentials()?;
// Update credential
cred.name = "Updated Name".to_string();
vault.update_credential(&cred)?;
// Delete credential
vault.delete_credential(cred_id)?;
Linking Credentials to Hosts
// Link a credential to a host (set as default)
vault.link_credential_to_host(host_id, credential_id, true)?;
// Link another credential to the same host (not default)
vault.link_credential_to_host(host_id, another_credential_id, false)?;
// Get all credentials for a host
let host_creds = vault.get_host_credentials(host_id)?;
for (credential, is_default) in host_creds {
println!("{}: {} (default: {})", credential.id, credential.name, is_default);
}
// Unlink credential from host
vault.unlink_credential_from_host(host_id, credential_id)?;
Database Schema
The library uses an SQLite database with the following schema:
hosts- Host configurations (hostname, port, startup commands, encoding, etc.)host_env_vars- Environment variables for hostscredentials- Authentication credentials (all types)host_credentials- Many-to-many relationship between hosts and credentialsschema_version- Automatic migration tracking
Versioning & Migrations
The library includes an autonomous migration system that:
- Automatically detects schema version on vault load
- Runs pending migrations seamlessly
- Ensures backward compatibility
- Tracks applied migrations
Migrations run automatically when you load a vault, ensuring it's always at the latest schema version.
KDF Compatibility
Vaults encrypted with one key-derivation configuration are not guaranteed to be decryptable by builds that change the KDF parameters/algorithm. If you need to support older vault files, keep the previous KDF available for decryption and migrate by re-exporting.
Testing
Run the comprehensive test suite:
cargo test
Run tests with output:
cargo test -- --nocapture
API Reference
Core Types
-
Vault- Main vault structure for managing encrypted datanew(name, password)- Create new vaultload_from_bytes(bytes, password)- Load from encrypted bytesexport_to_bytes()- Export as encrypted bytes
-
VaultStorage- Trait for implementing custom storage backendsfetch(password)- Fetch and load vaultsave(vault)- Save vaultdelete()- Delete vault
-
Host- Host configurationnew(name, hostname, port)- Create new hostwith_startup_command(cmd)- Set startup commandwith_encoding(encoding)- Set custom encodingadd_env_var(key, value)- Add environment variable
-
Credential- Authentication credentialnew_username_password(name, username, password)- Create password credentialnew_ssh_key(name, type, private_key, public_key, passphrase)- Create SSH key credential
-
CredentialType- Enum of supported authentication typesUsernamePasswordRsaOpenSshEd25519EcdsaCertificateCustom
Error Handling
All operations return Result<T, VaultError>:
use hana_vault::{Vault, VaultError};
let bytes = std::fs::read("vault.hev")?;
match Vault::load_from_bytes(&bytes) {
Ok(vault) => println!("Vault loaded successfully"),
Err(VaultError::InvalidPassword) => eprintln!("Wrong password"),
Err(VaultError::CorruptedData(msg)) => eprintln!("Data corruption: {}", msg),
Err(e) => eprintln!("Error: {}", e),
}
File Format
Encrypted vault files (.hev) have the following structure:
┌─────────────────────────────────────────┐
│ Header (68 bytes, unencrypted) │
│ ├─ Magic bytes (8 bytes): "HANAVLT1" │
│ ├─ Version (4 bytes): u32 │
│ ├─ UID (16 bytes): UUID v4 │
│ ├─ Checksum (32 bytes): SHA-256 │
│ └─ Data size (8 bytes): u64 │
├─────────────────────────────────────────┤
│ Encrypted Data │
│ ├─ Salt (32 bytes) │
│ ├─ Nonce (12 bytes) │
│ └─ Ciphertext (variable) │
│ └─ Encrypted SQLite database │
└─────────────────────────────────────────┘
Contributing
Contributions are welcome! Please ensure:
- All tests pass (
cargo test) - Code follows Rust conventions (
cargo fmt,cargo clippy) - Security implications are considered
- Documentation is updated
License
This project is licensed under the MIT License - see the LICENSE file for details.
Dependencies
~30MB
~559K SLoC