fix(golden): sort challenge-id canonical JSON by code point, not locale#54
Open
MattewGraham wants to merge 2 commits into
Open
fix(golden): sort challenge-id canonical JSON by code point, not locale#54MattewGraham wants to merge 2 commits into
MattewGraham wants to merge 2 commits into
Conversation
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.
Challenge-ID canonicalization: golden adapter sorts keys with locale collation
The challenge id is
base64url(HMAC-SHA256(secret, realm|method|intent| base64url(canonical_json(request))|expires|digest|opaque)). For that HMAC tobe a cross-SDK identifier,
canonical_jsonhas to be byte-identicaleverywhere. It isn't:
localeCompare— locale/ICU collationjson.dumps(sort_keys=True)— code pointjson.Marshalmap ordering — byte ordersort_by_key— byte ordersort_by { key }— byte orderORDER_MAP_ENTRIES_BY_KEYS— code-unit orderFive implementations agree on code-point order; the reference is the odd one
out. Locale collation interleaves cases (
amount<Currency<network<ZONE) where code-point order groups uppercase first (Currency<ZONE<amount<network), so any request object with mixed-case keys canonicalizesdifferently — and therefore HMACs differently — on the golden adapter.
Demonstrated with
{"amount", "Currency", "ZONE", "network": {"chainId", "Layer"}}:Worse than plain divergence:
localeCompareoutput depends on the runtime'sICU data and default locale, so the reference implementation's "canonical" ids
aren't even guaranteed reproducible across machines. Every existing
challenge-id.jsonvector happens to use lowercase-initial keys, which is theonly reason this never surfaced.
Fix
Sort with a plain code-point comparator in
stableStringify(used exclusivelyby
generateConformanceChallengeId):New vector
Added
mixed_case_key_orderingtovectors/challenge-id.json(mixed-casekeys at both the top level and inside a nested object, expected id
WXjRL_…). It fails against the unfixed golden adapter and passes after thefix, so the suite now pins this behavior for every SDK. The expected value was
computed independently (Python
hmac/hashlibreimplementation, which alsoreproduces all 25 existing vector ids) and cross-checked against the python
adapter.
Results
vector_runner.py --adapter typescript --adapter python --vector challenge-id:52/52 pass (26 scenarios × 2 adapters) after the fix.
stableStringifyhas no other call sites.