Skip to content

app-storage: deterministic mock key per app_name#72

Merged
jgowdy-godaddy merged 1 commit intomainfrom
fix/mock-deterministic-key
Apr 17, 2026
Merged

app-storage: deterministic mock key per app_name#72
jgowdy-godaddy merged 1 commit intomainfrom
fix/mock-deterministic-key

Conversation

@jgowdy-godaddy
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #71. `ENCLAVEAPP_MOCK_STORAGE=1` path was picking a random AES key per process, which breaks cross-process integration tests (parent encrypts, child spawned by assert_cmd reads — different random key → `aead::Error`). Derive the key deterministically from `app_name` so every process for a given app lands on the same key.

  • Derivation: `SHA-256("enclaveapp-app-storage mock v1\0" || app_name)`. Not secret — mock is test-only.
  • `::new()` / `::default()` still random; `decrypt_wrong_key_fails` still covers that case.

Test plan

  • `cargo clippy --workspace --all-targets -- -D warnings`
  • `cargo test -p enclaveapp-app-storage`
  • `ENCLAVEAPP_MOCK_STORAGE=1 cargo test --workspace -- --test-threads=1` on npmenc — 26/26 integration pass locally in 2.31s

The ENCLAVEAPP_MOCK_STORAGE path added in #71 constructed a fresh
MockEncryptionStorage via `::new()`, which generated a random
AES-256 key. That breaks any test suite whose parent `cargo test`
process writes encrypted state and then spawns a child binary
(e.g. via `assert_cmd::cargo_bin(...)`) that reads it back — the
child's `MockEncryptionStorage` has a different random key and
`decrypt` fails with `aead::Error`.

Derive the mock key deterministically from the app name:
`SHA-256("enclaveapp-app-storage mock v1\0" || app_name)`. Every
process instantiating storage for the same app now lands on the
same AES key, so cross-process tests succeed. The derivation is
explicitly non-secret — anyone with the app name can recompute
it — which is fine because the mock is test-only.

The existing `::new()` / `::default()` random path stays for
consumers that want that semantics, and the
`decrypt_wrong_key_fails` test still covers it.
@jgowdy-godaddy jgowdy-godaddy merged commit 6e04e1b into main Apr 17, 2026
3 checks passed
jgowdy-godaddy pushed a commit that referenced this pull request Apr 17, 2026
#71 made the mock backend always-compiled so ENCLAVEAPP_MOCK_STORAGE
could be honored at runtime without cargo feature gymnastics. That
is a security hole for release binaries: anyone setting the env var
on a production install would downgrade the hardware-backed backend
to an AES-in-RAM mock.

Restore the compile-time gate:
- `mock` cargo feature opt-in, with aes-gcm/rand/sha2 as optional
  deps only pulled in by the feature.
- `mock` module, `MockEncryptionStorage` re-export, `MOCK_STORAGE_ENV`
  constant, and the env-var check inside `create_encryption_storage`
  all gated on `#[cfg(feature = "mock")]`.
- Release builds (no --features mock) have no path to the mock at
  all — setting ENCLAVEAPP_MOCK_STORAGE in production is a no-op.
- Test builds enable the feature via downstream `[dev-dependencies]`
  with `features = ["mock"]`; cargo unifies features during
  `cargo test`, so the env var works in CI as intended.

Keeps #72's deterministic key derivation inside the gated mock
module so cross-process test spawning still works.
jgowdy-godaddy added a commit that referenced this pull request Apr 17, 2026
#71 made the mock backend always-compiled so ENCLAVEAPP_MOCK_STORAGE
could be honored at runtime without cargo feature gymnastics. That
is a security hole for release binaries: anyone setting the env var
on a production install would downgrade the hardware-backed backend
to an AES-in-RAM mock.

Restore the compile-time gate:
- `mock` cargo feature opt-in, with aes-gcm/rand/sha2 as optional
  deps only pulled in by the feature.
- `mock` module, `MockEncryptionStorage` re-export, `MOCK_STORAGE_ENV`
  constant, and the env-var check inside `create_encryption_storage`
  all gated on `#[cfg(feature = "mock")]`.
- Release builds (no --features mock) have no path to the mock at
  all — setting ENCLAVEAPP_MOCK_STORAGE in production is a no-op.
- Test builds enable the feature via downstream `[dev-dependencies]`
  with `features = ["mock"]`; cargo unifies features during
  `cargo test`, so the env var works in CI as intended.

Keeps #72's deterministic key derivation inside the gated mock
module so cross-process test spawning still works.

Co-authored-by: Jay Gowdy <jay@gowdy.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants