Skip to content

Signing Service: Two-Tier Local Key Management #18

@iamyxsh

Description

@iamyxsh

Signing Service: Two-Tier Local Key Management (Encrypted Keyfile + Secure Enclave)

Owner: Yash
Priority: P0 — blocks onchain permit signing
Crate: signer/
Depends on: Credential Vault (#XX), Policy Engine (#XX)
Blocked by: None


Summary

Implement a two-tier signing service for secp256k1 private keys used in EIP-712 permit signing. Users can either import an existing key or generate a fresh wallet during setup. Both paths encrypt immediately — no plaintext ever touches disk.

Tier Mode Protection Availability
Tier 1 encrypted-keyfile Argon2id-derived key + libsodium secretbox All platforms
Tier 2 secure-enclave Tier 1 + encryption key sealed to macOS Secure Enclave hardware macOS with Apple Silicon / T2

There is no fishnet signer export command. The full private key is never retrievable after initial generation or import. If the user loses their master password and SE access, the key is gone. This is by design — same as hardware wallets. Recovery path is:

  1. Use the original key from wherever they sourced it (their wallet backup, or the backup they made during generate)
  2. fishnet signer import or fishnet signer generate for a new key
  3. Call setSigner() on smart wallets if address changed

Acceptance Criteria

  • fishnet init offers choice: import existing key OR generate new wallet
  • Import path: accepts pasted private key, encrypts immediately, no plaintext on disk
  • Generate path: creates secp256k1 keypair, displays full key once, requires backup confirmation before encrypting
  • Tier 1 (encrypted-keyfile) works on Linux and macOS
  • Tier 2 (secure-enclave) auto-detects and activates on supported macOS hardware
  • fishnet signer status shows mode, public address, blurred key, SE binding, last used
  • fishnet signer reveal shows blurred key after master password auth, clears terminal after keypress
  • fishnet signer import replaces existing key, logs event in audit log, warns about onchain impact
  • fishnet signer generate creates new keypair, shows full key once, encrypts after confirmation
  • fishnet signer rotate aliases to import with replace semantics
  • fishnet signer pubkey outputs address for contract deployment
  • Blurred key format: 0x + first 4 hex + •••••••••• + last 4 hex — consistent everywhere
  • Blurred key cached in DB at creation time (no decryption needed to display)
  • Dashboard signer card shows status, address, blurred key behind [reveal], mode badge, color indicator
  • Dashboard settings page supports import, generate, reveal, SE upgrade, and diagnostics
  • Generate flow in dashboard: two-step (generate → confirm), pending key discarded if modal closed
  • All API endpoints except pubkey enforce master password authentication
  • /api/signer/generate is the only endpoint that ever returns a full private key
  • Signing path: decrypt → mlock → sign → zeroize, key in memory < 100ms
  • Argon2id params: 256MB memory cost, 3 iterations
  • fishnet doctor validates signer health and SE binding
  • Unit tests: encrypt/decrypt roundtrip, zeroize verification, generate keypair, blur formatting, SE mock for CI
  • Integration test: init → generate key → sign permit → verify signature matches public address
  • Integration test: init → import key → sign permit → verify signature matches public address

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions