Skip to content

Step 1c: device-key authentication for /dev/* signer endpoints (follow-up to #74) #76

@hanwencheng

Description

@hanwencheng

Background

PR #75 landed issue #74 step 1: the dev_key_service signer with the /dev/derive-address + /dev/sign-message wire contract per docs/spec/signer-protocol.md. That step ships the signer with no HTTP-layer auth (loopback-only assumption from signer-protocol.md §"What's intentionally out of scope at v0").

A follow-up (step 1b, separate issue) deploys signer.litentry.org as an independent listener with bearer-JWT verification — strict improvement over today, but the broker becomes a single point of compromise: forge a JWT at the broker → impersonate any omni at the signer.

This issue (step 1c) eliminates that SPOF.

Plan doc

docs/spec/plans/issue-74-step-1c-device-key-auth.md is the canonical plan. Highlights:

  • Init: daemon generates a device keypair locally; identity ceremony (email-link click / OAuth2 callback / EVM-wallet signature / WebAuthn) binds the device pubkey to the omni at the broker.
  • Per request: daemon signs (omni || message_hex || nonce || timestamp) with the device key; signer verifies the per-request signature against the device pubkey extracted from the session JWT claim.
  • Trust shape: signer never trusts the broker as a transitive authenticator. Broker compromise post-init does not enable forging new sign requests.
  • Identity-type uniformity: evm, email, oauth2_google, passkey all use the same per-request signature shape. Only the init-time binding ceremony differs.
  • UX uniformity: one ceremony at init, automatic per-request signing thereafter (no MetaMask popup, no hardware-wallet prompt).

Why this matters

Per the user discussion that produced the plan: even with a public signer.<zone> listener and bearer-JWT auth, the broker is the single thing protecting /dev/sign-message. Broker compromise = forge requests for any user whose omni is known.

Per-request crypto signature with a user-controlled (device) key removes the SPOF. The pattern matches Heima's ClientAuth::EvmSiweSigned / BackendSigned tier — and is a strict upgrade because (a) the per-request key is user-controlled (not backend-controlled), (b) it's automatic (no per-request user interaction), (c) it's identity-type uniform (covers email/OAuth2 omnis where Heima today only has the bearer path).

External validation: WebAuthn/passkey, EIP-7702 session keys, and ERC-4337 session keys all use the same primitive (high-friction identity verification authorizes a low-friction signing key). The pattern is well-validated.

Implementation order

11 stages laid out in §"Implementation order" of the plan doc. Roughly:

  1. signer-protocol.md v0.2 — wire contract revision
  2. agentkeys-core::device_key module — keypair + canonical_json + sign helper
    2-4. Broker: extend session JWT mint + identity-ceremony handlers to bind device pubkey
  3. dev_key_service handlers: per-request sig verification
  4. init_flow updates: generate device key, register, bind
  5. HttpSignerClient updates: send JWT + device sig
  6. Deprecate the bearer-JWT-only path (step 1b) — protocol doc + handler reject requests without device sig
  7. TEE-stub conformance test extended
  8. Demo doc + operator runbook updated
  9. Live broker host redeploy + smoke walkthrough

Rough total: ~1200 LOC + protocol-doc revision + 11 stage-gated test waves.

Dependencies

Blocks on step 1b landing first (public listener split) so the device-key auth replaces a working bearer-JWT auth rather than a no-auth deployment.

Blocks the TEE worker (step 2) because step 2's threat model assumes the signer can't be tricked by a compromised broker — which is exactly what step 1c delivers.

Out of scope

  • Multi-device authorization per omni (v0.2)
  • Hardware-backed device keys (Secure Enclave / TPM / YubiKey — v0.2)
  • Operator-initiated rotation cadence (v0.2)
  • Cross-device device-key migration (v0.2)

See plan doc §"Non-goals" for full list.

Open questions

See plan doc §"Open questions for review":

  • JWT-claim vs separate signer-side registry for the device pubkey
  • RFC 8785 vs hand-rolled canonical JSON
  • Nonce LRU sizing
  • Sandbox VM device-key persistence

CEO review pending before implementation lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions