tasks: decompose vault, core, call crates into 28 atomic implementation tasks
Break down the three initial crates (alknet-vault, alknet-core, alknet-call) into dependency-ordered task files for implementation agents. Structure: - tasks/vault/ (10 tasks) — drift fixes from ADR-025/026 refactor, review, spec sync. Vault is independent and can run fully in parallel with core/call. - tasks/core/ (6 tasks) — crate init, core types, config, auth, endpoint, review. Core is foundational; call depends on it. - tasks/call/ (12 tasks) — split into registry/ and protocol/ topic subdirs reflecting the two subsystems. CallAdapter is the merge point. Key decisions: - Drifts 3+9+10 grouped as one task (key-versioning-rotation) — the complete ADR-021 rotation feature that doesn't compile in pieces - Reviews injected at end of each crate phase (vault, core, call) - Vault spec-sync task removes the drift table and bumps doc status to stable - ACME deferred in core/endpoint (noted as TODO; X509 manual certs for now) - OperationEnv kept as a trait (load-bearing for ADR-024 layering) Validated: 28 tasks, no cycles, 11 generations of parallel work. Critical path runs through call (11 tasks). Vault completes by generation 4. 6 high-risk tasks identified (21%): irpc-removal, endpoint, operation-context, operation-env, call-adapter, abort-cascade.
This commit is contained in:
85
tasks/vault/cache-zeroization-test.md
Normal file
85
tasks/vault/cache-zeroization-test.md
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: vault/cache-zeroization-test
|
||||
name: Verify and test that HashMap::clear() drops CachedKey values triggering zeroization
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Fix drift item #6: `KeyCache::clear()` removes entries and relies on
|
||||
`CachedKey`'s `Drop` impl for zeroization. The spec says to verify that
|
||||
`HashMap::clear()` actually drops the values (it does, but this is worth a
|
||||
test). This task adds a test that proves zeroization happens on cache eviction
|
||||
and clear.
|
||||
|
||||
### Background
|
||||
|
||||
`CachedKey` derives `Zeroize` and `ZeroizeOnDrop` (via the `DerivedKey` it
|
||||
holds, which is `#[zeroize(drop)]`). When the cache evicts an entry (LRU or TTL)
|
||||
or `clear()` is called, the `CachedKey` is dropped, which triggers
|
||||
`ZeroizeOnDrop` — the private key bytes are zeroized before deallocation.
|
||||
|
||||
`HashMap::clear()` drops all values, which triggers their `Drop` impls. This
|
||||
is standard Rust behavior, but the security-critical nature of key material
|
||||
warrants an explicit test.
|
||||
|
||||
### What to add
|
||||
|
||||
A test in `cache.rs` (or `tests/`) that:
|
||||
|
||||
1. Inserts a `CachedKey` with a known private key into the cache
|
||||
2. Verifies the key is present
|
||||
3. Calls `clear()` (or evicts via LRU/TTL)
|
||||
4. Verifies the `CachedKey` was dropped and zeroized
|
||||
|
||||
Testing zeroization directly is tricky because the memory is freed — you can't
|
||||
easily inspect it after drop. A practical approach:
|
||||
|
||||
- **Option A**: Use a custom type with a `Drop` impl that sets a flag (e.g., an
|
||||
`Arc<AtomicBool>`) when zeroized. Insert it into the cache, clear, verify the
|
||||
flag is set. This tests the drop path, not the zeroize path directly, but
|
||||
confirms `clear()` drops values.
|
||||
- **Option B**: Test the LRU eviction path — fill the cache to `max_entries`,
|
||||
insert one more, verify the LRU entry was evicted (dropped).
|
||||
- **Option C**: Test that `lock()` calls `cache.clear()` and the cache is empty
|
||||
afterward (integration test via `VaultServiceHandle`).
|
||||
|
||||
At minimum, implement Option B and C. Option A is a bonus if feasible without
|
||||
over-engineering the test type.
|
||||
|
||||
### Scope
|
||||
|
||||
This task touches `cache.rs` (test additions) and possibly `tests/`. It does
|
||||
not depend on the irpc removal task (drift #4) because `cache.rs` is a separate
|
||||
file. It can run in parallel with drift #4.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Test: LRU eviction drops the evicted `CachedKey` (cache exceeds `max_entries`, oldest evicted)
|
||||
- [ ] Test: `lock()` clears the cache (verify cache is empty after lock)
|
||||
- [ ] Test: TTL expiry evicts entries (set short TTL, wait, verify entry gone)
|
||||
- [ ] Test: `clear()` removes all entries (verify empty after clear)
|
||||
- [ ] `cargo test` succeeds
|
||||
- [ ] `cargo clippy` succeeds with no warnings
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/crates/vault/README.md — Known Source Drift table item #6
|
||||
- docs/architecture/crates/vault/service.md — Cache section, Security Constraints
|
||||
- docs/architecture/crates/vault/encryption.md — Security Constraints
|
||||
|
||||
## Notes
|
||||
|
||||
> `HashMap::clear()` does drop values, triggering their `Drop` impls. This is
|
||||
> standard Rust behavior, but key material is security-critical enough to
|
||||
> warrant an explicit test. This task touches only `cache.rs` and can run in
|
||||
> parallel with the irpc removal task (drift #4).
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in New Issue
Block a user