feat: add governed local content encryption provider
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user