Closes Blue audit B4. Four nonce-binding gaps in the ReleaseGate are
closed in one pass:
1. Nonce derivation is now HMAC-SHA256(rootKey, "kms/release/v1" ||
epoch || jobID || rand32). A nonce issued for jobID A cannot be
replayed under jobID B even if the random component is identical.
2. Issue() persists (jobID, nonce, epoch, expires) in a NonceStore.
Release() looks up the stored nonce and constant-time-compares it
against req.Nonce. The forged-nonce path now refuses with
ErrNonceMismatch instead of silently accepting any 32 bytes.
3. AAD now binds the issued nonce: epoch || jobID || teePub || nonce.
Two Release() calls with the same (epoch, jobID) but different
issuances produce distinct AADs; sealed keys are non-fungible
across issuances even after a process restart.
4. State (epoch + pending nonces + consumed-set) is durable via the
NonceStore interface. Two implementations:
- MemoryNonceStore for tests / single-shot dev
- DatabaseNonceStore for prod (luxfi/database; pair with encdb +
ZapDB for at-rest encryption per Hanzo Base pattern)
Replay window default 1h; pending TTL 1h. GC reclaims both.
Tests cover all five required scenarios + nonce-binding + AAD-binding
asserts. -race clean.
Backwards-incompatible change to NewLocalReleaseGate / NewMPCReleaseGate
/ NewHSMReleaseGate signatures; no compat shim per audit guidance.
Backend:
- MFA TOTP via pquerna/otp: generate secret, verify code, enforce at login
- KMS client: real AES-256-GCM encryption with Argon2id key derivation
- Smart wallet handlers: propose/execute Safe tx, submit UserOp via DB
- 9 new KMS tests, all 27 API tests passing
Frontend:
- Dashboard home fetches real vault/wallet counts, transactions, cluster status
- User menu shows actual logged-in email instead of hardcoded placeholder
- Reshare page connected to real API
- Smart wallet detail connected to real API (propose, execute, user-op)
- Loading/error states throughout