@zkp2p/zkp2p-attestation 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.0
4
+
5
+ - Change Venmo identity registration to require the captured stories URL in session material. Venmo identity session material now requires `url`, `params` only requires `SENDER_ID`, and `params.SENDER_ID` must match the URL `externalId`.
6
+ - Require `callerAddress` on identity attestation requests and bind it into the signed `IdentityAttestation`; `verifyIdentityAttestation` now requires `expectedCallerAddress`.
7
+
8
+ ## 1.4.0
9
+
10
+ - Add typed identity flow helpers: `encryptIdentitySessionMaterial`, `createEncryptedIdentitySessionMaterial`, and `requestIdentityAttestation`.
11
+ - Add response signature verifiers for all TEE flows: `verifyBuyerTeePaymentAttestation`, `verifyIdentityAttestation`, and `verifySellerCredentialBundle`.
12
+ - Require caller-provided intent/payee bindings in response verifiers so valid same-domain attestations cannot be replayed for the wrong buyer intent, identity, or seller credential.
13
+ - Export `getUnifiedPaymentVerifierDomainSeparator` and `hashUtf8` so consumers can pin the EIP-712 domain and compute expected payee hashes.
14
+ - Add identity platform/action/params typing for Venmo, PayPal, and Wise. Wrong platform/action pairings now fail at TypeScript compile time.
15
+ - Keep buyer TEE's per-platform param typing and add identity param typing. Seller credential upload keeps its existing Wise-vs-payee-id platform narrowing while leaving `sessionMaterial` flexible because each seller platform can accept multiple credential sources.
16
+ - Move `ethers` to runtime dependencies because public response verifiers recover EIP-712 signers.
17
+
3
18
  ## 1.3.1
4
19
 
5
20
  - Republish of 1.3.0 from a clean build. The 1.3.0 tarball on npm shipped a stale `dist/` (built from a pre-rebase checkout): the runtime `dist/index.cjs` still posted the legacy `metadata` field and the typings still exported `*BuyerTeeMetadata` / `BuyerTeeMetadataByPlatform`. The new server only accepts `params`, so consumers on 1.3.0 hit a 4xx on every buyer TEE verify call.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @zkp2p/zkp2p-attestation
2
2
 
3
- Client-side verifier and encryptor for ZKP2P Nitro seller credential uploads and buyer TEE session-material requests.
3
+ Client-side verifier and encryptor for ZKP2P Nitro identity attestations, seller credential uploads, and buyer TEE session-material requests.
4
4
 
5
5
  The package runs in Chrome MV3 service workers, browser DOM contexts, React Native with a Web Crypto polyfill, and Node >= 20. Library entrypoints use `fetch`, `SubtleCrypto`, `crypto.getRandomValues`, and `Uint8Array`; they do not import `node:*` modules or use `Buffer`.
6
6
 
@@ -64,8 +64,15 @@ const credentialBundle = await nitro.uploadSellerCredential({
64
64
  platform: "wise",
65
65
  sessionMaterial: {
66
66
  apiToken: "<wise-api-token>",
67
+ profileId: "41246868",
67
68
  },
68
69
  });
70
+
71
+ const verifiedCredentialBundle = verifySellerCredentialBundle(credentialBundle, {
72
+ trustedSigners,
73
+ expectedPlatform: "wise",
74
+ expectedPayeeIdHash: hashUtf8("<wise-recipient-id>"),
75
+ });
69
76
  ```
70
77
 
71
78
  Venmo, Cash App, and PayPal still require a top-level `payeeId`; existing callers can keep using the same call shape:
@@ -77,6 +84,10 @@ const encryptedUpload = await nitro.createEncryptedSellerCredentialUpload({
77
84
  });
78
85
  ```
79
86
 
