Step 18: FIDO2 support with interface and mock.

FIDO2Device interface abstracts hardware interaction (Register, Derive,
Available, MatchesCredential). Real libfido2 implementation deferred;
mock device used for full test coverage.

AddFIDO2Slot: registers FIDO2 credential, derives KEK via HMAC-secret,
wraps DEK, adds fido2/<label> slot to manifest.

UnlockDEK: tries all fido2/* slots first (checks credential_id against
connected device), falls back to passphrase. User never specifies
which method.

6 tests: add slot, reject duplicate, unlock via FIDO2, fallback to
passphrase when device unavailable, slot persistence, encrypted
round-trip unlocked via FIDO2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 09:15:20 -07:00
parent 3b961b5d8a
commit 5bb65795c8
5 changed files with 443 additions and 11 deletions

View File

@@ -197,11 +197,10 @@ Depends on Steps 13, 14.
Depends on Step 17.
- [ ] `garden/encrypt_fido2.go`: FIDO2 hmac-secret KEK derivation via libfido2
- [ ] `garden/encrypt.go`: extend UnlockDEK to try fido2/* slots first (check credential_id against connected devices), fall back to passphrase
- [ ] `garden/encrypt.go`: `AddFIDO2Slot(label string) error` — unlock DEK via existing slot, register FIDO2 credential, wrap DEK, add slot to manifest
- [ ] Tests: FIDO2 slot add/unwrap (may need mock or skip on CI without hardware)
- [ ] Verify: `go test ./... && go vet ./... && golangci-lint run ./...`
- [x] `garden/encrypt_fido2.go`: FIDO2Device interface, AddFIDO2Slot, unlockFIDO2, defaultFIDO2Label
- [x] `garden/encrypt.go`: UnlockDEK tries fido2/* slots first (credential_id matching), falls back to passphrase
- [x] `garden/encrypt_fido2_test.go`: mock FIDO2 device, 6 tests (add slot, duplicate rejected, unlock via FIDO2, fallback to passphrase, persistence, encrypted round-trip with FIDO2)
- [x] Verify: `go test ./... && go vet ./... && golangci-lint run ./...`
### Step 19: Encryption CLI + Slot Management