Skip to content

KMS envelope encryption

Every share Sigil persists is envelope-encrypted:

  1. A fresh 256-bit DEK is generated per write.
  2. The share is sealed with AES-GCM under the DEK.
  3. The DEK is wrapped by Cloud KMS using the platform KEK (projects/sigilkeys/locations/europe-west1/keyRings/sigil/cryptoKeys/sigil-platform).
  4. Both the wrapped DEK and the AES-GCM ciphertext are stored.
  5. The DEK is wiped from memory.

Bindings

Every Cloud KMS call passes additional_authenticated_data of the form:

organization:<org_id>:wallet:<wallet_id>

If a ciphertext from one wallet is replayed against another wallet’s id, KMS refuses to decrypt — the AAD doesn’t match. This neutralises the class of attacks where a database read leak combined with KMS access could be replayed under a different identity.

The local AES-GCM AAD on the share itself is wallet:<wallet_id>, so the same binding is enforced even before the KMS call.

Recovery purpose

When the recovery share is held by Sigil (the Sigil-managed recovery mode), it is wrapped with a distinct KMS purpose:

organization:<org_id>:wallet:<wallet_id>:purpose:recovery

So the unwrap material for the provider share and the recovery share are distinct — a single key compromise can’t unwrap both.

Operating

Cloud KMS access is gated on the cloudkms.cryptoKeyEncrypterDecrypter role granted to the sigil-api runtime service account. The portal never touches KMS. Migrations never touch KMS. Auditing happens automatically via Cloud KMS’ own audit log in addition to our audit_events table.