- Added `FilesystemPackRunProvenanceWriter` to write provenance manifests to the filesystem. - Introduced `MongoPackRunArtifactReader` to read artifacts from MongoDB. - Created `MongoPackRunProvenanceWriter` to store provenance manifests in MongoDB. - Developed unit tests for filesystem and MongoDB provenance writers. - Established `ITimelineEventStore` and `ITimelineIngestionService` interfaces for timeline event handling. - Implemented `TimelineIngestionService` to validate and persist timeline events with hashing. - Created PostgreSQL schema and migration scripts for timeline indexing. - Added dependency injection support for timeline indexer services. - Developed tests for timeline ingestion and schema validation.
3.5 KiB
3.5 KiB
Export Center KMS Envelope Pattern (age + AES-GCM)
Status: Adopted for Sprint 0164-0001-0001 (ExportCenter III)
Scope: Defines deterministic envelope handling for mirror bundle encryption (EXPORT-SVC-37-002) and general export signing. Applies to worker path and verification docs.
Key hierarchy
- Content key (DEK): 32-byte random generated per export run. Used for AES-256-GCM over encrypted payloads (
/datasubtree for mirror; optional for others). - Nonce: 12-byte random per file; stored alongside ciphertext; derive Additional Authenticated Data (AAD) as
{runId}:{relativePath}to bind file path and run. - Wrapping keys:
- age recipients (preferred for offline): each tenant can list one or more age public keys. DEK is wrapped once per recipient using age X25519. Store
recipient,wrappedKey(base64), and optionalkeyIdin provenance. - KMS envelope (Authority/HSM): DEK wrapped with tenant-scoped KMS key alias
export/envelope. StorekmsKeyId(authority URI or external ARN) andwrappedKey(base64) plus KMS-providedalgorithm.
- age recipients (preferred for offline): each tenant can list one or more age public keys. DEK is wrapped once per recipient using age X25519. Store
Write path (worker)
- Generate DEK (32 bytes) per run; zeroize after use.
- For each encrypted file, derive AAD =
{runId}:{relativePath}; encrypt with AES-256-GCM (nonce per file). Storenonceandciphertext. - Wrap DEK for all configured recipients:
- age:
age --encrypt --recipient <pub>over DEK bytes → base64. - KMS:
Encrypt/WrapKeywithKeyId=export/envelopeandEncryptionContext={runId,tenant}→ base64.
- age:
- Record wrapping metadata in
provenance.jsonunderenvironment.encryption.recipients[]preserving deterministic order (age recipients lexicographically byrecipient, then KMS entries bykmsKeyId). - Include
encryption.mode(ageoraes-gcm+kms),aadFormat, andnonceFormatin provenance for verification tooling.
Read/verification path
- Select a recipient entry that matches available keys (age private key or KMS key).
- Unwrap DEK:
- age:
age --decrypt→ DEK bytes. - KMS:
Decrypt/UnwrapKeywith same encryption context.
- age:
- For each encrypted file, recompute AAD from
{runId}:{relativePath}, decrypt with AES-256-GCM using storednonce, verify tag. - Recompute SHA-256 of decrypted payload and compare with
export.jsonentries.
Determinism & offline posture
- Recipient lists and wrapped keys are ordered deterministically to keep
provenance.jsonhashes stable across retries. - age path works fully offline; KMS path requires Authority/HSM availability but stores all metadata to allow later decryption once KMS is reachable.
- Use fixed casing and field names:
mode,recipients[] {type, recipient|kmsKeyId, wrappedKey, keyId?}andaadFormat.
Testing notes
- Add regression cases that encrypt/decrypt fixtures with both age and KMS paths, asserting identical manifest/provenance hashes across reruns.
- Ensure decryption fails when AAD does not match expected
{runId}:{relativePath}(prevents path swapping). - Keep tests air-gap friendly: mock KMS wrapper with deterministic stub keys.
Rollout guidance
- Default to age recipients for Offline Kit deployments; enable KMS wrapping where Authority/HSM is reachable.
- Configuration knobs:
ExportCenter:Encryption:Mode=age|kmsExportCenter:Encryption:Recipients= list of age public keysExportCenter:Encryption:KmsKeyId= tenant-specific key alias (when using KMS)
- Documented verification commands should reference this pattern (update CLI/Console guides when payloads change).