KMS envelope encryption
Every share Sigil persists is envelope-encrypted:
- A fresh 256-bit DEK is generated per write.
- The share is sealed with AES-GCM under the DEK.
- The DEK is wrapped by Cloud KMS using the platform KEK
(
projects/sigilkeys/locations/europe-west1/keyRings/sigil/cryptoKeys/sigil-platform). - Both the wrapped DEK and the AES-GCM ciphertext are stored.
- 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:recoverySo 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.