Skip to main content
Security

Your data, protected

AES‑256‑GCM for data at rest with envelope encryption. Versioned blobs include IV and authentication tag.

AES-256-GCM at rest • Master-key envelope • Authenticated encryption (GCM)

At a glance

  • Data at rest: AES‑256‑GCM (authenticated encryption)
  • Per‑record data key: 32 bytes (random)
  • Envelope encryption: data key wrapped with a 32‑byte master key via AES‑256‑GCM
  • IV: 12 bytes per NIST recommendation for GCM
  • Tag: 16 bytes (GCM authentication tag)
  • Blobs: versioned; we currently use version 0x01

How it works

  1. Generate a 32‑byte data key for the record.
  2. Encrypt the record with AES‑256‑GCM using that data key.
  3. Envelope encrypt the data key using a 32‑byte master key (from ENCRYPTION_MASTER_KEY).
  4. Store: encrypted record + wrapped data key.

Master key format: either 32‑byte base64 or 64‑hex characters. We include a 1‑byte version prefix to allow safe future changes.

Blob layout

  • 0..1: version (0x01)
  • 1..13: IV (12 bytes)
  • 13..29: tag (16 bytes)
  • 29..N: ciphertext
base64(version || iv || tag || ciphertext)

Redacted example

Example of a versioned, envelope‑encrypted data key blob (base64):

AQAAAAAAAAAAABCD1234AAAAAAAAAAAAAAABCD1234••••••••••••••••••••••••••••••••••••

Note: contents are illustrative; sensitive portions are redacted.

Try it live (client-side demo)

Run a local encryption to see exactly how records are encrypted and keys are wrapped. This uses your browser’s Web Crypto API (AES‑GCM); no data leaves your device.

Using temporary in-browser master key
Decrypt to Verify

Demo runs fully in your browser using Web Crypto (AES‑GCM). No data leaves your device. In production, the master key never reaches the browser, so blobs are undecryptable without server access.

Server code examples

These helpers live in lib/crypto.ts and are used by the worker when storing credentials securely.

import { generateDataKey, encryptWithDataKey, envelopeEncryptDataKey, envelopeDecryptDataKey, decryptWithDataKey } from '@/lib/crypto' // Encrypt and wrap const dataKey = generateDataKey() const dataBlobB64 = encryptWithDataKey(dataKey, JSON.stringify(record)) const wrappedKeyB64 = envelopeEncryptDataKey(dataKey) // Later: unwrap and decrypt const unwrappedKey = envelopeDecryptDataKey(wrappedKeyB64) const plaintext = decryptWithDataKey(unwrappedKey, dataBlobB64)

Responsible disclosure

If you believe you’ve found a security issue, please reach out at security@idlemat.es.

See also our Privacy Policy and Terms of Service.