sal-modular/docs/vault_impl_plan.md

9.4 KiB

Vault Implementation Plan (Technical Appendix)

This document is a technical reference for contributors and maintainers of the Vault crate. It covers advanced implementation details, design rationale, and data models. For a high-level overview and usage, see vault.md and architecture.md.


Table of Contents


Design Principle: The vault crate will provide both a stateless (context-passing) API and an ergonomic session-based API. This ensures maximum flexibility for both library developers and application builders, supporting both functional and stateful usage patterns.

Design Principle: Stateless & Session APIs

The vault crate is a modular, async, and WASM-compatible cryptographic keystore. It manages an encrypted keyspace (multiple keypairs), provides cryptographic APIs, and persists all data via the kvstore trait. The design ensures all sensitive material is encrypted at rest and is portable across native and browser environments.

Core Components:

  • Vault: Main manager for encrypted keyspace and cryptographic operations.
  • KeyPair: Represents individual asymmetric keypairs (e.g., secp256k1, Ed25519).
  • Symmetric Encryption Module: Handles encryption/decryption and key derivation.
  • SessionManager (Optional): Maintains current context (e.g., selected keypair) for user sessions.
  • KVStore: Async trait for backend-agnostic persistence (sled on native, IndexedDB on WASM).

You can design the vault crate to support both stateless and session-based (stateful) usage patterns. This gives maximum flexibility to both library developers and application builders.

Stateless API

  • All operations require explicit context (unlocked keyspace, keypair, etc.) as arguments.
  • No hidden or global state; maximally testable and concurrency-friendly.
  • Example:
    let keyspace = vault.unlock_keyspace("personal", b"password").await?;
    let signature = keyspace.sign("key1", &msg).await?;
    

Session Manager API

  • Maintains in-memory state of unlocked keyspaces and current selections.
  • Provides ergonomic methods for interactive apps (CLI, desktop, browser).
  • Example:
    let mut session = SessionManager::new();
    session.unlock_keyspace("personal", b"password", &vault)?;
    session.select_keypair("key1");
    let signature = session.current_keypair().unwrap().sign(&msg)?;
    session.logout(); // wipes all secrets from memory
    

How They Work Together

  • The stateless API is the core, always available and used internally by the session manager.
  • The session manager is a thin, optional layer that wraps the stateless API for convenience.
  • Applications can choose which pattern fits their needs, or even mix both (e.g., use stateless for background jobs, session manager for user sessions).

Benefits

  • Flexibility: Library users can pick the best model for their use case.
  • Security: Session manager can enforce auto-lock, timeouts, and secure memory wiping.
  • Simplicity: Stateless API is easy to test and reason about, while session manager improves UX for interactive flows.

Commitment: Provide Both APIs

  • Both stateless and session-based APIs will be provided in the vault crate.
  • Stateless API: For backend, automation, or library contexts—explicit, functional, and concurrency-friendly.
  • Session manager API: For UI/UX-focused applications—ergonomic, stateful, and user-friendly.

Data Model

VaultMetadata & Keyspace Model

struct VaultMetadata {
    name: String,
    keyspaces: Vec<KeyspaceMetadata>,
    // ... other vault-level metadata (optionally encrypted)
}

struct KeyspaceMetadata {
    name: String,
    salt: [u8; 16], // Unique salt for this keyspace
    encrypted_blob: Vec<u8>, // All keypairs & secrets, encrypted with keyspace password
    // ... other keyspace metadata
}

// The decrypted contents of a keyspace:
struct KeyspaceData {
    keypairs: Vec<KeyEntry>,
    // ... other keyspace-level metadata
}

struct KeyEntry {
    id: String,
    key_type: KeyType,
    private_key: Vec<u8>, // Only present in memory after decryption
    public_key: Vec<u8>,
    metadata: Option<KeyMetadata>,
}

enum KeyType {
    Secp256k1,
    Ed25519,
    // ...
}
  • The vault contains a list of keyspaces, each with its own salt and encrypted blob.
  • Each keyspace is unlocked independently using its password and salt.
  • Key material is never stored unencrypted; only decrypted in memory after unlocking a keyspace.

3. API Design (Keyspace Model)

Vault

impl<S: KVStore + Send + Sync> Vault<S> {
    async fn open(store: S) -> Result<Self, VaultError>;
    async fn list_keyspaces(&self) -> Result<Vec<KeyspaceInfo>, VaultError>;
    async fn create_keyspace(&mut self, name: &str, password: &[u8]) -> Result<(), VaultError>;
    async fn delete_keyspace(&mut self, name: &str) -> Result<(), VaultError>;
    async fn unlock_keyspace(&mut self, name: &str, password: &[u8]) -> Result<(), VaultError>;
    async fn lock_keyspace(&mut self, name: &str);
    // ...
}

Keyspace Management

impl Keyspace {
    fn is_unlocked(&self) -> bool;
    fn name(&self) -> &str;
    async fn create_key(&mut self, key_type: KeyType, name: &str) -> Result<String, VaultError>;
    async fn list_keys(&self) -> Result<Vec<KeyInfo>, VaultError>;
    async fn sign(&self, key_id: &str, msg: &[u8]) -> Result<Signature, VaultError>;
    async fn encrypt(&self, key_id: &str, plaintext: &[u8]) -> Result<Ciphertext, VaultError>;
    async fn decrypt(&self, key_id: &str, ciphertext: &[u8]) -> Result<Vec<u8>, VaultError>;
    async fn change_password(&mut self, old: &[u8], new: &[u8]) -> Result<(), VaultError>;
    // ...
}

SessionManager

impl SessionManager {
    fn select_key(&mut self, key_id: &str);
    fn current_key(&self) -> Option<&KeyPair>;
}
vault/
├── src/
│   ├── lib.rs            # Vault API and main logic
│   ├── data.rs           # Data models: VaultData, KeyEntry, etc.
│   ├── crypto.rs         # Symmetric/asymmetric crypto, key derivation
│   ├── session.rs        # SessionManager
│   ├── error.rs          # VaultError and error handling
│   └── utils.rs          # Helpers, serialization, etc.
├── tests/
│   ├── native.rs         # Native (sled) tests
│   └── wasm.rs           # WASM (IndexedDB) tests
└── ...

Advanced Notes

  • For further context on cryptographic choices, async patterns, and WASM compatibility, see architecture.md.
  • This appendix is intended for developers extending or maintaining the Vault implementation.

Cryptography: Crates and Algorithms

Crates:

  • aes-gcm: AES-GCM authenticated encryption (WASM-compatible)
  • chacha20poly1305: ChaCha20Poly1305 authenticated encryption (WASM-compatible)
  • pbkdf2: Password-based key derivation (WASM-compatible)
  • scrypt: Alternative KDF, strong and WASM-compatible
  • k256: secp256k1 ECDSA (Ethereum keys)
  • ed25519-dalek: Ed25519 keypairs
  • rand_core: Randomness, WASM-compatible
  • getrandom: Platform-agnostic RNG

Algorithm Choices:

  • Vault Encryption:
    • AES-256-GCM (default, via aes-gcm)
    • Optionally ChaCha20Poly1305 (via chacha20poly1305)
  • Password Key Derivation:
    • PBKDF2-HMAC-SHA256 (via pbkdf2)
    • Optionally scrypt (via scrypt)
  • Asymmetric Keypairs:
    • secp256k1 (via k256) for Ethereum/EVM
    • Ed25519 (via ed25519-dalek) for general-purpose signatures
  • Randomness:
    • Use rand_core and getrandom for secure RNG in both native and WASM

Feature-to-Algorithm Mapping:

Feature Crate(s) Algorithm(s)
Vault encryption aes-gcm, chacha20poly1305 AES-256-GCM, ChaCha20Poly1305
Password KDF pbkdf2, scrypt PBKDF2-HMAC-SHA256, scrypt
Symmetric encryption aes-gcm, chacha20poly1305 AES-256-GCM, ChaCha20Poly1305
secp256k1 keypairs k256 secp256k1 ECDSA
Ed25519 keypairs ed25519-dalek Ed25519
Randomness rand_core, getrandom OS RNG

7. WASM & Native Considerations

  • Use only WASM-compatible crypto crates (aes-gcm, chacha20poly1305, k256, ed25519-dalek, etc).
  • Use wasm-bindgen/wasm-bindgen-futures for browser interop.
  • Use tokio::task::spawn_blocking for blocking crypto on native.
  • All APIs are async and runtime-agnostic.

6. Future Extensions

  • Multi-user vaults (multi-password, access control)
  • Hardware-backed key storage (YubiKey, WebAuthn)
  • Key rotation and auditing
  • Pluggable crypto algorithms
  • Advanced metadata and tagging

7. References

  • See docs/Architecture.md and docs/kvstore-vault-architecture.md for high-level design and rationale.
  • Crypto patterns inspired by industry best practices (e.g., Wire, Signal, Bitwarden).