Skip to content

Commit c85c63a

Browse files
NAOR YUVALclaude
authored andcommitted
feat: add grantId/budgetId chain linkage and domain-separated signing
Adds explicit ID-based linkage through the MPCP authorization chain: - SBA now requires and stores grantId (links to PolicyGrant) - SPA now requires and stores budgetId (links to SBA) - Verifier checks grantId/budgetId chain integrity at each step - Domain-separated hashing: "MPCP:SBA:1.0:" and "MPCP:SPA:1.0:" prefixes Also regenerates all golden test vectors and updates all tests to provide the new required fields. Adds scripts/gen-vectors.mjs for reproducible vector regeneration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 9b96bae commit c85c63a

45 files changed

Lines changed: 839 additions & 144 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"version": "1.0",
3+
"budgetId": "baa7d916-a377-47ef-a0ad-ac35c748f15c",
4+
"grantId": "5ad06052-234c-49e8-8719-3a6ca6b3965a",
5+
"sessionId": "22222222-2222-4222-8222-222222222222",
6+
"vehicleId": "ev-7890",
7+
"policyHash": "a1b2c3d4e5f7",
8+
"currency": "USD",
9+
"minorUnit": 2,
10+
"budgetScope": "SESSION",
11+
"maxAmountMinor": "5000",
12+
"allowedRails": [
13+
"xrpl"
14+
],
15+
"allowedAssets": [
16+
{
17+
"kind": "IOU",
18+
"currency": "RLUSD",
19+
"issuer": "rIssuer"
20+
}
21+
],
22+
"destinationAllowlist": [
23+
"rChargingStation"
24+
],
25+
"expiresAt": "2030-12-31T23:59:59Z"
26+
}

examples/ev-charging/generate.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const { runVerify } = await import("../../dist/cli/verify.js");
4444

4545
const EXPIRY = "2030-12-31T23:59:59Z";
4646
const SETTLEMENT_NOW = "2026-01-15T12:00:00Z";
47-
const policyHash = "ev-charging-policy-v1";
47+
const policyHash = "a1b2c3d4e5f7";
4848

