Vault Encryption
ZKVAULT's vault encryption subsystem provides military-grade confidential storage with identity-blind access patterns and forward secrecy.
Encryption Algorithms
ChaCha20-Poly1305 (Default)
ChaCha20-Poly1305 is an authenticated encryption with associated data (AEAD) algorithm:
Properties:
• Cipher: ChaCha20 stream cipher (256-bit key)
• MAC: Poly1305 authenticator (128-bit tag)
• Security: IND-CCA2 secure
• Performance: ~3-4 cycles/byte (faster than AES on non-AES-NI CPUs)
Usage:
import { encryptVaultData } from '@zkvault/sdk'
const encrypted = await encryptVaultData(
plaintext,
recipientPublicKey,
{ algorithm: 'chacha20-poly1305' }
)
Output format:
{
ciphertext: Uint8Array, // Encrypted data
nonce: Uint8Array, // 96-bit random nonce
mac: Uint8Array // 128-bit authentication tag
}AES-256-GCM (Alternative)
Properties:
• Cipher: AES in Galois/Counter Mode (256-bit key)
• MAC: GHASH (128-bit tag)
• Security: IND-CCA2 secure
• Performance: ~0.5 cycles/byte (with AES-NI hardware acceleration)
Usage:
const encrypted = await encryptVaultData(
plaintext,
recipientPublicKey,
{ algorithm: 'aes-256-gcm' }
)
// Use when AES-NI available for maximum performanceKey Derivation
HKDF (HMAC-based Key Derivation Function)
// Derive encryption key from master key
function deriveKey(masterKey: Uint8Array, salt: Uint8Array, info: string) {
return HKDF({
hash: 'SHA-256',
ikm: masterKey, // Input key material
salt: salt, // Random salt
info: info, // Context string
length: 32 // Output: 256-bit key
})
}
// Example usage
const masterKey = walletKeypair.secretKey.slice(0, 32)
const salt = crypto.randomBytes(32)
const encryptionKey = deriveKey(masterKey, salt, 'zkvault-v1-encryption')
// Different contexts derive different keys
const authKey = deriveKey(masterKey, salt, 'zkvault-v1-authentication')Elliptic Curve Diffie-Hellman (ECDH)
For public-key encryption with forward secrecy:
import { x25519 } from '@zkvault/sdk/crypto'
// Sender side
const ephemeralKeypair = x25519.generateKeypair()
const sharedSecret = x25519.deriveSharedSecret(
ephemeralKeypair.secretKey,
recipientPublicKey
)
const encryptionKey = deriveKey(sharedSecret, salt, 'zkvault-ecdh')
// Encrypt with derived key
const encrypted = chacha20poly1305.encrypt(plaintext, encryptionKey, nonce)
// Send: (ephemeralPublicKey, encrypted, nonce, mac)
// Recipient side
const sharedSecret = x25519.deriveSharedSecret(
recipientSecretKey,
ephemeralPublicKey
)
const encryptionKey = deriveKey(sharedSecret, salt, 'zkvault-ecdh')
const decrypted = chacha20poly1305.decrypt(encrypted, encryptionKey, nonce, mac)Forward Secrecy
Forward secrecy ensures that past encrypted data remains secure even if keys are compromised:
// Enable forward secrecy
const encrypted = await zkvault.vault.encryptVaultData(
data,
recipientPubkey,
{ forwardSecrecy: true } // Use ephemeral keys
)
// How it works:
1. Generate ephemeral keypair for THIS encryption only
2. Derive shared secret via ECDH
3. Encrypt data with derived key
4. Immediately destroy ephemeral private key
5. Store only (ephemeral_public, ciphertext, nonce, mac)
// Result:
• Decryption requires recipient's long-term private key
• Compromise of ephemeral_public reveals nothing
• Even if recipient's key compromised in future, past ciphertext secure
(attacker needs ephemeral_private which was destroyed)Identity-Blind Encryption
Hide access patterns and data ownership:
// Standard encryption: Reveals recipient identity
encrypted = encrypt(data, recipient_pubkey)
// Stored: (recipient_pubkey, ciphertext) → reveals who can decrypt
// Identity-blind encryption: Hides recipient
const anonymousKey = deriveAnonymousKey(recipient_pubkey, session_id)
encrypted = encrypt(data, anonymousKey)
// Stored: (anonymous_key, ciphertext) → recipient identity hidden
// Only recipient can:
1. Compute anonymousKey from their pubkey + session_id
2. Decrypt using derived key
3. No observer can link ciphertext to recipient
// Implementation
function deriveAnonymousKey(
recipientPubkey: Uint8Array,
sessionId: Uint8Array
): Uint8Array {
return HKDF({
ikm: recipientPubkey,
salt: sessionId,
info: 'zkvault-anonymous-key',
length: 32
})
}Complete Encryption Example
import { ZKVault } from '@zkvault/sdk'
import { Connection, Keypair } from '@solana/web3.js'
async function encryptAndStore() {
const connection = new Connection('https://api.devnet.solana.com')
const wallet = Keypair.generate()
const zkvault = new ZKVault({ connection, wallet, cluster: 'devnet' })
// Data to encrypt
const sensitiveData = {
privateKey: '0x1234...',
balance: 1000000,
lastAccess: Date.now()
}
// Recipient (could be same wallet or different)
const recipient = Keypair.generate()
// Encrypt with all security features
const encrypted = await zkvault.vault.encryptVaultData(
JSON.stringify(sensitiveData),
recipient.publicKey,
{
algorithm: 'chacha20-poly1305',
forwardSecrecy: true,
identityBlind: true,
compress: true // Optional: GZIP compression before encryption
}
)
console.log('Encrypted payload:', {
ciphertext: encrypted.ciphertext.length + ' bytes',
nonce: encrypted.nonce,
ephemeralKey: encrypted.ephemeralPublicKey,
mac: encrypted.mac
})
// Store on-chain (in vault PDA) or off-chain (IPFS, Arweave, etc.)
await storeEncrypted(encrypted)
return encrypted
}
async function decryptAndRetrieve(encrypted: EncryptedPayload) {
const connection = new Connection('https://api.devnet.solana.com')
const recipient = Keypair.generate() // Same keypair used for encryption
const zkvault = new ZKVault({ connection, wallet: recipient, cluster: 'devnet' })
// Decrypt
const decrypted = await zkvault.vault.decryptVaultData(
encrypted,
recipient.secretKey
)
const data = JSON.parse(Buffer.from(decrypted).toString())
console.log('Decrypted data:', data)
return data
}Encryption Performance
| Operation | Algorithm | Time (1 KB) | Time (1 MB) |
|---|---|---|---|
| Encrypt | ChaCha20-Poly1305 | ~0.05ms | ~15ms |
| Encrypt | AES-256-GCM | ~0.02ms | ~8ms |
| Decrypt | ChaCha20-Poly1305 | ~0.05ms | ~15ms |
| Decrypt | AES-256-GCM | ~0.02ms | ~8ms |
| ECDH | x25519 | ~0.08ms | ~0.08ms |
| Key derivation | HKDF-SHA256 | ~0.03ms | ~0.03ms |
Benchmarked on M1 Mac. Times are approximate and vary by hardware.
Security Best Practices
- Always use forward secrecy for sensitive data - Protects against future key compromise
- Never reuse nonces - Each encryption must use unique random nonce
- Verify MAC before decrypting - Prevents timing attacks and tampering
- Rotate keys periodically - Limit exposure window if key compromised
- Use identity-blind encryption for privacy - Hides recipient from observers
- Store keys securely - Hardware wallets, secure enclaves, or HSMs
For more details on security, see Security Model.