Skip to main content

Overview

All sensitive cryptographic operations happen client-side using the Web Crypto API. The client SDK provides functions for key derivation, encryption/decryption, TOTP code generation, and family vault key management.

Key derivation

deriveVaultKey(masterPassword, salt)

Derives a CryptoKey from the master password and user salt using Argon2id.
import { deriveVaultKey } from '@/lib/vault-crypto'

const key = await deriveVaultKey('my-master-password', 'base64url-salt')
// Returns: CryptoKey (AES-256-GCM, non-extractable)
Variants:
FunctionMemoryIterationsUse case
deriveVaultKeyRaw()64 MiB3Default
deriveVaultKeyRawLowMemory()32 MiB4Constrained devices
deriveVaultKeyRawHighMemory()128 MiB2High-security
All variants return Uint8Array (raw key bytes) instead of a CryptoKey.

Encryption and decryption

encryptWithVaultKey(key, plaintext)

Encrypts a string with AES-256-GCM using the vault key.
import { encryptWithVaultKey } from '@/lib/vault-crypto'

const encrypted = await encryptWithVaultKey(vaultKey, 'my-secret-password')
// Returns: { iv: 'base64url...', ciphertext: 'base64url...' }

decryptWithVaultKey(key, payload)

Decrypts an EncryptedPayload back to plaintext.
import { decryptWithVaultKey } from '@/lib/vault-crypto'

const plaintext = await decryptWithVaultKey(vaultKey, {
  iv: 'base64url...',
  ciphertext: 'base64url...'
})
// Returns: 'my-secret-password'

Types

EncryptedPayload

type EncryptedPayload = {
  iv: string          // base64url-encoded 12-byte IV
  ciphertext: string  // base64url-encoded ciphertext + auth tag
}

Cipher constants

const VAULT_CIPHER = {
  ivLengthBytes: 12,
  tagLengthBits: 128,
  keyLengthBits: 256
}

const VAULT_KDF = {
  memoryCostKiB: 65536,        // 64 MiB
  fallbackMemoryCostKiB: 32768, // 32 MiB
  iterations: 3,
  fallbackIterations: 4,
  parallelism: 1,
  hashLengthBytes: 32
}

Salt management

generateVaultSalt()

Generate a cryptographically random salt for vault key derivation.
import { generateVaultSalt } from '@/lib/vault-crypto'

const salt = generateVaultSalt()
// Returns: base64url-encoded random salt

Recovery key

hashRecoveryKey(key)

Hash a recovery key for server-side storage and verification.

encryptVaultKeyWithRecoveryKey(rawKey, recoveryKeyHex)

Wrap the vault key with a recovery key for backup storage.

decryptVaultKeyWithRecoveryKey(blob, recoveryKeyHex)

Recover the vault key using a recovery key.

Family vault keys

wrapFamilyKeyForInvite(familyKey, inviteToken, salt?)

Wrap the family vault key for sharing via an invite token.
import { wrapFamilyKeyForInvite, generateInviteSalt } from '@/lib/vault-crypto'

const salt = generateInviteSalt()
const wrapped = await wrapFamilyKeyForInvite(familyKey, 'invite-token', salt)

unwrapFamilyKey(wrapped, userVaultKey)

Unwrap a family vault key using the user’s vault key.

unwrapFamilyKeyFromInvite(wrapped, inviteToken, salt?)

Unwrap a family vault key from an invite token.

Base64 utilities

import { base64ToUint8Array, uint8ArrayToBase64 } from '@/lib/vault-crypto'

const bytes = base64ToUint8Array('SGVsbG8=')
const b64 = uint8ArrayToBase64(new Uint8Array([72, 101, 108, 108, 111]))

TOTP client

Client-side TOTP code generation for zero-trust 2FA.

generateSecret(bytes?)

Generate a new TOTP secret.
import { generateSecret } from '@/lib/vault-totp-client'

const secret = generateSecret()  // base32-encoded secret

generateTotp(secret, now?, opts?)

Generate a current TOTP code.
import { generateTotp } from '@/lib/vault-totp-client'

const result = generateTotp('BASE32SECRET', Date.now())
// Returns: { token: '847293', periodStart: 1709553600, periodEnd: 1709553630 }
Options: { step: 30, digits: 6, algorithm: 'SHA-1' }

buildOtpauthUrl(options)

Generate an otpauth:// URL for QR code display.
import { buildOtpauthUrl } from '@/lib/vault-totp-client'

const url = buildOtpauthUrl({
  issuer: 'PassAgent',
  account: 'user@example.com',
  secret: 'BASE32SECRET'
})
// Returns: 'otpauth://totp/PassAgent:user@example.com?secret=BASE32SECRET&issuer=PassAgent'