87
+ Venmo seller uploads can use either the existing session material or Google OAuth. For Google OAuth, pass the numeric Venmo account id as `payeeId` and use `sessionMaterial: { credentialSource: "google_oauth", authorizationCode, redirectUri }`; the service validates Gmail access against venmo.com DKIM-signed incoming payment receipts. To onboard through a partner-owned Google OAuth app instead of Peer's default app, include `oauthClient: { clientId, clientSecret }` inside that encrypted `sessionMaterial`. The client secret is seller credential material: the package only places it in the compact JWE upload plaintext, it reaches the service enclave through that JWE, and the service persists it only inside the KMS-envelope-sealed seller credential bundle that the curator holds as ciphertext. It rides the same encrypted credential envelope as the Gmail refresh token and is reused only for later Gmail token refresh.
88
+
89
+ PayPal Gmail OAuth seller uploads use the same app-selection model. Pass the seller PayPal email as `payeeId` and use `sessionMaterial: { credentialSource: "google_oauth", authorizationCode, redirectUri }`; the service validates Gmail access against paypal.com DKIM-signed payment receipts. To onboard through a partner-owned Google OAuth app, include `oauthClient: { clientId, clientSecret }` inside the encrypted `sessionMaterial`. The PayPal partner client secret is handled as seller credential material: it rides only in the compact JWE upload plaintext, is sealed with the Gmail refresh token in the KMS-envelope seller credential bundle, and is reused only to refresh that seller's Gmail access token.
90
+
80
91
  Buyer TEE requests use the same attested upload key and compact JWE envelope. The typed helper encrypts the captured
81
92
  session material, posts `{ encryptedSessionMaterial, params, chainId, intent }` to
82
93
  `POST /buyer/verify/:platform/:actionType`, unwraps the service envelope, and returns a typed `AttestationOutput`.
@@ -93,10 +104,18 @@ const attestation = await nitro.verifyBuyerTeePayment({
93
104
  Cookie: "<captured-cookie-header>",
94
105
  "User-Agent": "<captured-user-agent>",
95
106
  },
96
- params: { senderId: "<venmo-account-id>", index: 0 },
107
+ params: { SENDER_ID: "<venmo-account-id>", index: 0 },
97
108
  chainId,
98
109
  intent,
99
110
  });
