spec(security): pin sf-binary encoding + tighten URL canonicalization (#2341, #2343)#2348
Merged
Conversation
…#2341, #2343) Clarifications to the AdCP RFC 9421 request-signing profile surfaced by TypeScript reference SDK work (adcp-client#575) and expert review. #2341: Signature and Content-Digest sf-binary tokens are base64url-no- padding (RFC 4648 §5), overriding RFC 8941's standard-base64 default. Matches the nonce rule; avoids proxy hazards with '/' and '='. Verifiers MUST reject mixed-alphabet tokens (closes digest-mismatch ambiguity) and MAY lenient-decode pure standard-base64 through AdCP 3.2. #2343: @target-uri algorithm expanded: - UTS-46 Nontransitional pinned for IDN to Punycode (TS / Go / Python default differently). - IPv6 zone identifiers (RFC 6874) are rejected -- node-local, no meaning cross-host. - Consecutive slashes preserved byte-for-byte (was: collapsed). Closes the /admin//foo vs /admin/foo path-confusion surface. Servers MUST disable slash-folding on signed routes. - Malformed-authority rejection expanded: bracket-mismatch IPv6, bare IPv6, empty authority, userinfo-only, raw non-ASCII host. - @authority MUST be derived from wire Host / :authority and MUST match the @target-uri authority byte-for-byte (closes cross-vhost replay on shared verifier pools). - Percent-encoding: reserved stays encoded (%3A), full unreserved set decoded (%41 to A, not just %7E to ~). - Combined dot-segment + consecutive-slash cases pinned. New canonicalization.json (version 3.0): 31 fixed input/output cases (25 positive, 6 malformed-reject) covering every algorithm step. No crypto needed -- SDKs run it as a fast unit test. Closes #2341, #2343. Original profile: #2323. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Schema Link Check ResultsCommit:
|
…_malformed Addresses expert-review nit on #2348. The previous taxonomy used request_signature_header_malformed for both actual header malformation AND URL-parse rejections — semantically confusing because URL rejections happen before any signature header is inspected. Split into two codes: - request_target_uri_malformed: URL syntax invalid (empty authority, bare IPv6, IPv6 zone identifier, bracket-mismatch, raw non-ASCII host, @authority / Host mismatch). - request_signature_header_malformed: unchanged — covers actual Signature / Signature-Input header problems and the Signature / Content-Digest mixed-alphabet rejection. Safe to rename now — 3.0 has not shipped. Updates the 6 reject cases in canonicalization.json; existing negative vectors that test actual header malformation (011, 019) stay on the original code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two clarifications to the AdCP RFC 9421 request-signing profile surfaced by TypeScript reference SDK work (adcp-client#575) and follow-up expert review. Both are spec tightenings — no behavioral change for signers that already match the reference vectors.
#2341 — binary value encoding
SignatureandContent-Digestsf-binary tokens are normatively base64url-no-padding (RFC 4648 §5), overriding RFC 8941 §3.3.5's standard-base64 default. Matches the existingnoncerule; avoids two proxy hazards (/rewriting,=parsed as a structured-field delimiter).[+/=]plus any of[-_]in one value) — mixed tokens can decode to different bytes depending on "translate-then-decode" order, letting an attacker stage aContent-Digestmismatch that one verifier accepts and another rejects.#2343 — URL canonicalization
Expanded the
@target-urialgorithm with normative text for every case that previously admitted implementation variance:url.domainToASCII), Go (x/net/idna), and Python (idnapackage)./admin//foovs/admin/foopath-confusion surface. Deployments MUST disable slash-folding on signed routes.@authorityderived from wireHost(or HTTP/2+:authority), MUST byte-for-byte match canonical@target-uriauthority. Closes cross-vhost replay on shared verifier pools.%3A), full unreserved set decoded (%41→A, not just%7E→~)./a/.//b→/a//b,/a//../b→/a/b).New conformance set
static/compliance/source/test-vectors/request-signing/canonicalization.json— 31 fixed input/output cases (25 positive, 6 malformed-reject) covering every algorithm step. Version-pinned ("version": "3.0"). Independent of crypto — SDKs run it as a fast unit test. Published at/compliance/{version}/test-vectors/request-signing/canonicalization.json.Closes #2341, #2343. Original profile: #2323 (3.0 GA).
Test plan
npm run precommitpasses (29 test files, 587 tests, typecheck clean)canonicalization.jsonparses, version-pinned, no duplicateinput_urls, all required fields present on every casebücher.exampleindependently verified via Node'surl.domainToASCIIcanonicalization.jsonas a unit-test suite🤖 Generated with Claude Code