PR O: EIP-712 / JWS event signatures (pluggable signer) #19

Open
nsatoshi wants to merge 1 commits from devin/1776882169-pr-o-eip712 into main
Owner

Closes gap-analysis v2 §7.5 and §10.5 (event signatures were HMAC-only).

What

New: services/eventSigner.ts — three interchangeable strategies selected via EVENT_SIGNING_MODE:

Mode Keying Output shape
hmac (default) EVENT_BUS_HMAC_SECRET 64-char hex
eip712 ORCHESTRATOR_PRIVATE_KEY (ethers Wallet) or HSM via EVENT_SIGNING_HSM_KEY_ID 0x + 130 hex (ECDSA secp256k1)
jws EVENT_BUS_HMAC_SECRET (HS256) compact JWS header.body.sig

EIP-712 domain is pinned to:

{name: "CurrenciCombo", version: "1", chainId: CHAIN_138_CHAIN_ID, verifyingContract: NOTARY_REGISTRY_ADDRESS}

Changed: services/eventBus.ts:

  • publish() now delegates to getEventSigner().sign(...) instead of the inline HMAC.
  • verifyChain() uses the active signer, with a legacy HMAC fall-through so rows written before PR O (from PR D) still verify. Failure message embeds the active mode for debuggability.

Helpers: legacyHmac() preserves the pre-PR-O signature scheme for historical verification; payloadHashOf() re-exported.

Why

Gap v2 §7.5 flagged the event signature as HMAC-only — the arch note (§13) calls for non-repudiable signed payloads and an HSM fallback. EIP-712 gives us on-chain-verifiable signatures keyed to the same wallet that already writes to NotaryRegistry on Chain-138.

Verification

  • npx tsc --noEmit clean.
  • npx jest 93/93 green.
  • 13 new unit tests: mode resolution (incl. fall-through on unknown values), HMAC round-trip, JWS round-trip + payload-tamper + sig-truncation rejection, EIP-712 round-trip + ethers address recovery + cross-event replay rejection.

Rollout notes

This ships with hmac as the default — no operational change until EVENT_SIGNING_MODE=eip712 is set. When that env flip happens, existing rows still verify because verifyChain() falls back to legacyHmac().

Closes gap-analysis v2 §7.5 and §10.5 (event signatures were HMAC-only). ## What **New**: `services/eventSigner.ts` — three interchangeable strategies selected via `EVENT_SIGNING_MODE`: | Mode | Keying | Output shape | |---|---|---| | `hmac` (default) | `EVENT_BUS_HMAC_SECRET` | 64-char hex | | `eip712` | `ORCHESTRATOR_PRIVATE_KEY` (ethers Wallet) or HSM via `EVENT_SIGNING_HSM_KEY_ID` | `0x` + 130 hex (ECDSA secp256k1) | | `jws` | `EVENT_BUS_HMAC_SECRET` (HS256) | compact JWS `header.body.sig` | EIP-712 domain is pinned to: ``` {name: "CurrenciCombo", version: "1", chainId: CHAIN_138_CHAIN_ID, verifyingContract: NOTARY_REGISTRY_ADDRESS} ``` **Changed**: `services/eventBus.ts`: - `publish()` now delegates to `getEventSigner().sign(...)` instead of the inline HMAC. - `verifyChain()` uses the active signer, with a **legacy HMAC fall-through** so rows written before PR O (from PR D) still verify. Failure message embeds the active mode for debuggability. **Helpers**: `legacyHmac()` preserves the pre-PR-O signature scheme for historical verification; `payloadHashOf()` re-exported. ## Why Gap v2 §7.5 flagged the event signature as HMAC-only — the arch note (§13) calls for non-repudiable signed payloads and an HSM fallback. EIP-712 gives us on-chain-verifiable signatures keyed to the same wallet that already writes to NotaryRegistry on Chain-138. ## Verification - `npx tsc --noEmit` clean. - `npx jest` 93/93 green. - 13 new unit tests: mode resolution (incl. fall-through on unknown values), HMAC round-trip, JWS round-trip + payload-tamper + sig-truncation rejection, EIP-712 round-trip + ethers address recovery + cross-event replay rejection. ## Rollout notes This ships with `hmac` as the default — no operational change until `EVENT_SIGNING_MODE=eip712` is set. When that env flip happens, existing rows still verify because `verifyChain()` falls back to `legacyHmac()`.
nsatoshi added 1 commit 2026-04-22 18:26:11 +00:00
EIP-712 / JWS event signatures via pluggable signer
Some checks failed
CI / Frontend Lint (pull_request) Failing after 8s
CI / Frontend Type Check (pull_request) Failing after 6s
CI / Frontend Build (pull_request) Failing after 6s
CI / Frontend E2E Tests (pull_request) Failing after 7s
CI / Orchestrator Build (pull_request) Failing after 6s
CI / Contracts Compile (pull_request) Failing after 5s
CI / Contracts Test (pull_request) Failing after 6s
Code Quality / SonarQube Analysis (pull_request) Failing after 21s
Code Quality / Code Quality Checks (pull_request) Failing after 5s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 4s
4c90617208
Closes gap-analysis v2 §7.5 and §10.5.

- services/eventSigner.ts — three interchangeable strategies
  selected via EVENT_SIGNING_MODE:
    hmac  (default; back-compat) HMAC-SHA256 via EVENT_BUS_HMAC_SECRET
    eip712 EIP-712 typed data signed by ORCHESTRATOR_PRIVATE_KEY
           (ethers Wallet) or via services/hsm.ts when
           EVENT_SIGNING_HSM_KEY_ID is set. Domain pinned to
           CurrenciCombo/1/chain-138/NOTARY_REGISTRY_ADDRESS.
    jws   Compact JWS (HS256), useful when the signature has to
           traverse a JWT-aware infra layer.
- services/eventBus.ts — publish() now delegates to getEventSigner();
  verifyChain() uses the active signer, falling through to legacyHmac
  for rows written before PR O so the historical tail still verifies.
- legacyHmac() helper + payloadHashOf() re-exported.
- 13 unit tests across mode resolution, HMAC round-trip, JWS
  round-trip + tamper rejection, EIP-712 round-trip + ethers address
  recovery, and cross-event replay rejection.
- Full suite 93/93 green; tsc --noEmit clean.
Some checks failed
CI / Frontend Lint (pull_request) Failing after 8s
CI / Frontend Type Check (pull_request) Failing after 6s
CI / Frontend Build (pull_request) Failing after 6s
CI / Frontend E2E Tests (pull_request) Failing after 7s
CI / Orchestrator Build (pull_request) Failing after 6s
CI / Contracts Compile (pull_request) Failing after 5s
CI / Contracts Test (pull_request) Failing after 6s
Code Quality / SonarQube Analysis (pull_request) Failing after 21s
Code Quality / Code Quality Checks (pull_request) Failing after 5s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 4s
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin devin/1776882169-pr-o-eip712:devin/1776882169-pr-o-eip712
git checkout devin/1776882169-pr-o-eip712
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: d-bis/CurrenciCombo#19