CloudCruise’s credential vault adopts the envelope‑encryption pattern recommended by NIST SP 800‑57 and mirrors proven designs used by services like AWS Secrets Manager and Google Secret Manager. Plaintext credentials are never persisted or transmitted to our servers. This page outlines the key design choices behind that guarantee.

Key hierarchy

  • Per‑user data key (AES‑256‑GCM) – generated the first time a user stores a secret.
    • Stored only in encrypted form.
    • Can be rotated at any time from the dashboard.
  • Vault master key – a 256‑bit key held in AWS KMS/HSM.
    • Used solely to encrypt/decrypt each user’s data key.
    • Stored as a Kubernetes Secret

Secret ingestion flow

  1. Client encrypts the credential locally with their data key (see Recommended client encryption below).
  2. The ciphertext is sent to the vault. The vault verifies the envelope and stores it verbatim.
  3. The vault keeps a mapping: user‑id → { encrypted_data_key, ciphertext, metadata }.

The vault rejects any credential that is not already encrypted with the correct data key for that user.

Runtime access

When a browser agent needs a credential:

  1. The agent requests the ciphertext.
  2. The vault decrypts the data key entirely in‑memory using the master key mounted from the Kubernetes Secret.
  3. The agent decrypts the credential in memory, uses it, and immediately zeroises the buffer.
  4. No decrypted value is logged, cached, or exported.

Key rotation

Users may rotate their data key at any time. The vault automatically:

  1. Generates a new data key.
  2. Decrypts each stored secret with the old key within a secure enclave.
  3. Re‑encrypts the secret with the new key and updates metadata.
  4. Shreds the old key material.
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import binascii, json

def encrypt_message(message: dict, key_hex: str) -> str:
    key = binascii.unhexlify(key_hex)  # 32‑byte AES‑256 key
    iv = get_random_bytes(12)          # 96‑bit IV for GCM
    serialized = json.dumps(message)   # ensure deterministic encoding
    cipher = AES.new(key, AES.MODE_GCM, iv)
    ciphertext, tag = cipher.encrypt_and_digest(serialized.encode())
    return iv.hex() + ciphertext.hex() + tag.hex()

We use AES‑256‑GCM because it provides confidentiality, integrity, and built‑in authentication (via the tag) without requiring a separate MAC.

Threat model & mitigations

ThreatMitigation
Database breachSecrets remain encrypted with per‑user keys; attacker lacks the data keys.
Compromise of a single data keyBlast radius limited to that user only.
Master key exposureMaster key stored in KMS with hardware isolation & strict IAM; usage logged and alerted.
Replay/tamperingAES‑GCM tag validation prevents bit‑flips or stale ciphertext from being accepted.

For security questions or key‑rotation assistance, email founders@cloudcruise.com.