111
+
112
+ const verifiedAttestation = verifyBuyerTeePaymentAttestation(attestation, {
113
+ trustedSigners,
114
+ expectedPlatform: "venmo",
115
+ expectedActionType: "transfer_venmo",
116
+ expectedDomain: { chainId, verifyingContract },
117
+ expectedIntentHash: intent.intentHash,
118
+ });
100
119
  ```
101
120
 
102
121
  The helper is a thin wrapper over the existing wire format; callers can still hand-roll the final POST:
@@ -113,7 +132,7 @@ await fetch(`${attestationServiceUrl}/buyer/verify/wise/transfer_wise`, {
113
132
  headers: { "content-type": "application/json" },
114
133
  body: JSON.stringify({
115
134
  encryptedSessionMaterial,
116
- params: { profileId: "41246868", transactionId: "123456789" },
135
+ params: { PROFILE_ID: "41246868", TRANSACTION_ID: "123456789" },
117
136
  chainId,
118
137
  intent,
119
138
  }),
@@ -124,26 +143,69 @@ Buyer TEE platform matrix:
124
143
 
125
144
  | Platform | `actionType` | Required encrypted session material | Public params fields |
126
145
  |---|---|---|---|
127
- | `venmo` | `transfer_venmo` | `Cookie` | `senderId`, `index` |
128
- | `cashapp` | `transfer_cashapp` | `Cookie`, `x-csrf-token`, `x-device-name`, `x-request-signature`, `x-request-uuid`, `cash-web-request`, `x-web-device-info`, `x-web-context`, `x-bt-id` | `senderId`, `index` |
129
- | `luxon` | `transfer_luxon` | `X-Auth-Token` | `transferId` |
130
- | `monzo` | `transfer_monzo` | `Authorization` | `txId` |
131
- | `n26` | `transfer_n26` | `Cookie`, `csrf-token`, `body` | `{}` |
132
- | `wise` | `transfer_wise` | `Cookie` or `X-Access-Token` | `profileId`, `transactionId` |
146
+ | `venmo` | `transfer_venmo` | `Cookie` | `SENDER_ID`, `index` |
147
+ | `cashapp` | `transfer_cashapp` | `Cookie`, `x-csrf-token`, `x-device-name`, `x-request-signature`, `x-request-uuid`, `cash-web-request`, `x-web-device-info`, `x-web-context`, `x-bt-id` | `SENDER_ID`, `index` |
148
+ | `monzo` | `transfer_monzo` | `Authorization` | `TX_ID` |
149
+ | `wise` | `transfer_wise` | `Cookie` or `X-Access-Token` | `PROFILE_ID`, `TRANSACTION_ID` |
133
150
  | `revolut` | `transfer_revolut` | `Cookie`, `x-device-id` | `index` |
134
- | `idfc` | `transfer_idfc` | `Cookie` | `senderId`, `index` |
135
151
  | `citi` | `transfer_zelle` | `Cookie` | `index` |
136
152
  | `chime` | `transfer_chime` | `Cookie`, `body` | `{}` |
137
153
  | `chase` | `transfer_zelle` | `Cookie`, `x-jpmc-channel`, `x-jpmc-csrf-token`, `Referer`, `Origin` | `index` |
138
154
  | `bankofamerica` | `transfer_zelle` | `Cookie` | `index` |
139
- | `mercadopago` | `transfer_mercadopago` | `Cookie` | `paymentId`, `urlParamsFrom` |
140
- | `paypal` | `transfer_paypal` | `Cookie` | `index` |
141
- | `alipay` | `transfer_alipay` | `Cookie` | `tradeNo` |
155
+ | `paypal` | `transfer_paypal` | `Cookie` | `PAYMENT_ID` |
142
156
 
143
157
  Per-platform session-material types require these headers using the canonical names shown above while still allowing
144
158
  additional captured headers. Captured request bodies are session material because they can contain sensitive data and
145
159
  are encrypted before being sent to the service.
146
160
 
161
+ Identity requests use the same attested upload key and compact JWE plaintext shape as buyer TEE, but with a narrower
162
+ platform/action matrix. The typed helper encrypts captured session material, posts
163
+ `{ platform, actionType, callerAddress, encryptedSessionMaterial, params }` to `POST /identity`, unwraps the service response, and
164
+ returns an `IdentityAttestationOutput`. `verifyIdentityAttestation` checks the EIP-712 signature, trusted signer,
165
+ platform/action binding, expected caller address, expected payee hash, canonical identity `dataHash`, and `validUntil`,
166
+ then returns normalized identity details.
167
+
168
+ ```ts
169
+ const identityPayload = await nitro.requestIdentityAttestation({
170
+ platform: "wise",
171
+ actionType: "register_wise",
172
+ callerAddress: "0x0000000000000000000000000000000000000002",
173
+ sessionMaterial: {
174
+ Cookie: "<captured-cookie-header>",
175
+ "X-Access-Token": "<captured-token>",
176
+ },
177
+ params: { PROFILE_ID: "41246868" },
178
+ });
179
+
180
+ const identity = verifyIdentityAttestation(identityPayload, {
181
+ trustedSigners,
182
+ expectedPlatform: "wise",
183
+ expectedActionType: "register_wise",
184
+ expectedCallerAddress: "0x0000000000000000000000000000000000000002",
185
+ expectedPayeeIdHash: hashUtf8("<wise-recipient-id>"),
186
+ });
187
+ // identity: { payeeIdHash, payeeId, username, metadata, ... }
188
+ ```
189
+
190
+ Identity platform matrix:
191
+
192
+ | Platform | `actionType` | Required encrypted session material | Public params fields |
193
+ |---|---|---|---|
194
+ | `venmo` | `register_venmo` | `url`, `Cookie` | `SENDER_ID` |
195
+ | `paypal` | `register_paypal` | `Cookie` | `{}` |
196
+ | `wise` | `register_wise` | `Cookie`, `X-Access-Token` | `PROFILE_ID` |
197
+
198
+ For Venmo identity, pass the captured `https://account.venmo.com/api/stories?feedType=me&externalId=...` request URL
199
+ inside encrypted `sessionMaterial.url`; the service validates that `params.SENDER_ID` matches the URL `externalId` and
200
+ that Venmo returns a valid stories response with an explicit `stories` array.
201
+
202
+ Buyer TEE and identity expose discriminated unions keyed by `platform` and `actionType`, including per-platform
203
+ `params`, so invalid pairings fail during TypeScript compilation. Seller credential upload keeps its existing
204
+ platform narrowing (`wise` derives payee id from the credential; Venmo, Cash App, and PayPal require `payeeId`) but
205
+ does not add exhaustive `sessionMaterial` typing: each seller platform can accept multiple credential sources, such as
206
+ cookie headers or Google OAuth with optional partner OAuth client material, and encoding all combinations would make
207
+ the SDK brittle without improving the wire contract.
208
+
147
209
  ## Cache-Friendly Path
148
210
 
149
211
  ```ts
@@ -171,6 +233,19 @@ const encryptedSessionMaterial = await nitro.encryptBuyerTeeSessionMaterial({
171
233
  });
172
234
  ```
173
235
 
236
+ For identity flows with a cached attestation:
237
+
238
+ ```ts
239
+ const encryptedSessionMaterial = await nitro.encryptIdentitySessionMaterial({
240
+ key: verified.attestedSellerUploadKey,
241
+ platform: "paypal",
242
+ actionType: "register_paypal",
243
+ sessionMaterial: {
244
+ Cookie: "<captured-cookie-header>",
245
+ },
246
+ });
247
+ ```
248
+
174
249
  The attested SPKI is stable within one enclave process. If the enclave restarts, cached keys become stale and upload decrypt will fail; fetch a fresh attestation for a new session.
175
250
 
176
251
  ## React Native Wiring
@@ -193,12 +268,20 @@ Any conforming `SubtleCrypto` works. The package does not require `Buffer`.
193
268
  ```ts
194
269
  import {
195
270
  createEncryptedBuyerTeeSessionMaterial,
271
+ createEncryptedIdentitySessionMaterial,
196
272
  createEncryptedSellerCredentialUpload,
197
273
  encryptBuyerTeeSessionMaterial,
274
+ encryptIdentitySessionMaterial,
198
275
  encryptSellerCredentialUpload,
199
276
  fetchAndVerifyAttestation,
277
+ getUnifiedPaymentVerifierDomainSeparator,
278
+ hashUtf8,
279
+ requestIdentityAttestation,
200
280
  uploadSellerCredential,
281
+ verifyBuyerTeePaymentAttestation,
201
282
  verifyBuyerTeePayment,
283
+ verifyIdentityAttestation,
284
+ verifySellerCredentialBundle,
202
285
  } from "@zkp2p/zkp2p-attestation";
203
286
  ```
204
287
 
@@ -4672,7 +4672,7 @@ Options:
4672
4672
  --expected-pcr8 <hex> Strict PCR8 pin
4673
4673
  --expected-signer <addr> Strict signer pin for --verify-signature
4674
4674
  --expected-verifying-contract <addr> Strict verifyingContract pin
4675
- --verify-signature <path> Optional path to a prior /verify or /seller/verify JSON response
4675
+ --verify-signature <path> Optional path to a prior buyer or seller verify JSON response
4676
4676
  --help Show this help text
4677
4677
  `);
4678
4678
  }