@zkp2p/zkp2p-attestation 1.3.0 → 1.4.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 +15 -0
- package/README.md +90 -13
- package/dist/cli/verify.js +6 -2
- package/dist/cli/verify.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +182 -87
- package/dist/index.d.ts +182 -87
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.0
|
|
4
|
+
|
|
5
|
+
- Add typed identity flow helpers: `encryptIdentitySessionMaterial`, `createEncryptedIdentitySessionMaterial`, and `requestIdentityAttestation`.
|
|
6
|
+
- Add response signature verifiers for all TEE flows: `verifyBuyerTeePaymentAttestation`, `verifyIdentityAttestation`, and `verifySellerCredentialBundle`.
|
|
7
|
+
- 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.
|
|
8
|
+
- Export `getUnifiedPaymentVerifierDomainSeparator` and `hashUtf8` so consumers can pin the EIP-712 domain and compute expected payee hashes.
|
|
9
|
+
- Add identity platform/action/params typing for Venmo, PayPal, and Wise. Wrong platform/action pairings now fail at TypeScript compile time.
|
|
10
|
+
- 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.
|
|
11
|
+
- Move `ethers` to runtime dependencies because public response verifiers recover EIP-712 signers.
|
|
12
|
+
|
|
13
|
+
## 1.3.1
|
|
14
|
+
|
|
15
|
+
- 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.
|
|
16
|
+
- Action: upgrade to 1.3.1. 1.3.0 will be `npm deprecate`'d.
|
|
17
|
+
|
|
3
18
|
## 1.3.0
|
|
4
19
|
|
|
5
20
|
- BREAKING: Buyer TEE verify request body field renamed from `metadata` to `params`. The server Zod schema rejects the legacy `metadata` field. All `*BuyerTeeMetadata` types in `@zkp2p/zkp2p-attestation` are renamed to `*BuyerTeeParams`, and `BuyerTeeMetadataByPlatform` is now `BuyerTeeParamsByPlatform`.
|
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: {
|
|
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: {
|
|
135
|
+
params: { PROFILE_ID: "41246868", TRANSACTION_ID: "123456789" },
|
|
117
136
|
chainId,
|
|
118
137
|
intent,
|
|
119
138
|
}),
|
|
@@ -124,26 +143,63 @@ Buyer TEE platform matrix:
|
|
|
124
143
|
|
|
125
144
|
| Platform | `actionType` | Required encrypted session material | Public params fields |
|
|
126
145
|
|---|---|---|---|
|
|
127
|
-
| `venmo` | `transfer_venmo` | `Cookie` | `
|
|
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` | `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
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
|
-
| `
|
|
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, 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 payee hash, canonical identity `dataHash`, and `validUntil`, then returns normalized
|
|
166
|
+
identity details.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const identityPayload = await nitro.requestIdentityAttestation({
|
|
170
|
+
platform: "wise",
|
|
171
|
+
actionType: "register_wise",
|
|
172
|
+
sessionMaterial: {
|
|
173
|
+
Cookie: "<captured-cookie-header>",
|
|
174
|
+
"X-Access-Token": "<captured-token>",
|
|
175
|
+
},
|
|
176
|
+
params: { PROFILE_ID: "41246868" },
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const identity = verifyIdentityAttestation(identityPayload, {
|
|
180
|
+
trustedSigners,
|
|
181
|
+
expectedPlatform: "wise",
|
|
182
|
+
expectedActionType: "register_wise",
|
|
183
|
+
expectedPayeeIdHash: hashUtf8("<wise-recipient-id>"),
|
|
184
|
+
});
|
|
185
|
+
// identity: { payeeIdHash, payeeId, username, metadata, ... }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Identity platform matrix:
|
|
189
|
+
|
|
190
|
+
| Platform | `actionType` | Required encrypted session material | Public params fields |
|
|
191
|
+
|---|---|---|---|
|
|
192
|
+
| `venmo` | `register_venmo` | `Cookie` | `SENDER_ID`, `index` |
|
|
193
|
+
| `paypal` | `register_paypal` | `Cookie` | `{}` |
|
|
194
|
+
| `wise` | `register_wise` | `Cookie`, `X-Access-Token` | `PROFILE_ID` |
|
|
195
|
+
|
|
196
|
+
Buyer TEE and identity expose discriminated unions keyed by `platform` and `actionType`, including per-platform
|
|
197
|
+
`params`, so invalid pairings fail during TypeScript compilation. Seller credential upload keeps its existing
|
|
198
|
+
platform narrowing (`wise` derives payee id from the credential; Venmo, Cash App, and PayPal require `payeeId`) but
|
|
199
|
+
does not add exhaustive `sessionMaterial` typing: each seller platform can accept multiple credential sources, such as
|
|
200
|
+
cookie headers or Google OAuth with optional partner OAuth client material, and encoding all combinations would make
|
|
201
|
+
the SDK brittle without improving the wire contract.
|
|
202
|
+
|
|
147
203
|
## Cache-Friendly Path
|
|
148
204
|
|
|
149
205
|
```ts
|
|
@@ -171,6 +227,19 @@ const encryptedSessionMaterial = await nitro.encryptBuyerTeeSessionMaterial({
|
|
|
171
227
|
});
|
|
172
228
|
```
|
|
173
229
|
|
|
230
|
+
For identity flows with a cached attestation:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
const encryptedSessionMaterial = await nitro.encryptIdentitySessionMaterial({
|
|
234
|
+
key: verified.attestedSellerUploadKey,
|
|
235
|
+
platform: "paypal",
|
|
236
|
+
actionType: "register_paypal",
|
|
237
|
+
sessionMaterial: {
|
|
238
|
+
Cookie: "<captured-cookie-header>",
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
174
243
|
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
244
|
|
|
176
245
|
## React Native Wiring
|
|
@@ -193,12 +262,20 @@ Any conforming `SubtleCrypto` works. The package does not require `Buffer`.
|
|
|
193
262
|
```ts
|
|
194
263
|
import {
|
|
195
264
|
createEncryptedBuyerTeeSessionMaterial,
|
|
265
|
+
createEncryptedIdentitySessionMaterial,
|
|
196
266
|
createEncryptedSellerCredentialUpload,
|
|
197
267
|
encryptBuyerTeeSessionMaterial,
|
|
268
|
+
encryptIdentitySessionMaterial,
|
|
198
269
|
encryptSellerCredentialUpload,
|
|
199
270
|
fetchAndVerifyAttestation,
|
|
271
|
+
getUnifiedPaymentVerifierDomainSeparator,
|
|
272
|
+
hashUtf8,
|
|
273
|
+
requestIdentityAttestation,
|
|
200
274
|
uploadSellerCredential,
|
|
275
|
+
verifyBuyerTeePaymentAttestation,
|
|
201
276
|
verifyBuyerTeePayment,
|
|
277
|
+
verifyIdentityAttestation,
|
|
278
|
+
verifySellerCredentialBundle,
|
|
202
279
|
} from "@zkp2p/zkp2p-attestation";
|
|
203
280
|
```
|
|
204
281
|
|
package/dist/cli/verify.js
CHANGED
|
@@ -4389,10 +4389,14 @@ async function fetchWithTimeout(url, fetcher, timeoutMs) {
|
|
|
4389
4389
|
}),
|
|
4390
4390
|
timeoutPromise
|
|
4391
4391
|
]);
|
|
4392
|
-
|
|
4392
|
+
if (timeoutHandle !== void 0) {
|
|
4393
|
+
clearTimeout(timeoutHandle);
|
|
4394
|
+
}
|
|
4393
4395
|
return response;
|
|
4394
4396
|
} catch (cause) {
|
|
4395
|
-
|
|
4397
|
+
if (timeoutHandle !== void 0) {
|
|
4398
|
+
clearTimeout(timeoutHandle);
|
|
4399
|
+
}
|
|
4396
4400
|
if (cause instanceof Error && (cause.name === "AbortError" || cause.message === "timeout")) {
|
|
4397
4401
|
fail("TIMEOUT", `GET /attestation timed out after ${timeoutMs}ms`, { cause });
|
|
4398
4402
|
}
|