M6-S2 (#199) wrapped the WORM audit and filter-execution-ledger HMAC keys in Zeroizing but did not length-validate them, unlike storage_key. A corrupted or hand-edited vault entry of the wrong length would be silently accepted as a 16/8/whatever-byte HMAC signing key for the tamper-evident WORM log and filter ledger.
This PR:
Changes worm_key/ledger_key on DatabaseEngine from Zeroizing<Vec<u8>> to Zeroizing<[u8; 32]>, matching storage_key (preserves the #199 zeroize-on-drop hardening — no regression).
resolve_engine_keys now try_into()s each key into a [u8; 32] with a distinct DatabaseError::KeyProvisioning message per key ("WORM audit key material must be exactly 32 bytes" / "ledger key material must be exactly 32 bytes") before wrapping in Zeroizing, mirroring the existing storage-key pattern exactly.
Updates the few call sites that pass &self.worm_key/&self.ledger_key into &[u8]-typed parameters (HMAC new_from_slice, verify_worm_chain) to &self.worm_key[..]/&self.ledger_key[..], since &Zeroizing<[u8; 32]> does not auto-coerce to &[u8].
Kept #[allow(clippy::type_complexity)] on resolve_engine_keys — clippy still flags the 3-tuple return type even with all three elements now the same simple type.
Adds two new tests seeding a mock vault with a 16-byte (wrong-length) WORM/ledger key and asserting DatabaseEngine::connect_file fails closed with DatabaseError::KeyProvisioning, mirroring get_or_create_key_bytes_fails_closed_on_corrupt_material but at the engine-construction layer.
nuncio-store unit tests: 61 passed, 0 failed, 0 ignored (includes new db::tests::connect_file_fails_closed_on_wrong_length_worm_key and db::tests::connect_file_fails_closed_on_wrong_length_ledger_key)
Full workspace: all crates green (nuncio-cal 25+2, nuncio-cli 28+4, nuncio-contacts 12+3, nuncio-core 52, nuncio-filter 26+2, nuncio-mail 52+3, nuncio-proto 23, nuncio-store 61, nunciod 117 + 13 E2E integration tests across accounts/calendar/contacts/daemon/filters-triage/grpc-auth/spine), doc-tests all 0/0
Pre-commit hook (runs the same fmt/clippy/test gate) passed on commit
## Summary
Closes #200.
M6-S2 (#199) wrapped the WORM audit and filter-execution-ledger HMAC keys in `Zeroizing` but did not length-validate them, unlike `storage_key`. A corrupted or hand-edited vault entry of the wrong length would be silently accepted as a 16/8/whatever-byte HMAC signing key for the tamper-evident WORM log and filter ledger.
This PR:
- Changes `worm_key`/`ledger_key` on `DatabaseEngine` from `Zeroizing<Vec<u8>>` to `Zeroizing<[u8; 32]>`, matching `storage_key` (preserves the #199 zeroize-on-drop hardening — no regression).
- `resolve_engine_keys` now `try_into()`s each key into a `[u8; 32]` with a distinct `DatabaseError::KeyProvisioning` message per key (`"WORM audit key material must be exactly 32 bytes"` / `"ledger key material must be exactly 32 bytes"`) before wrapping in `Zeroizing`, mirroring the existing storage-key pattern exactly.
- Updates the few call sites that pass `&self.worm_key`/`&self.ledger_key` into `&[u8]`-typed parameters (HMAC `new_from_slice`, `verify_worm_chain`) to `&self.worm_key[..]`/`&self.ledger_key[..]`, since `&Zeroizing<[u8; 32]>` does not auto-coerce to `&[u8]`.
- Kept `#[allow(clippy::type_complexity)]` on `resolve_engine_keys` — clippy still flags the 3-tuple return type even with all three elements now the same simple type.
- Adds two new tests seeding a mock vault with a 16-byte (wrong-length) WORM/ledger key and asserting `DatabaseEngine::connect_file` fails closed with `DatabaseError::KeyProvisioning`, mirroring `get_or_create_key_bytes_fails_closed_on_corrupt_material` but at the engine-construction layer.
## Test plan
- [x] `cargo fmt --all -- --check` — clean
- [x] `cargo check-all` (clippy `--all-targets --workspace -D warnings`) — 0 warnings
- [x] `cargo test-all` — all green, 0 ignored
- `nuncio-store` unit tests: 61 passed, 0 failed, 0 ignored (includes new `db::tests::connect_file_fails_closed_on_wrong_length_worm_key` and `db::tests::connect_file_fails_closed_on_wrong_length_ledger_key`)
- Full workspace: all crates green (nuncio-cal 25+2, nuncio-cli 28+4, nuncio-contacts 12+3, nuncio-core 52, nuncio-filter 26+2, nuncio-mail 52+3, nuncio-proto 23, nuncio-store 61, nunciod 117 + 13 E2E integration tests across accounts/calendar/contacts/daemon/filters-triage/grpc-auth/spine), doc-tests all 0/0
- [x] Pre-commit hook (runs the same fmt/clippy/test gate) passed on commit
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closes #200.
M6-S2 (#199) wrapped the WORM audit and filter-execution-ledger HMAC keys in
Zeroizingbut did not length-validate them, unlikestorage_key. A corrupted or hand-edited vault entry of the wrong length would be silently accepted as a 16/8/whatever-byte HMAC signing key for the tamper-evident WORM log and filter ledger.This PR:
worm_key/ledger_keyonDatabaseEnginefromZeroizing<Vec<u8>>toZeroizing<[u8; 32]>, matchingstorage_key(preserves the #199 zeroize-on-drop hardening — no regression).resolve_engine_keysnowtry_into()s each key into a[u8; 32]with a distinctDatabaseError::KeyProvisioningmessage per key ("WORM audit key material must be exactly 32 bytes"/"ledger key material must be exactly 32 bytes") before wrapping inZeroizing, mirroring the existing storage-key pattern exactly.&self.worm_key/&self.ledger_keyinto&[u8]-typed parameters (HMACnew_from_slice,verify_worm_chain) to&self.worm_key[..]/&self.ledger_key[..], since&Zeroizing<[u8; 32]>does not auto-coerce to&[u8].#[allow(clippy::type_complexity)]onresolve_engine_keys— clippy still flags the 3-tuple return type even with all three elements now the same simple type.DatabaseEngine::connect_filefails closed withDatabaseError::KeyProvisioning, mirroringget_or_create_key_bytes_fails_closed_on_corrupt_materialbut at the engine-construction layer.Test plan
cargo fmt --all -- --check— cleancargo check-all(clippy--all-targets --workspace -D warnings) — 0 warningscargo test-all— all green, 0 ignorednuncio-storeunit tests: 61 passed, 0 failed, 0 ignored (includes newdb::tests::connect_file_fails_closed_on_wrong_length_worm_keyanddb::tests::connect_file_fails_closed_on_wrong_length_ledger_key)