4949
// Charging station as destination
5050
const DESTINATION = "rChargingStation";
@@ -59,6 +59,7 @@ const policyGrant = createPolicyGrant({
5959
const budgetAuth = createBudgetAuthorization({
6060
sessionId: "22222222-2222-4222-8222-222222222222",
6161
vehicleId: "ev-7890",
62+
grantId: policyGrant.grantId,
6263
policyHash,
6364
currency: "USD",
6465
maxAmountMinor: "5000",
@@ -71,6 +72,7 @@ const budgetAuth = createBudgetAuthorization({
7172
const signedBudgetAuth = createSignedBudgetAuthorization({
7273
sessionId: budgetAuth.sessionId,
7374
vehicleId: budgetAuth.vehicleId,
75+
grantId: policyGrant.grantId,
7476
policyHash: budgetAuth.policyHash,
7577
currency: budgetAuth.currency,
7678
maxAmountMinor: budgetAuth.maxAmountMinor,
@@ -115,7 +117,7 @@ const paymentPolicyDecision = {
115117
const signedPaymentAuth = createSignedPaymentAuthorization(
116118
budgetAuth.sessionId,
117119
paymentPolicyDecision,
118-
{ settlementIntent: intent },
120+
{ settlementIntent: intent, budgetId: signedBudgetAuth.authorization.budgetId },
119121
);
120122

121123
if (!signedPaymentAuth) throw new Error("Failed to create SPA");
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"decisionId": "dec-ev-1",
3+
"policyHash": "a1b2c3d4e5f7",
4+
"action": "ALLOW",
5+
"reasons": [
6+
"OK"
7+
],
8+
"expiresAtISO": "2030-12-31T23:59:59Z",
9+
"rail": "xrpl",
10+
"asset": {
11+
"kind": "IOU",
12+
"currency": "RLUSD",
13+
"issuer": "rIssuer"
14+
},
15+
"priceFiat": {
16+
"amountMinor": "2500",
17+
"currency": "USD"
18+
},
19+
"chosen": {
20+
"rail": "xrpl",
21+
"quoteId": "q1"
22+
},
23+
"settlementQuotes": [
24+
{
25+
"quoteId": "q1",
26+
"rail": "xrpl",
27+
"amount": {
28+
"amount": "25000000",
29+
"decimals": 6
30+
},
31+
"destination": "rChargingStation",
32+
"expiresAt": "2030-12-31T23:59:59Z",
33+
"asset": {
34+
"kind": "IOU",
35+
"currency": "RLUSD",
36+
"issuer": "rIssuer"
37+
}
38+
}
39+
]
40+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"grantId": "5ad06052-234c-49e8-8719-3a6ca6b3965a",
3+
"policyHash": "a1b2c3d4e5f7",
4+
"expiresAt": "2030-12-31T23:59:59Z",
5+
"allowedRails": [
6+
"xrpl"
7+
],
8+
"allowedAssets": [
9+
{
10+
"kind": "IOU",
11+
"currency": "RLUSD",
12+
"issuer": "rIssuer"
13+
}
14+
]
15+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
{
2+
"settlement": {
3+
"amount": "25000000",
4+
"rail": "xrpl",
5+
"asset": {
6+
"kind": "IOU",
7+
"currency": "RLUSD",
8+
"issuer": "rIssuer"
9+
},
10+
"destination": "rChargingStation",
11+
"nowISO": "2026-01-15T12:00:00Z"
12+
},
13+
"settlementIntent": {
14+
"version": "1.0",
15+
"rail": "xrpl",
16+
"amount": "25000000",
17+
"createdAt": "2026-01-15T12:00:00Z",
18+
"destination": "rChargingStation",
19+
"asset": {
20+
"kind": "IOU",
21+
"currency": "RLUSD",
22+
"issuer": "rIssuer"
23+
}
24+
},
25+
"spa": {
26+
"authorization": {
27+
"version": "1.0",
28+
"decisionId": "dec-ev-1",
29+
"sessionId": "22222222-2222-4222-8222-222222222222",
30+
"policyHash": "a1b2c3d4e5f7",
31+
"budgetId": "a3dc0432-5a64-4d64-89ea-de61a3eede3e",
32+
"quoteId": "q1",
33+
"rail": "xrpl",
34+
"asset": {
35+
"kind": "IOU",
36+
"currency": "RLUSD",
37+
"issuer": "rIssuer"
38+
},
39+
"amount": "25000000",
40+
"destination": "rChargingStation",
41+
"intentHash": "62b57f4ca753d014bca80b82cac2c7ca33850e3697c4707e636326e365c58687",
42+
"expiresAt": "2030-12-31T23:59:59Z"
43+
},
44+
"signature": "+Oz+9+eGK32BeGW8FON0hj66wcPhN23TZ9xec9HZfLwC2oeKMDLBFZZCYTIh0Me8AxJbKQ0Jv+lOU6ZsIKOrDA==",
45+
"keyId": "mpcp-spa-signing-key-1"
46+
},
47+
"sba": {
48+
"authorization": {
49+
"version": "1.0",
50+
"budgetId": "a3dc0432-5a64-4d64-89ea-de61a3eede3e",
51+
"grantId": "5ad06052-234c-49e8-8719-3a6ca6b3965a",
52+
"sessionId": "22222222-2222-4222-8222-222222222222",
53+
"vehicleId": "ev-7890",
54+
"policyHash": "a1b2c3d4e5f7",
55+
"currency": "USD",
56+
"minorUnit": 2,
57+
"budgetScope": "SESSION",
58+
"maxAmountMinor": "5000",
59+
"allowedRails": [
60+
"xrpl"
61+
],
62+
"allowedAssets": [
63+
{
64+
"kind": "IOU",
65+
"currency": "RLUSD",
66+
"issuer": "rIssuer"
67+
}
68+
],
69+
"destinationAllowlist": [
70+
"rChargingStation"
71+
],
72+
"expiresAt": "2030-12-31T23:59:59Z"
73+
},
74+
"signature": "WT0EExkYrd9KmjNtxR92YKfLOdpdLbgVAPuwZoP7+f+uioGz+N30kQICJAnIAB8mo8dacQz1otJZz17EfSCPCg==",
75+
"keyId": "mpcp-sba-signing-key-1"
76+
},
77+
"policyGrant": {
78+
"grantId": "5ad06052-234c-49e8-8719-3a6ca6b3965a",
79+
"policyHash": "a1b2c3d4e5f7",
80+
"expiresAt": "2030-12-31T23:59:59Z",
81+
"allowedRails": [
82+
"xrpl"
83+
],
84+
"allowedAssets": [
85+
{
86+
"kind": "IOU",
87+
"currency": "RLUSD",
88+
"issuer": "rIssuer"
89+
}
90+
]
91+
},
92+
"paymentPolicyDecision": {
93+
"decisionId": "dec-ev-1",
94+
"policyHash": "a1b2c3d4e5f7",
95+
"action": "ALLOW",
96+
"reasons": [
97+
"OK"
98+
],
99+
"expiresAtISO": "2030-12-31T23:59:59Z",
100+
"rail": "xrpl",
101+
"asset": {
102+
"kind": "IOU",
103+
"currency": "RLUSD",
104+
"issuer": "rIssuer"
105+
},
106+
"priceFiat": {
107+
"amountMinor": "2500",
108+
"currency": "USD"
109+
},
110+
"chosen": {
111+
"rail": "xrpl",
112+
"quoteId": "q1"
113+
},
114+
"settlementQuotes": [
115+
{
116+
"quoteId": "q1",
117+
"rail": "xrpl",
118+
"amount": {
119+
"amount": "25000000",
120+
"decimals": 6
121+
},
122+
"destination": "rChargingStation",
123+
"expiresAt": "2030-12-31T23:59:59Z",
124+
"asset": {
125+
"kind": "IOU",
126+
"currency": "RLUSD",
127+
"issuer": "rIssuer"
128+
}
129+
}
130+
]
131+
},
132+
"sbaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAT0Oq22kLSptosoBNSpYLDWuufSm8upVS5WTUal6d8CM=\n-----END PUBLIC KEY-----\n",
133+
"spaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAy2za2OJ7PYHyQ6ARF/ynBlgmVKvH8YFEmsPCMW7jApc=\n-----END PUBLIC KEY-----\n"
134+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "1.0",
3+
"rail": "xrpl",
4+
"amount": "25000000",
5+
"createdAt": "2026-01-15T12:00:00Z",
6+
"destination": "rChargingStation",
7+
"asset": {
8+
"kind": "IOU",
9+
"currency": "RLUSD",
10+
"issuer": "rIssuer"
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"amount": "25000000",
3+
"rail": "xrpl",
4+
"asset": {
5+
"kind": "IOU",
6+
"currency": "RLUSD",
7+
"issuer": "rIssuer"
8+
},
9+
"destination": "rChargingStation",
10+
"nowISO": "2026-01-15T12:00:00Z"
11+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"authorization": {
3+
"version": "1.0",
4+
"budgetId": "a3dc0432-5a64-4d64-89ea-de61a3eede3e",
5+
"grantId": "5ad06052-234c-49e8-8719-3a6ca6b3965a",
6+
"sessionId": "22222222-2222-4222-8222-222222222222",
7+
"vehicleId": "ev-7890",
8+
"policyHash": "a1b2c3d4e5f7",
9+
"currency": "USD",
10+
"minorUnit": 2,
11+
"budgetScope": "SESSION",
12+
"maxAmountMinor": "5000",
13+
"allowedRails": [
14+
"xrpl"
15+
],
16+
"allowedAssets": [
17+
{
18+
"kind": "IOU",
19+
"currency": "RLUSD",
20+
"issuer": "rIssuer"
21+
}
22+
],
23+
"destinationAllowlist": [
24+
"rChargingStation"
25+
],
26+
"expiresAt": "2030-12-31T23:59:59Z"
27+
},
28+
"signature": "WT0EExkYrd9KmjNtxR92YKfLOdpdLbgVAPuwZoP7+f+uioGz+N30kQICJAnIAB8mo8dacQz1otJZz17EfSCPCg==",
29+
"keyId": "mpcp-sba-signing-key-1"
30+
}

examples/ev-charging/spa.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"authorization": {
3+
"version": "1.0",
4+
"decisionId": "dec-ev-1",
5+
"sessionId": "22222222-2222-4222-8222-222222222222",
6+
"policyHash": "a1b2c3d4e5f7",
7+
"budgetId": "a3dc0432-5a64-4d64-89ea-de61a3eede3e",
8+
"quoteId": "q1",
9+
"rail": "xrpl",
10+
"asset": {
11+
"kind": "IOU",
12+
"currency": "RLUSD",
13+
"issuer": "rIssuer"
14+
},
15+
"amount": "25000000",
16+
"destination": "rChargingStation",
17+
"intentHash": "62b57f4ca753d014bca80b82cac2c7ca33850e3697c4707e636326e365c58687",
18+
"expiresAt": "2030-12-31T23:59:59Z"
19+
},
20+
"signature": "+Oz+9+eGK32BeGW8FON0hj66wcPhN23TZ9xec9HZfLwC2oeKMDLBFZZCYTIh0Me8AxJbKQ0Jv+lOU6ZsIKOrDA==",
21+
"keyId": "mpcp-spa-signing-key-1"
22+
}

examples/machine-commerce/demo-fleet.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const policyGrant = createPolicyGrant({
8888
const budgetAuth = createBudgetAuthorization({
8989
sessionId: "22222222-2222-4222-8222-222222222222",
9090
vehicleId: "veh-robotaxi-001",
91+
grantId: policyGrant.grantId,
9192
policyHash,
9293
currency: "USD",
9394
maxAmountMinor: "3000",
@@ -104,6 +105,7 @@ log("[Vehicle Agent] Generating SettlementIntent and SPA");
104105
const signedBudgetAuth = createSignedBudgetAuthorization({
105106
sessionId: budgetAuth.sessionId,
106107
vehicleId: budgetAuth.vehicleId,
108+
grantId: policyGrant.grantId,
107109
policyHash: budgetAuth.policyHash,
108110
currency: budgetAuth.currency,
109111
maxAmountMinor: budgetAuth.maxAmountMinor,
@@ -145,7 +147,7 @@ const paymentPolicyDecision = {
145147
const signedPaymentAuth = createSignedPaymentAuthorization(
146148
budgetAuth.sessionId,
147149
paymentPolicyDecision,
148-
{ settlementIntent: intent },
150+
{ settlementIntent: intent, budgetId: signedBudgetAuth.authorization.budgetId },
149151
);
150152
if (!signedPaymentAuth) throw new Error("Failed to create SPA");
151153
log(` ✓ SPA signed: amount=${intent.amount}, destination=${intent.destination}`);

0 commit comments

Comments
 (0)