herolib-vault
Secure vault for managing cryptographic keys with Redis backend.
Overview
The vault provides a secure way to store and manage cryptographic keys for multiple blockchain ecosystems:
- Ed25519: Used by Near Protocol and many other chains
- Sr25519: Used by Polkadot, Kusama, and Substrate-based chains (including ThreeFold)
Features
- Redis Backend: Keys are stored encrypted in Redis for distributed access
- Multiple Vaults: Open multiple vaults with different names on the same Redis
- XChaCha20-Poly1305: Authenticated encryption for key storage
- Argon2id: Memory-hard key derivation from secrets (GPU-resistant)
- Crypt Integration: Full access to herolib-crypt primitives
Quick Start
Rust
use herolib_vault::Vault;
// Open a vault (uses default Redis connection from environment)
let vault = Vault::open("my_vault", "my-secret-password")?;
// Generate keys
let ed25519_pubkey = vault.generate_ed25519("near_key")?;
let sr25519_pubkey = vault.generate_sr25519("polkadot_key")?;
// Sign messages
let signature = vault.sign("near_key", b"Hello, Near!")?;
let signature_hex = vault.sign_hex("near_key", b"Hello, Near!")?;
// Verify signatures
let valid = vault.verify("near_key", b"Hello, Near!", &signature)?;
// Get addresses
let near_address = vault.near_address("near_key")?;
let polkadot_address = vault.polkadot_address("polkadot_key")?;
// Use symmetric encryption (using the vault's secret)
let encrypted = vault.encrypt_string("secret data")?;
let decrypted = vault.decrypt_string(&encrypted)?;
Rhai
// Open a vault
let vault = vault_open("my_vault", "my-secret");
// Generate keys
let ed25519_pubkey = vault.generate_ed25519("near_key");
let sr25519_pubkey = vault.generate_sr25519("polkadot_key");
// Import existing keys
vault.import_ed25519("imported_near", "hex_private_key_here");
vault.import_sr25519("imported_dot", "hex_private_key_here");
// Sign a message
let signature = vault.sign("near_key", "Hello, Near!");
let signature_hex = vault.sign_hex("near_key", "Hello, Near!");
// Verify a signature
let valid = vault.verify("near_key", "Hello, Near!", signature);
// Get public key
let pubkey = vault.public_key("near_key");
let key_type = vault.key_type("near_key"); // "Ed25519" or "Sr25519"
// Get addresses
let near_addr = vault.near_address("near_key");
let polkadot_addr = vault.polkadot_address("polkadot_key");
let kusama_addr = vault.kusama_address("polkadot_key");
let substrate_addr = vault.substrate_address("polkadot_key");
let custom_addr = vault.ss58_address("polkadot_key", 42); // Custom network ID
// Symmetric encryption
let encrypted = vault.encrypt_string("secret data");
let decrypted = vault.decrypt_string(encrypted);
// Key management
let keys = vault.list();
print(`Keys in vault: ${keys}`);
if vault.exists("near_key") {
print("Key exists!");
}
vault.delete("old_key");
// Get vault name
let name = vault.name();
// Standalone encryption functions
let keypair = generate_encryption_keypair();
let encrypted = encrypt_for_recipient("secret", keypair.public_key);
let decrypted = decrypt_with_private_key(encrypted, keypair.private_key);
// Password-based encryption (standalone)
let encrypted = encrypt_with_password("data", "password");
let decrypted = decrypt_with_password(encrypted, "password");
Redis Configuration
The vault uses environment variables for Redis connection:
| Variable |
Description |
Default |
REDIS_HOST |
Redis host |
127.0.0.1 |
REDIS_PORT |
Redis port |
6379 |
REDIS_PASSWORD |
Redis password |
(none) |
REDIS_USERNAME |
Redis username (6.0+) |
(none) |
REDISDB |
Redis database number |
0 |
Or use a Unix socket at ~/hero/var/myredis.sock if it exists.
Custom Redis Configuration
use herolib_vault::{Vault, RedisConfigBuilder};
let config = RedisConfigBuilder::new()
.host("redis.example.com")
.port(6380)
.password("secret")
.db(1);
let vault = Vault::open_with_config(config, "my_vault", "my-secret")?;
Using an Existing Redis Client
use herolib_vault::Vault;
use herolib_clients::redis::get_redis_client;
use std::sync::Arc;
let redis = get_redis_client()?;
let vault = Vault::open_with_client(redis, "my_vault", "my-secret")?;
API Reference
Vault Methods
Opening a Vault
| Method |
Description |
Vault::open(name, secret) |
Open vault with default Redis connection |
Vault::open_with_config(config, name, secret) |
Open vault with custom Redis config |
Vault::open_with_client(redis, name, secret) |
Open vault with existing Redis client |
Key Generation
| Method |
Description |
generate_ed25519(key_name) |
Generate new Ed25519 keypair, returns public key hex |
generate_sr25519(key_name) |
Generate new Sr25519 keypair, returns public key hex |
Key Import
| Method |
Description |
import_ed25519(key_name, private_key_bytes) |
Import Ed25519 from bytes |
import_ed25519_hex(key_name, private_key_hex) |
Import Ed25519 from hex string |
import_sr25519(key_name, private_key_bytes) |
Import Sr25519 from bytes |
import_sr25519_hex(key_name, private_key_hex) |
Import Sr25519 from hex string |
Key Operations
| Method |
Description |
public_key(key_name) |
Get public key as hex string |
public_key_bytes(key_name) |
Get public key as bytes |
key_type(key_name) |
Get key type (Ed25519 or Sr25519) |
sign(key_name, message) |
Sign message, returns signature bytes |
sign_hex(key_name, message) |
Sign message, returns signature hex |
verify(key_name, message, signature) |
Verify signature |
Address Generation
| Method |
Description |
near_address(key_name) |
Get Near format address (ed25519:...) |
ss58_address(key_name, network_id) |
Get SS58 address with custom prefix |
polkadot_address(key_name) |
Get Polkadot address (SS58 prefix 0) |
kusama_address(key_name) |
Get Kusama address (SS58 prefix 2) |
substrate_address(key_name) |
Get generic Substrate address (SS58 prefix 42) |
Key Management
| Method |
Description |
exists(key_name) |
Check if key exists |
delete(key_name) |
Delete a key |
list() |
List all key names |
name() |
Get vault name |
Symmetric Encryption
| Method |
Description |
encrypt(plaintext_bytes) |
Encrypt bytes |
decrypt(ciphertext_bytes) |
Decrypt bytes |
encrypt_string(plaintext) |
Encrypt string, returns base64 |
decrypt_string(ciphertext_base64) |
Decrypt base64 string |
Standalone Functions
| Function |
Description |
generate_encryption_keypair() |
Generate X25519 keypair for asymmetric encryption |
encrypt_for_recipient(message, public_key_hex) |
Encrypt message for recipient |
decrypt_with_private_key(encrypted, private_key_hex) |
Decrypt with private key |
encrypt_with_password(data, password) |
Password-based encryption |
decrypt_with_password(encrypted, password) |
Password-based decryption |
Key Types
Ed25519 (Near Protocol)
Ed25519 keys provide:
- Fast signing and verification
- Deterministic signatures
- 32-byte public keys
- Near-format addresses:
ed25519:<base58_public_key>
let vault = Vault::open("my_vault", "secret")?;
// Generate key
vault.generate_ed25519("my_near_key")?;
// Or import existing key
vault.import_ed25519_hex("imported_key", "your_private_key_hex")?;
// Get Near address
let address = vault.near_address("my_near_key")?;
// Returns: "ed25519:Abc123..."
// Sign and verify
let sig = vault.sign("my_near_key", b"message")?;
let valid = vault.verify("my_near_key", b"message", &sig)?;
Sr25519 (Polkadot/Substrate)
Sr25519 keys provide:
- VRF (Verifiable Random Function) support
- Better security properties for some use cases
- SS58 address encoding for different networks
let vault = Vault::open("my_vault", "secret")?;
// Generate key
vault.generate_sr25519("my_dot_key")?;
// Get addresses for different networks
let polkadot = vault.polkadot_address("my_dot_key")?; // SS58 prefix 0
let kusama = vault.kusama_address("my_dot_key")?; // SS58 prefix 2
let substrate = vault.substrate_address("my_dot_key")?; // SS58 prefix 42
// Custom network (e.g., ThreeFold)
let threefold = vault.ss58_address("my_dot_key", 42)?;
Encryption
Symmetric Encryption (Vault Secret)
The vault can encrypt/decrypt data using its own secret:
let vault = Vault::open("my_vault", "secret")?;
// Encrypt bytes
let encrypted = vault.encrypt(b"binary data")?;
let decrypted = vault.decrypt(&encrypted)?;
// Encrypt strings (returns base64)
let encrypted = vault.encrypt_string("sensitive data")?;
let decrypted = vault.decrypt_string(&encrypted)?;
Password-Based Encryption
Encrypt data with a standalone password:
use herolib_vault::{encrypt_with_password, decrypt_with_password};
let encrypted = encrypt_with_password(b"data", "password")?;
let decrypted = decrypt_with_password(&encrypted, "password")?;
Asymmetric Encryption (X25519)
For public-key encryption:
use herolib_vault::{generate_encryption_keypair, encrypt_for_recipient, decrypt_with_private_key};
// Generate keypair
let (private_key, public_key) = generate_encryption_keypair()?;
// Encrypt for recipient
let encrypted = encrypt_for_recipient("secret message", &public_key)?;
// Decrypt with private key
let decrypted = decrypt_with_private_key(&encrypted, &private_key)?;
Security
- All private keys are encrypted before storage using XChaCha20-Poly1305
- Encryption keys are derived from secrets using Argon2id (GPU-resistant)
- Each vault has its own unique salt stored in Redis
- Private keys are zeroized from memory when dropped
- Keys stored in Redis are prefixed with
vault:<name>:keys: for isolation
Building
cd packages/vault
cargo build
cargo test
cargo doc --open
Dependencies
herolib-crypt: Cryptographic primitives (XChaCha20-Poly1305, Argon2id, X25519)
herolib-clients: Redis client
ed25519-dalek: Ed25519 signatures
schnorrkel: Sr25519 (Schnorrkel) signatures
rhai: Scripting support