fix: implement JCS cart-to-payment mandate binding per RFC 8785 - #253
fix: implement JCS cart-to-payment mandate binding per RFC 8785#253chopmob-cloud wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements a cart-to-payment mandate binding mechanism to ensure the integrity of the payment process by linking a PaymentMandate to a specific CartMandate. Key changes include the addition of cart_mandate_id and cart_mandate_hash fields to the PaymentMandateContents model, the introduction of a validation module using RFC 8785 (JCS) for canonicalization and SHA-256 for hashing, and updates to the AP2 specification documentation. I have no feedback to provide.
|
Thanks @gemini-code-assist. Noted that the automated review found no issues. Flagging for @GarethCOliver or any AP2 maintainer when you have a cycle -- this is the cart-integrity binding gap from #211 and is independently useful alongside the Biome lint fix in #249. |
|
Thanks for the update, @chopmob-cloud. I've reviewed the implementation of the cart-to-payment mandate binding. The use of RFC 8785 for canonicalization is the correct approach to ensure cross-language consistency, and the |
|
The Biome lint failures are pre-existing in |
a1b73d4 to
d1cb27c
Compare
…oogle-agentic-commerce#211) Adds cart_mandate_id and cart_mandate_hash = hex(sha256(JCS(CartMandate))) to PaymentMandateContents, and a strict verifier that recomputes the hash over the raw received CartMandate bytes and rejects on mismatch, absent hash, or tampered/injected fields. Fail-closed by default; an explicit allow_unbound_cart opt-out is reserved for legacy mandates and never weakens a present hash. Signed-off-by: AlgoVoi <chopmob@gmail.com>
d1cb27c to
23a8847
Compare
Summary
Closes the CartMandate / PaymentMandate binding gap raised in #211.
Without a cryptographic link from
CartMandatetoPaymentMandate, a malicious or misconfigured agent can substitute a different cart after the user has expressed intent. This binds them with a deterministic RFC 8785 (JCS) hash that is stable across implementations.Spec (
docs/ap2/specification.md)New normative section, Cart-to-Payment Mandate Binding, under the Payment Mandate section, with three requirements:
PaymentMandateContentsMUST includecart_mandate_idandcart_mandate_hash = hex(sha256(JCS(CartMandate))).null/Noneoptional fields, so Python and Go (omitempty) produce identical canonical bytes.Types (
code/sdk/python/ap2/models/mandate.py)Adds two optional fields to
PaymentMandateContents:cart_mandate_idandcart_mandate_hash. Both are optional for backward compatibility; new mandates SHOULD populate them.Verifier (
code/samples/python/src/common/validation.py)validate_cart_mandate_hash()recomputes and compares the hash over the raw received CartMandate JSON, taken before schema parsing. Hashing a re-serialised model is not sufficient: parsers silently drop unknown or extension fields and can collapse an explicitnullwith an absent field, so tampering outside the model schema would escape a model-derived hash. The verifier is strict by default: a PaymentMandate that omitscart_mandate_hashis rejected. An explicit keyword-onlyallow_unbound_cart=Trueopt-out is reserved for legacy mandates during rollout, and it never weakens verification of a present hash.Dependency
Adds
rfc8785>=0.1.2to the samples.Test plan
cart_mandate_hashis rejected by default.allow_unbound_cart=True, and that opt-out does not weaken a present hash.nulldiffers from an absent field.All eight tests pass (
code/samples/python/tests/validation_tests.py).