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 performance

Key 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

OperationAlgorithmTime (1 KB)Time (1 MB)
EncryptChaCha20-Poly1305~0.05ms~15ms
EncryptAES-256-GCM~0.02ms~8ms
DecryptChaCha20-Poly1305~0.05ms~15ms
DecryptAES-256-GCM~0.02ms~8ms
ECDHx25519~0.08ms~0.08ms
Key derivationHKDF-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.