fix(ai): use injective serialization for tool approval HMAC payload#17493
fix(ai): use injective serialization for tool approval HMAC payload#17493dnukumamras wants to merge 3 commits into
Conversation
Serialize the signed approval fields with JSON.stringify instead of newline concatenation so distinct field tuples cannot produce identical signed bytes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
what happens re backwards compat? what about people who use v7 in prod and have the old signatures |
This only impacts for signatures that are pending ones. The edge case is if a tool the model requested (signed by the old server) whose human approval arrives on a turn handled by the new server, before the tool has executed. New server recomputes the JSON payload, HMAC differs, and throws a hard error |
…miter-free Fall back to the legacy newline-joined payload on verify when no signed field contains a newline, so approvals signed by an older version still verify across an upgrade without reopening the collision. Marked for removal in v8. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bugfix reviewOutcome: approved Fixes issueStatus: fully-addresses The fixed-position JSON array gives each signed string field an unambiguous boundary and adds domain separation. Independent verification confirmed the old newline-retupled payloads collide while the new payloads and HMACs differ. Side effectsRisk: low Normal signing, verification, and canonical input ordering remain unchanged apart from the intentional signature-format replacement. Concerns:
PerformanceRisk: low JSON serialization remains linear in the field lengths and replaces an existing concatenated-string allocation with only small escaping and prefix overhead. Backwards compatibilityRisk: low Stored messages are not modified, but signatures stored in pending approval histories from the old format will no longer verify; this deliberate fail-closed migration is disclosed in the pull request. Concerns:
SecurityRisk: none The change closes the field-boundary collision without weakening HMAC verification, canonical input hashing, or secret handling. The fixed domain prefix also prevents reuse across differently versioned payload domains. TestingStatus: appropriate Tests cover the original newline retupling attack, valid self-verification, distinct signatures, canonical input key ordering, and representative control and escaping characters in both Node and edge environments. VerificationReviewed the complete three-file diff and surrounding signing and replay-validation paths. The focused signature and approval-validation suites passed in Node and edge environments, package type checking passed, formatting and lint checks passed, and a bounded serialization check found no collisions across 2,000 representative tuples including delimiters and lone surrogates. |
The tool approval signature payload was built by joining fields with
\n. Because fields liketoolNameandtoolCallIdcan contain a newline, two different field tuples could serialize to identical bytes — so a signature issued for one tuple could verify against a different one. The payload is now serialized withJSON.stringify(with a constant domain-separation prefix), which escapes delimiter/control characters and makes the encoding injective.Verification stays backwards compatible: it falls back to the old newline format only when no signed field contains a
\n, so approvals signed by an older version still verify across an upgrade. This legacy fallback is markedTODO: remove in v8.Happy path
(approvalId, toolCallId, toolName, input)tuple.Unhappy path
toolNamecontains a newline can no longer be retupled intotoolCallIdto reuse the same signature for a different tool — in both the new and the legacy fallback paths.\n,\r,\t,\0,",\\) shifted across a field boundary fails verification.\n).🤖 Generated with Claude Code