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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions