feat: add governed local content encryption provider

This commit is contained in:
2026-08-02 03:40:50 +02:00
parent 858c41d5ad
commit 42f35f8d00
11 changed files with 2318 additions and 26 deletions
+48 -8
View File
@@ -5,11 +5,16 @@
`govoplan-encryption` is the optional governance and lifecycle boundary for
cryptographic protection. It persists opaque provider references, versioned
content-protection envelopes, idempotent operation state, recovery decisions,
and disable-preflight evidence. It does not ship a cipher, a raw key store, a
KMS/HSM driver, or a client E2EE protocol.
and disable-preflight evidence. It ships a bounded `local_aesgcm` server-envelope
provider. The provider uses AES-256-GCM, wraps generated vault keys under a key
derived from deployment `MASTER_KEY_B64`, wraps one generated content key per
envelope, and persists only wrapped keys, nonces, keyed commitments, ciphertext
digests, and opaque references
in shared SQL state. It does not ship a KMS/HSM driver or client E2EE protocol.
Enabling the module is therefore not an encryption or E2EE claim. A deployment
may make such a claim only for a named protection profile whose concrete
Selecting the bundled profile is an authenticated encryption-at-rest claim for
the covered payload, not an E2EE, endpoint-security, HSM/KMS, or legal
certification claim. Stronger claims require a named protection profile whose
provider, clients, algorithms, backup procedure, and failure tests have passed
the profile's conformance and security review.
@@ -72,6 +77,31 @@ encryption hides this metadata.
## Deployment Profiles
### Bundled local AES-GCM reference provider
`local_aesgcm` is a concrete server-side envelope provider intended for bounded
deployments and integration verification. A per-deployment wrapping key is
derived with HKDF-SHA-256 from `MASTER_KEY_B64`; the master key itself is never
stored in the database. Vault keys and content keys are generated with the
operating-system CSPRNG and wrapped with AES-256-GCM. Every content ciphertext
authenticates a canonical tenant, owner, resource, profile, provider, vault,
key-version, and algorithm context.
The provider stores no unkeyed plaintext digest. Its internal plaintext
commitment is keyed by deployment custody and bound to the authenticated
resource context, preventing an offline database reader from enumerating
low-entropy values such as ballot choices.
The provider supports idempotent provision, rotation, revocation, destruction
scheduling, content encryption/decryption, and same-provider key rewrap. A
revoked vault key cannot protect new content but can open existing content until
destruction. Due destruction clears wrapped vault material and causes reads to
fail closed. Payload re-encryption is an explicit two-phase owner-coordinated
workflow: Encryption decrypts and prepares an idempotent target ciphertext and
exact envelope, while the owner must durably replace its payload before it
records the migration as successful. This also permits cross-provider or
algorithm migration without giving Encryption ownership of the payload store.
### Server-side envelope encryption
The application or a deployment KMS/HSM can unwrap data keys after normal
@@ -143,9 +173,15 @@ operation as `prepared`, `succeeded`, or `outcome_unknown`. If a provider call
raises or a process dies around the external side effect, absence of an error is
never treated as success. Reconciliation repeats the exact idempotent provider
request and records only bounded error class codes.
An `outcome_unknown` content migration may subsequently be resolved to success
or rejection; it is not an immutable terminal state.
Content migrations are explicit records. Rewrap and re-encryption succeed only
when an exact target envelope references the authorized source and migration.
Re-encryption preparation leaves the source in `migrating` and does not register
the active target envelope. Repeating it with the same migration and target
reference returns identical authenticated ciphertext. Only the owner's
evidence-backed outcome call activates the target and retires the source.
Decrypt, export, and destruction require evidence references. Until an outcome
is known, the source remains `migration_pending`; no implicit plaintext fallback
is allowed.
@@ -206,7 +242,11 @@ They resolve Encryption through optional capabilities and must fail closed when
an encrypted record is encountered without the capability. Unencrypted module
combinations continue to operate without Encryption.
Focused fixtures currently validate Files- and Postbox-owned envelope records.
Concrete feature adapters and production provider conformance remain tracked
work; direct imports of Encryption database models from feature modules are not
allowed.
Focused fixtures validate Files and Postbox owner adapters: plaintext operation
continues without Encryption, configured encrypted writes store ciphertext and
an envelope link, encrypted reads fail closed when the capability is absent,
and feature modules do not import Encryption models or providers. Files keeps
plaintext semantic checksums separate from stored-ciphertext integrity and uses
the protection profile in its deduplication identity. Postbox encrypts message
bodies while subjects, routing, participant, and attachment-reference metadata
remain observable.