chore: add Phase 3 secret-service decomposition tasks
9 atomic tasks for alknet-secret spec conformance and gap closure, derived from architect's implementation review. Dependencies form a 5-generation graph starting with spec update, then parallel implementation tasks, ending with a review gate. Tasks address: DerivedKey zeroize security, key caching with TTL, irpc protocol integration, password derivation, secp256k1/Ethereum derivation, encryption salt/KDF, crypto test vectors, and final spec conformance review.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: spec-update-secret-service
|
||||
name: Update secret-service.md spec to close implementation-identified gaps
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Update `docs/architecture/secret-service.md` to address seven gaps identified during the architect's initial implementation of alknet-secret. The tests pass and the code isn't "wrong," but the spec needs to be brought into alignment with what was learned and with what the implementation needs to be complete.
|
||||
|
||||
This is a **spec-first** task — no code changes here, just documentation. The spec must be complete enough that the subsequent implementation tasks can be picked up without ambiguity.
|
||||
|
||||
### Gaps to Close
|
||||
|
||||
1. **irpc integration undefined** (biggest gap): The spec says `SecretProtocol` uses `#[rpc_requests(message = SecretMessage)]` but doesn't define the concrete wiring. How does a local `SecretServiceHandle` relate to the irpc protocol? What's the `Client<SecretProtocol>` type? Where does the service run (in-process mpsc vs remote QUIC)? The current code has `pub type SecretMessage = SecretProtocol;` as a placeholder.
|
||||
|
||||
2. **Key caching strategy**: The security model table says "derived keys cached in RAM" but never specifies: what's the cache key? What's the TTL? Is it LRU? OQ-SVC-04 was resolved "yes, with TTL default 1 hour" but the spec doesn't reflect this resolution. Current implementation has no caching at all.
|
||||
|
||||
3. **`DerivedKey` security properties**: The struct carries `private_key: Vec<u8>` but doesn't derive `Zeroize`. ADR-038 says all sensitive material must zeroize. The spec should specify this.
|
||||
|
||||
4. **`DerivePassword` specification**: The `SecretProtocol::DerivePassword` variant exists but has no specification for how deterministic password derivation works: what hash function? What encoding? What character set?
|
||||
|
||||
5. **secp256k1/Ethereum derivation**: `DeriveEthereumKey` is in the protocol but the spec doesn't acknowledge that BIP-0032 secp256k1 (path `m/44'/60'/0'/0/0`) requires a fundamentally different derivation algorithm and crate than SLIP-0010 Ed25519.
|
||||
|
||||
6. **Test vectors requirement**: A crypto crate needs known-answer tests against published BIP39/SLIP-0010 test vectors. The spec doesn't reference any.
|
||||
|
||||
7. **`EncryptedData.salt` purpose**: The salt field is generated and stored but not used in key derivation. The spec should either specify that the salt is used in a KDF (PBKDF2/HKDF) to derive the AES key, or document it as reserved for future KDF-based key rotation.
|
||||
|
||||
### Changes to Make
|
||||
|
||||
In `docs/architecture/secret-service.md`:
|
||||
|
||||
- **Section: SecretProtocol irpc Service** — Replace the `SecretProtocol` enum definition with one that includes the irpc integration model: define `SecretServiceHandle` (local, in-process) vs `SecretProtocol` irpc client (remote), clarify the two dispatch paths, and specify that `SecretMessage` is the irpc wire type (generated by `#[rpc_requests]`).
|
||||
|
||||
- **Section: Security Model** — Add a "Key Caching" subsection specifying: derivation path as cache key, TTL of 1 hour (configurable), LRU eviction, cache cleared on `Lock`. Per OQ-SVC-04 resolution.
|
||||
|
||||
- **Section: Key Derivation** — Add a note after the `DerivedKey` struct that `private_key` must derive `Zeroize` per ADR-038. This means `DerivedKey` cannot use `#[derive(Clone)]` directly on the private key field; it needs a custom implementation that zeroizes the source on clone.
|
||||
|
||||
- **Section: Key Derivation** (after derivation paths table) — Add a "Password Derivation" subsection specifying: `DerivePassword` uses HMAC-SHA512 at the derivation path, truncates to `length` bytes, and encodes as Base64url (no special character set in v1). The path format is `m/74'/1'/0'/{hash}'` where `{hash}'` is a site-specific hardened index.
|
||||
|
||||
- **Section: Key Derivation** (after Ethereum path) — Add a "secp256k1 Derivation" note: `DeriveEthereumKey` uses BIP-0032 (not SLIP-0010) at path `m/44'/60'/0'/0/0`, requiring the `libsecp256k1` crate. This is a different derivation algorithm from Ed25519. The `alknet-secret` crate should gate this behind a `secp256k1` feature flag.
|
||||
|
||||
- **Section: AES-256-GCM Encryption** — Specify that `EncryptedData.salt` is currently reserved for future KDF-based key rotation. In v1, the encryption key is derived directly from the seed at path `m/74'/2'/0'/0'` without a salt-based KDF. The `salt` field is included for forward compatibility: when key rotation is implemented, the salt will be used as input to HKDF or PBKDF2 for stretch-based key derivation. For now, the salt is random and stored but not used in key derivation.
|
||||
|
||||
- **New Section: Test Vectors** — Require known-answer tests against:
|
||||
- BIP39 test vectors (mnemonic → seed)
|
||||
- SLIP-0010 test vectors (seed → master key, master key → child key at `m/74'/0'/0'/0'`)
|
||||
- IEEE P802.1ASck test vectors for AES-256-GCM (or equivalent published vectors)
|
||||
|
||||
- **Section: Open Questions** — Mark OQ-SVC-04 as resolved (key caching: yes, TTL default 1 hour, LRU eviction). Add note about OQ-SVC-03 (EncryptedData compatibility: wire format stable, migration path is re-encrypt with new key version).
|
||||
|
||||
- **Dependencies** section — Add `libsecp256k1` (behind `secp256k1` feature flag) and `hkdf`/`pbkdf2` (deferred, for Phase B KDF — listed as future dependency, not current). Update `irpc` line to clarify it's required but the integration model needs specification.
|
||||
|
||||
- **Crate Structure** — Update to include `cache.rs` module for key caching.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `secret-service.md` has a new subsection on irpc integration (local SecretServiceHandle vs remote SecretProtocol client, dispatch paths)
|
||||
- [ ] `secret-service.md` has a "Key Caching" subsection specifying derivation path as cache key, 1-hour TTL, LRU eviction, cleared on Lock
|
||||
- [ ] `secret-service.md` notes that `DerivedKey.private_key` must derive `Zeroize` per ADR-038
|
||||
- [ ] `secret-service.md` has a "Password Derivation" subsection specifying HMAC-SHA512 → Base64url encoding
|
||||
- [ ] `secret-service.md` has a "secp256k1 Derivation" note specifying BIP-0032 algorithm and `secp256k1` feature flag
|
||||
- [ ] `secret-service.md` specifies that `EncryptedData.salt` is reserved for future KDF-based key rotation, not used in v1 key derivation
|
||||
- [ ] `secret-service.md` has a "Test Vectors" section requiring BIP39, SLIP-0010, and AES-256-GCM known-answer tests
|
||||
- [ ] OQ-SVC-04 is marked as resolved in `secret-service.md`
|
||||
- [ ] Dependencies section updated with secp256k1 (feature-gated) and noted future KDF deps
|
||||
- [ ] Crate structure diagram updated to include `cache.rs`
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/secret-service.md — The spec being updated
|
||||
- docs/architecture/decisions/038-seed-lifecycle-memory-security.md — ADR-038 (zeroize requirement)
|
||||
- docs/architecture/decisions/027-crate-decomposition.md — ADR-027 (crate independence)
|
||||
- docs/research/services.md — SecretProtocol definition source
|
||||
- crates/alknet-secret/ — Current implementation (reference for what exists)
|
||||
|
||||
## Notes
|
||||
|
||||
> This is a spec-only task. No code changes. The goal is to make the spec complete enough that subsequent implementation tasks have zero ambiguity about what to build.
|
||||
|
||||
> The architect's message (msg_eacbd63b2001DsTCHZn04meEpB) identified exactly these 7 gaps. This task closes them in the spec so that the implementation tasks can be done against a single source of truth.
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in New Issue
Block a user