feat(conformance): add open_mandate_hash derivation vectors v0 - #279
feat(conformance): add open_mandate_hash derivation vectors v0#279chopmob-cloud wants to merge 17 commits into
Conversation
Lands the v0 conformance vector set for open_mandate_hash derivation proposed in google-agentic-commerce#265, cross-validated across 6 independent implementations. Derivation rule: open_mandate_hash = SHA-256(JCS_RFC8785(unsigned_open_checkout_mandate_body)) Hash input is the mandate claims object, NOT the JWS compact form. 7 vectors covering the canonicalisation rules that catch implementation divergence in practice: - baseline-001: reference - object-key-order-002: JCS sorts object keys (MUST be same hash as baseline) - array-order-003: arrays are order-significant (MUST differ from baseline) - optional-fields-004: presence != absence (MUST differ from baseline) - currency-minor-unit-005: integer minor units only (no float/decimal) - unicode-nfc-006a: no Unicode normalisation (MUST differ from 006b) - unicode-nfd-006b: no Unicode normalisation (MUST differ from 006a) Cross-validated: Python rfc8785@0.1.4, JS canonicalize@3.0.0 (RFC 8785 author), Go gowebpki/jcs v1.0.1, Java cyberphone/json-canonicalization (RFC 8785 reference), Rust serde_jcs 0.2.0, Python rfc8785 (Crest). All 6 implementations: 7/7 vectors + 4/4 pair invariants. PEAC Protocol has already adopted this derivation as normative in AP2-COMPOSITION.md (peacprotocol/peac). Closes google-agentic-commerce#265.
There was a problem hiding this comment.
Code Review
This pull request introduces conformance vectors for the AP2 open_mandate_hash derivation rule, adding a detailed README and a JSON file containing seven test vectors. The feedback suggests correcting the description of the base64 encoding in the README to specify standard base64 instead of base64url, updating the vector IDs in the README table to use their full names for consistency, and adding a trailing newline to the JSON file.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| { | ||
| "vector_id": "ap2-omh-v0-<name>", | ||
| "mandate_body": { ... }, | ||
| "expected_jcs_bytes_b64": "<base64url of RFC 8785 canonical bytes>", |
There was a problem hiding this comment.
The description states that expected_jcs_bytes_b64 is the base64url of the JCS bytes. However, the actual values in vectors-v0.json are encoded using standard base64 (including padding = and / characters). The description should be updated to specify standard base64 to avoid confusion for implementers.
| "expected_jcs_bytes_b64": "<base64url of RFC 8785 canonical bytes>", | |
| "expected_jcs_bytes_b64": "<standard base64 of RFC 8785 canonical bytes>", |
| | `unicode-nfc-006a` | `different_hash_from:unicode-nfd-006b` | No Unicode normalisation | | ||
| | `unicode-nfd-006b` | `different_hash_from:unicode-nfc-006a` | No Unicode normalisation | |
There was a problem hiding this comment.
The expectations in the table use short names (e.g., unicode-nfd-006b) instead of the full vector IDs (e.g., ap2-omh-v0-unicode-nfd-006b) defined in vectors-v0.json. Updating these to match the exact IDs ensures consistency for automated test runners.
Suggested changes:
| `unicode-nfc-006a` | `different_hash_from:ap2-omh-v0-unicode-nfd-006b` | No Unicode normalisation |
| `unicode-nfd-006b` | `different_hash_from:ap2-omh-v0-unicode-nfc-006a` | No Unicode normalisation |…nstant, whitespace in constructor
Apply google-java-format 1.35.0 (the version super-linter runs) to satisfy GOOGLE_JAVA_FORMAT. Whitespace and line-wrapping only, no logic changes.
The repo runs google-java-format and Sun-style Checkstyle together,
whose rules contradict (gjf: {} and 100-col; checkstyle: { } and
80-col), so no Java formatting passes both validators. Drop JcsRunner
and package-info; the Python, Node, and Go runners and the vectors
remain. The Java runner can return once the repo adds a Checkstyle
config.
Addresses review note: the field is standard base64 (with padding and +// characters), not base64url.
0990c9c to
968560c
Compare
Pin actions/checkout and super-linter to release hashes, add a least privilege permissions block, set persist-credentials false, and disable Biome lint (ESLint still covers JS/TS). Matches the configuration proven green on PR 310.
Additive expansion of the open_mandate_hash v0 set from 7 to 12 vectors: - 007a/007b: the headline rule (hash input is the claims object, not the JWS compact form) now has a vector pair. Both carry identical claims in two byte-for-byte different JWS envelopes (different protected headers, canonical vs non-canonical payload encoding, different signatures) and assert same_hash_as. Signatures are real Ed25519 under the RFC 8037 appendix A.1 test key, whose public half is already the cnf.jwk used throughout the vectors; runners do not verify signatures. - 008: control character escaping (quote, backslash, the five two-char escapes, and unnamed controls U+0000 and U+001F). - 009: non-BMP code points serialized as literal UTF-8, complementing the 006 NFC/NFD pair. - 010: integer exactness boundary (2^53 - 1) for open integer claims. All three runners additionally re-derive the hash from the base64url decoded jws_compact payload when present, so an implementation that hashes the envelope instead of the claims fails the vector. Runner usage strings now reference vectors-v0.json (the actual file name). Verified 12/12 vectors and all pair invariants with rfc8785 0.1.4 (Python), canonicalize 3.0.0 (Node), and gowebpki/jcs v1.0.1 (Go); a tampered payload negative test fails in all three.
The open_mandate_hash vectors carry jws_compact fixtures signed with the published RFC 8037 Appendix A.1 Ed25519 test key (public half is the vectors' own cnf.jwk), which gitleaks flags as high-entropy JWTs. Keep all default gitleaks rules and allowlist only the conformance vector JSON path. Signed-off-by: AlgoVoi <chopmob@gmail.com>
What
Lands the v0 conformance vector set for
open_mandate_hashderivation proposed in #265.Derivation rule
Hash input is the mandate claims object, not the JWS compact form. This is the load-bearing interop rule: JWS re-encoding by intermediaries changes the envelope even when the underlying payload is identical.
Location
code/sdk/schemas/ap2/conformance/open_mandate_hash/vectors-v0.json— 7 conformance vectorsREADME.md— derivation rule, verification recipe, known implementation hazardsVectors
baseline-001object-key-order-002array-order-003optional-fields-004currency-minor-unit-005unicode-nfc-006aunicode-nfd-006bCross-implementation validation
6 independent implementations, all 7/7 vectors + 4/4 pair invariants:
rfc8785@0.1.4canonicalize@3.0.0gowebpki/jcs v1.0.1cyberphone/json-canonicalizationserde_jcs 0.2.0rfc8785(fixed)Full validation history in #265.
Prior adoption
PEAC Protocol has already adopted this derivation as normative in
AP2-COMPOSITION.md:Test gate
Closes #265.