Keystore & Signatures
The Ashen keystore provides secure, encrypted storage for ed25519 signing keys. All key material is encrypted at rest using XChaCha20-Poly1305 with a password-derived key (Argon2id). There is no plaintext metadata—the entire payload requires the password to access.
Quick Start
Section titled “Quick Start”# Initialize a new keystoreashen keystore init
# Generate a new keyashen keystore add --label my-validator
# List keysashen keystore list
# Export public addressashen keystore export --label my-validatorSecurity Architecture
Section titled “Security Architecture”Signature Scheme: ed25519
Section titled “Signature Scheme: ed25519”Ashen uses ed25519 for all transaction signatures and validator network keys.
| Property | Value |
|---|---|
| Algorithm | Ed25519 (RFC 8032) |
| Private Key Size | 32 bytes |
| Public Key Size | 32 bytes |
| Signature Size | 64 bytes |
| Security Level | ~128 bits |
ed25519 provides:
- Fast signing and verification — optimized for high-throughput transaction processing
- Deterministic signatures — same message + key always produces the same signature
- No side-channel leaks — constant-time operations prevent timing attacks
- Small keys and signatures — efficient for network transmission and storage
Encryption at Rest: XChaCha20-Poly1305
Section titled “Encryption at Rest: XChaCha20-Poly1305”The keystore file is encrypted using XChaCha20-Poly1305 (AEAD):
| Property | Value |
|---|---|
| Cipher | XChaCha20-Poly1305 |
| Key Size | 256 bits |
| Nonce Size | 192 bits (24 bytes) |
| Auth Tag | 128 bits |
Why XChaCha20-Poly1305:
- Extended nonce — 192-bit nonces allow safe random generation without collision risk
- AEAD — Authenticated Encryption with Associated Data prevents tampering
- No weak keys — Unlike AES, ChaCha20 has no weak key classes
- Constant-time — Immune to cache-timing attacks
Password Protection: Argon2id
Section titled “Password Protection: Argon2id”Passwords are stretched into encryption keys using Argon2id, the winner of the Password Hashing Competition:
| Parameter | Default Value | Description |
|---|---|---|
| Algorithm | Argon2id | Hybrid of Argon2i and Argon2d |
| Memory Cost | 64 MB (m_cost=65536) | Memory required for hashing |
| Time Cost | 3 iterations | Sequential hash iterations |
| Parallelism | 2 threads | Parallel computation lanes |
| Salt | 32 bytes (random) | Per-keystore salt |
| Output | 32 bytes | Derived encryption key |
Why Argon2id:
- Memory-hard — Requires 64 MB of RAM, making GPU/ASIC attacks expensive
- Side-channel resistant — Argon2id hybrid mode resists timing attacks
- Tunable — Parameters scale with hardware improvements
- Fresh salt — Each keystore has a unique random salt
Memory Safety: Zeroize
Section titled “Memory Safety: Zeroize”All sensitive key material is automatically zeroed from memory when:
- The
UnlockedKeystoreis dropped - Secret key bytes go out of scope
- The derived encryption key is released
// Internal implementation#[derive(Clone, Zeroize, ZeroizeOnDrop)]struct SecretBytes(Vec<u8>);
impl Drop for DerivedKey { fn drop(&mut self) { self.key.zeroize(); // Overwrite with zeros }}This prevents secrets from lingering in memory where they could be extracted via:
- Core dumps
- Memory scanners
- Cold boot attacks
- Swap file analysis
File System Security
Section titled “File System Security”On Unix systems, keystore files are written with mode 0600 (owner read/write only):
$ ls -la ~/.local/share/ashen/keystore/keystore.json-rw------- 1 user user 1234 Jan 22 10:00 keystore.jsonAdditional file safety measures:
- Atomic writes — Changes are written to a temp file, then atomically renamed
- File locking — Exclusive locks prevent concurrent corruption
- Parent directory creation — Directories are created with safe defaults
Keystore File Format
Section titled “Keystore File Format”The keystore is stored as JSON with the following structure:
{ "version": 1, "kdf": { "algorithm": "argon2id", "salt_b64": "<base64-encoded-32-byte-salt>", "m_cost": 65536, "t_cost": 3, "p_cost": 2 }, "nonce": "<base64-encoded-24-byte-nonce>", "ciphertext": "<base64-encoded-encrypted-payload>"}The encrypted payload contains:
struct KeystorePayload { version: u8, // Payload version (currently 1) keys: Vec<StoredKey>, // All stored keys}
struct StoredKey { handle: [u8; 16], // Random key identifier key_type: KeyType, // Ed25519 or BlsMinSig label: String, // User-assigned label created_at: u64, // Unix timestamp secret: Vec<u8>, // Encrypted private key public_key: Vec<u8>, // Derived public key}Key Types
Section titled “Key Types”| Type | Enum | Size | Use Case |
|---|---|---|---|
| ed25519 | KeyType::Ed25519 | 32 bytes | Transaction signing, validator network keys |
| BLS | KeyType::BlsMinSig | 32 bytes | Threshold signatures (DKG shares) |
ed25519 Keys
Section titled “ed25519 Keys”Used for:
- Transaction signatures — All transactions are signed with ed25519
- Validator network identity — P2P authentication between validators
- Account addresses — Derived from the public key
BLS Keys (Threshold)
Section titled “BLS Keys (Threshold)”BLS12-381 keys are used for threshold cryptography:
- Threshold signatures — 2f+1 validators sign blocks together
- DKG shares — Distributed Key Generation produces per-validator shares
- Sealed transactions — Threshold decryption for MEV protection
BLS keys are typically generated via DKG, not stored directly in the keystore.
CLI Commands
Section titled “CLI Commands”ashen keystore init
Section titled “ashen keystore init”Initialize a new keystore file.
ashen keystore initashen keystore init --path ~/.ashen/custom-keystore.jsonYou will be prompted for a password. The keystore file is created with no keys.
ashen keystore list
Section titled “ashen keystore list”List all keys in the keystore.
ashen keystore listOutput:
Handle Type Label Created Public Key────────────────────────────────────────────────────────────────────────────────a1b2c3d4e5f6... ed25519 my-validator 2026-01-22 10:00:00 0x493615aa...f6e5d4c3b2a1... ed25519 backup-key 2026-01-20 14:30:00 0xdeadbeef...ashen keystore add
Section titled “ashen keystore add”Generate a new random key.
# Generate ed25519 keyashen keystore add --label my-key
# Specify key typeashen keystore add --label my-key --type ed25519ashen keystore import
Section titled “ashen keystore import”Import an existing secret key.
# Import from hexashen keystore add --label imported --secret 0x<64-hex-chars>
# Import from fileashen keystore add --label imported --secret @./secret.keyashen keystore export
Section titled “ashen keystore export”Export a key’s public address.
ashen keystore export --label my-key# Output: 0x493615aa1e16a24f618d3ab6dd93a9250ca76e19996e46493a372c5994862e8cashen keystore remove
Section titled “ashen keystore remove”Remove a key from the keystore.
ashen keystore remove --label my-keyKey References
Section titled “Key References”Keys can be referenced in CLI commands using several formats:
| Format | Example | Description |
|---|---|---|
| Hex | 0xabcd1234... | Raw 64-character hex private key |
| Keystore label | keystore:my-validator | Reference by label |
| Keystore label (short) | ks:my-validator | Short form |
| File path | @./secret.key | Read from file |
| Environment | $ASHEN_PRIVATE_KEY | Environment variable |
Usage Examples
Section titled “Usage Examples”# Using hex directly (not recommended for production)ashen call 0xCONTRACT transfer ... --key 0xabcd1234...
# Using keystore reference (recommended)ashen call 0xCONTRACT transfer ... --key keystore:my-validatorashen call 0xCONTRACT transfer ... --key ks:my-validator
# Using environment variableexport ASHEN_PRIVATE_KEY="keystore:my-validator"ashen call 0xCONTRACT transfer ...
# Validator network keynode run --validator-network-key "keystore:validator-1"Password Management
Section titled “Password Management”Password Input Methods
Section titled “Password Input Methods”| Method | Flag | Security |
|---|---|---|
| Interactive prompt | (default) | Most secure |
| Stdin | --keystore-password-stdin | Good for automation |
| File | --keystore-password-file /path | Moderate (secure the file) |
# Interactive (recommended)ashen keystore list
# From stdin (for scripts)echo "my-password" | ashen keystore list --keystore-password-stdin
# From fileashen keystore list --keystore-password-file ~/.ashen/passwordChanging Passwords
Section titled “Changing Passwords”ashen keystore change-passwordThis re-encrypts all keys with the new password. The old password is required.
Security Best Practices
Section titled “Security Best Practices”- Use a strong password — At least 16 characters, mixed case, numbers, symbols
- Backup the keystore — Store encrypted backups in multiple locations
- Use keystore references — Never put raw private keys in command lines or scripts
- Restrict file permissions — Ensure 0600 permissions on keystore files
- Use separate keys — Different keys for different purposes (validator, testing, etc.)
- Don’t commit secrets — Never put keystore files or passwords in git
- Don’t share passwords — Each operator should have their own credentials
- Don’t use weak passwords — Short or dictionary passwords can be brute-forced
- Don’t disable memory zeroization — The
zeroizecrate is there for a reason - Don’t store passwords in environment — Use stdin or file references instead
Threat Model
Section titled “Threat Model”The keystore protects against:
| Threat | Protection |
|---|---|
| File theft | AES-256 equivalent encryption (XChaCha20) |
| Password brute force | Argon2id with 64MB memory cost |
| Memory scanning | Automatic zeroization on drop |
| Unauthorized file access | Unix 0600 permissions |
| Ciphertext tampering | Poly1305 authentication tag |
| Timing attacks | Constant-time crypto operations |
| Nonce reuse | 192-bit random nonces |
The keystore does NOT protect against:
- Compromised host — If the machine is compromised while the keystore is unlocked
- Weak passwords — A weak password can still be brute-forced offline
- Physical access — Cold boot attacks while the keystore is in memory
Programmatic Usage (Rust)
Section titled “Programmatic Usage (Rust)”use keystore::{Keystore, KeyType};
// Open existing keystorelet ks = Keystore::new("~/.local/share/ashen/keystore/keystore.json");let mut unlocked = ks.open("my-password")?;
// List keysfor key in unlocked.list() { println!("{}: {} ({})", key.label, key.handle, key.key_type);}
// Add a new keylet handle = unlocked.add_key(KeyType::Ed25519, "new-key")?;
// Sign a messagelet signature = unlocked.sign_ed25519(&handle, b"ashen", b"message")?;
// Save changesunlocked.save()?;Default Paths
Section titled “Default Paths”| Platform | Keystore Location |
|---|---|
| Linux | ~/.local/share/ashen/keystore/keystore.json |
| macOS | ~/Library/Application Support/xyz.ashen.ashen/keystore/keystore.json |
| Windows | %APPDATA%\ashen\ashen\keystore\keystore.json |
Override with --path or NODE_KEYSTORE_PATH environment variable.
Related
Section titled “Related”- Configuration — Node configuration including keystore paths
- Using the CLI — Full CLI reference
- Deploying Contracts — Using keys for deployment