Files
git.stella-ops.org/docs/modules/export-center/operations/kms-envelope-pattern.md
StellaOps Bot 17d45a6d30
Some checks failed
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
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.
2025-11-30 15:38:14 +02:00

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 (/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).