Skip to content

feat(conformance): add open_mandate_hash derivation vectors v0 - #279

Open
chopmob-cloud wants to merge 17 commits into
google-agentic-commerce:mainfrom
chopmob-cloud:feat/open-mandate-hash-conformance-vectors
Open

feat(conformance): add open_mandate_hash derivation vectors v0#279
chopmob-cloud wants to merge 17 commits into
google-agentic-commerce:mainfrom
chopmob-cloud:feat/open-mandate-hash-conformance-vectors

Conversation

@chopmob-cloud

Copy link
Copy Markdown

What

Lands the v0 conformance vector set for open_mandate_hash derivation proposed in #265.

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. 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 vectors
  • README.md — derivation rule, verification recipe, known implementation hazards

Vectors

Vector Pair invariant What it catches
baseline-001 reference Canonical baseline
object-key-order-002 MUST equal baseline JCS sorts object keys
array-order-003 MUST differ from baseline Array sorting bug
optional-fields-004 MUST differ from baseline Presence ≠ absence
currency-minor-unit-005 canonical form Float/decimal encoding
unicode-nfc-006a MUST differ from 006b Unicode normalisation
unicode-nfd-006b MUST differ from 006a Unicode normalisation

Cross-implementation validation

6 independent implementations, all 7/7 vectors + 4/4 pair invariants:

Implementation Language Authors
rfc8785@0.1.4 Python Trail of Bits
canonicalize@3.0.0 JavaScript Erdtman + Rundgren (RFC 8785 author)
gowebpki/jcs v1.0.1 Go @amavashev
cyberphone/json-canonicalization Java Rundgren (RFC 8785 reference impl)
serde_jcs 0.2.0 Rust @seritalien / Vauban
rfc8785 (fixed) Python Crest Systems (@andysalvo)

Full validation history in #265.

Prior adoption

PEAC Protocol has already adopted this derivation as normative in AP2-COMPOSITION.md:

"must not re-derive the open_mandate_hash from a non-JCS canonicalization or a non-SHA-256 digest function. The derivation in AP2 issue #265 is the only one PEAC recognizes for this composition pattern."

Test gate

  • No secrets in diff
  • Schema/conformance files only — no code changes

Closes #265.

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.
@chopmob-cloud
chopmob-cloud requested a review from a team as a code owner June 6, 2026 04:05

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.

Suggested change
"expected_jcs_bytes_b64": "<base64url of RFC 8785 canonical bytes>",
"expected_jcs_bytes_b64": "<standard base64 of RFC 8785 canonical bytes>",

Comment thread code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md Outdated
Comment on lines +43 to +44
| `unicode-nfc-006a` | `different_hash_from:unicode-nfd-006b` | No Unicode normalisation |
| `unicode-nfd-006b` | `different_hash_from:unicode-nfc-006a` | No Unicode normalisation |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

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 |

Comment thread code/sdk/schemas/ap2/conformance/open_mandate_hash/vectors-v0.json Outdated
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.
@chopmob-cloud
chopmob-cloud force-pushed the feat/open-mandate-hash-conformance-vectors branch from 0990c9c to 968560c Compare August 4, 2026 07:18
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Proposal] v0 conformance vectors for open_mandate_hash derivation (cross-impl validated)

1 participant