Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# AP2 `open_mandate_hash` Conformance Vectors — v0

Conformance vectors for the `open_mandate_hash` derivation rule for
`open_checkout_mandate.json`. Any AP2 implementation that derives
`open_mandate_hash` MUST reproduce these vectors byte-for-byte.

## Derivation rule

```
open_mandate_hash = SHA-256(JCS_RFC8785(unsigned_open_checkout_mandate_body))
```

**Lowercase hex output.**

The hash input is the mandate **claims object**, not the JWS compact form.
Re-encoding the JWS envelope MUST NOT change `open_mandate_hash`.

## Vectors

`vectors-v0.json` — 7 vectors anchored to
`code/sdk/schemas/ap2/open_checkout_mandate.json` at schema commit
`e3d9cafa7311d90612c7f908ae9b8821ddc8735a`.

Each vector is structured as:

```json
{
"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>",

"expected_open_mandate_hash": "sha256:<lowercase-hex>",
"expectation": "reference | same_hash_as:<id> | different_hash_from:<id>"
}
```

| Vector | Pair invariant | Tests |
|---|---|---|
| `baseline-001` | reference | Canonical baseline |
| `object-key-order-002` | `same_hash_as:baseline-001` | JCS sorts object members |
| `array-order-003` | `different_hash_from:baseline-001` | Arrays are order-significant |
| `optional-fields-004` | `different_hash_from:baseline-001` | Presence ≠ absence |
Comment thread
chopmob-cloud marked this conversation as resolved.
Outdated
| `currency-minor-unit-005` | canonical form | Integer minor units only |
| `unicode-nfc-006a` | `different_hash_from:unicode-nfd-006b` | No Unicode normalisation |

Check warning on line 43 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (normalisation)
| `unicode-nfd-006b` | `different_hash_from:unicode-nfc-006a` | No Unicode normalisation |

Check warning on line 44 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (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 |


The array-order and Unicode pairs catch the divergences most commonly seen
in practice: implementations that sort arrays or NFC-normalise strings will

Check warning on line 47 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (normalise)
fail the corresponding pair invariant immediately.

## Cross-implementation validation

| Implementation | Language | Library | Result |
|---|---|---|---|
| `rfc8785@0.1.4` | Python | Trail of Bits | 7/7 + 4/4 pair invariants ✓ |
| `canonicalize@3.0.0` | JavaScript | Erdtman + Rundgren (RFC 8785 author) | 7/7 + 4/4 ✓ |

Check warning on line 55 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Rundgren)

Check warning on line 55 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Erdtman)
| `gowebpki/jcs v1.0.1` | Go | @amavashev (AP2 maintainer) | 7/7 + 4/4 ✓ |

Check warning on line 56 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (amavashev)

Check warning on line 56 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (gowebpki)
| `cyberphone/json-canonicalization` | Java | Rundgren (RFC 8785 reference impl) | 7/7 + 4/4 ✓ |

Check warning on line 57 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Rundgren)

Check warning on line 57 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (cyberphone)
| `serde_jcs 0.2.0` | Rust | @seritalien / Vauban | 7/7 + 4/4 ✓ |

Check warning on line 58 in code/sdk/schemas/ap2/conformance/open_mandate_hash/README.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (serde)
| `rfc8785` (fixed) | Python | Crest Systems | 7/7 + 4/4 ✓ |

Full validation history: [AP2 issue #265](https://github.com/google-agentic-commerce/AP2/issues/265)

## Known implementation hazards

- **`json.dumps()` non-ASCII escaping** — Python's `json.dumps()` escapes
non-ASCII characters as `\uXXXX`. RFC 8785 requires literal UTF-8 bytes
for printable codepoints above U+007F. Use the `rfc8785` library instead.
This will cause failures on vectors 006a and 006b.

- **Array sorting** — Do not sort arrays before hashing. JCS preserves
array element order. Vector 003 catches this.

- **Unicode normalisation** — Do not NFC-normalise strings before hashing.
RFC 8785 makes no Unicode normalisation. Vectors 006a/006b catch this.

- **Float/decimal prices** — `item.price` and `amount.amount` are integers
in the currency minor unit. `10.50` MUST be encoded as `1050`. Vector
005 catches this.
Loading
Loading