feat: Implement Filesystem and MongoDB provenance writers for PackRun execution context
- 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.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# 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 (`/data` subtree 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 optional `keyId` in provenance.
|
||||
- **KMS envelope** (Authority/HSM): DEK wrapped with tenant-scoped KMS key alias `export/envelope`. Store `kmsKeyId` (authority URI or external ARN) and `wrappedKey` (base64) plus KMS-provided `algorithm`.
|
||||
|
||||
## Write path (worker)
|
||||
1) Generate DEK (32 bytes) per run; zeroize after use.
|
||||
2) For each encrypted file, derive AAD = `{runId}:{relativePath}`; encrypt with AES-256-GCM (nonce per file). Store `nonce` and `ciphertext`.
|
||||
3) Wrap DEK for all configured recipients:
|
||||
- age: `age --encrypt --recipient <pub>` over DEK bytes → base64.
|
||||
- KMS: `Encrypt`/`WrapKey` with `KeyId=export/envelope` and `EncryptionContext={runId,tenant}` → base64.
|
||||
4) Record wrapping metadata in `provenance.json` under `environment.encryption.recipients[]` preserving deterministic order (age recipients lexicographically by `recipient`, then KMS entries by `kmsKeyId`).
|
||||
5) Include `encryption.mode` (`age` or `aes-gcm+kms`), `aadFormat`, and `nonceFormat` in provenance for verification tooling.
|
||||
|
||||
## Read/verification path
|
||||
1) Select a recipient entry that matches available keys (age private key or KMS key).
|
||||
2) Unwrap DEK:
|
||||
- age: `age --decrypt` → DEK bytes.
|
||||
- KMS: `Decrypt`/`UnwrapKey` with same encryption context.
|
||||
3) For each encrypted file, recompute AAD from `{runId}:{relativePath}`, decrypt with AES-256-GCM using stored `nonce`, verify tag.
|
||||
4) Recompute SHA-256 of decrypted payload and compare with `export.json` entries.
|
||||
|
||||
## Determinism & offline posture
|
||||
- Recipient lists and wrapped keys are ordered deterministically to keep `provenance.json` hashes 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?}` and `aadFormat`.
|
||||
|
||||
## 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` | `kms`
|
||||
- `ExportCenter:Encryption:Recipients` = list of age public keys
|
||||
- `ExportCenter:Encryption:KmsKeyId` = tenant-specific key alias (when using KMS)
|
||||
- Documented verification commands should reference this pattern (update CLI/Console guides when payloads change).
|
||||
Reference in New Issue
Block a user