@zkp2p/zkp2p-attestation 1.1.0 → 1.3.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 +24 -0
- package/README.md +96 -1
- package/dist/cli/verify.js +3 -1
- 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 +303 -2
- package/dist/index.d.ts +303 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
- 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`.
|
|
6
|
+
- BREAKING: Buyer TEE `params` shape no longer accepts duplicate provider-row fields. `N26BuyerTeeParams` is `{}`. `RevolutBuyerTeeParams`, `CitiBuyerTeeParams`, and `BankOfAmericaBuyerTeeParams` are `{ index }` only — `paymentId`, `transactionId`, and `confirmationNumber` are derived from the selected response row.
|
|
7
|
+
- BREAKING: Drop `jweId` from the buyer TEE session-material plaintext and from `EncryptBuyerTeeSessionMaterialBaseParams`. The server enforces a strict plaintext schema and rejects envelopes that still carry `jweId`. Seller credential upload still uses `jweId`.
|
|
8
|
+
- BREAKING: Drop `issuedAtMs` and `now` from `EncryptBuyerTeeSessionMaterialBaseParams` and from the buyer TEE plaintext schema. The server no longer enforces a 5-minute capture-age window on buyer envelopes; encrypted session material is valid for the lifetime of the upstream session (cookies/PAT). Seller credential upload still carries `issuedAtMs` and is unchanged.
|
|
9
|
+
- Migration: regenerate buyer TEE envelopes after upgrading; do not retry an envelope produced by `<1.3.0`. Update buyer TEE call sites to rename `metadata` to `params` and drop the removed per-platform fields (Revolut `paymentId`, Citi `transactionId`, BoA `confirmationNumber`, N26 `paymentId`).
|
|
10
|
+
- Operational note: a leaked encrypted JWE is now valid for the upstream session lifetime — treat any disclosure as equivalent to leaking the underlying upstream credential and rotate the upstream session.
|
|
11
|
+
|
|
12
|
+
## 1.2.0
|
|
13
|
+
|
|
14
|
+
- Expand buyer TEE SDK types from Venmo-only to all 15 server-registered platforms.
|
|
15
|
+
- Tie each buyer TEE platform to its valid `actionType`, metadata shape, and named session-material header map.
|
|
16
|
+
- Require documented buyer TEE session-material headers in the per-platform TypeScript types (e.g. Wise `Cookie` or `X-Access-Token`, Revolut `Cookie` and `x-device-id`). A payload missing the required headers is a type error at the call site.
|
|
17
|
+
- Export `AttestationOutput`, `BuyerTeeVerifier`, `/buyer/supported`, and service response types for callers.
|
|
18
|
+
- Add `verifyBuyerTeePayment()` and `uploadSellerCredential()` one-shot helpers that chain attest → encrypt → POST → unwrap and return a typed result.
|
|
19
|
+
- Enforce `timeoutMs` across buyer TEE verify and seller credential upload POST requests, including through response body reads so stalled streams abort.
|
|
20
|
+
- Preserve structured unsuccessful ServiceResponse details on SDK errors via a new `SERVICE_RESPONSE_ERROR` code carrying `details.responseObject` — enables UIs to consume Wise multi-profile `WISE_PROFILE_SELECTION_REQUIRED` payloads and any future business-logic 4xx.
|
|
21
|
+
- Reject successful ServiceResponse envelopes that omit a `responseObject` (defends against bad-deploy / proxy-rewrite cases that would otherwise resolve helpers with `undefined`).
|
|
22
|
+
- Add buyer TEE discriminated-union type tests for Wise metadata/action narrowing and Revolut header enforcement.
|
|
23
|
+
- Drop the deprecated `verifyingContract` from the new buyer TEE verify helper parameters; the server resolves it from `DEPLOYMENT_ENV`.
|
|
24
|
+
- Keep the internal service-response envelope out of the public SDK type exports.
|
|
25
|
+
|
|
3
26
|
## 1.1.0
|
|
4
27
|
|
|
28
|
+
- Add buyer TEE session-material encryption helpers.
|
|
5
29
|
- Add platform-aware seller credential upload encryption.
|
|
6
30
|
- Support Wise seller credential uploads with `platform: "wise"` and no top-level `payeeId`.
|
|
7
31
|
- Preserve the existing payee-based upload shape for Venmo, Cash App, and PayPal.
|
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.
|
|
3
|
+
Client-side verifier and encryptor for ZKP2P Nitro 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
|
|
|
@@ -57,6 +57,17 @@ await fetch(`${attestationServiceUrl}/seller/credentials/wise`, {
|
|
|
57
57
|
|
|
58
58
|
This fetches `GET /attestation?nonce=...`, verifies the Nitro document to the pinned AWS root and PCR8, extracts the attested seller-upload RSA SPKI, and returns a compact JWE for `POST /seller/credentials/:platform`.
|
|
59
59
|
|
|
60
|
+
For the same flow without hand-rolling the POST:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const credentialBundle = await nitro.uploadSellerCredential({
|
|
64
|
+
platform: "wise",
|
|
65
|
+
sessionMaterial: {
|
|
66
|
+
apiToken: "<wise-api-token>",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
60
71
|
Venmo, Cash App, and PayPal still require a top-level `payeeId`; existing callers can keep using the same call shape:
|
|
61
72
|
|
|
62
73
|
```ts
|
|
@@ -66,6 +77,73 @@ const encryptedUpload = await nitro.createEncryptedSellerCredentialUpload({
|
|
|
66
77
|
});
|
|
67
78
|
```
|
|
68
79
|
|
|
80
|
+
Buyer TEE requests use the same attested upload key and compact JWE envelope. The typed helper encrypts the captured
|
|
81
|
+
session material, posts `{ encryptedSessionMaterial, params, chainId, intent }` to
|
|
82
|
+
`POST /buyer/verify/:platform/:actionType`, unwraps the service envelope, and returns a typed `AttestationOutput`.
|
|
83
|
+
The service does not enforce capture-age or one-use replay limits for buyer TEE session material; verification depends
|
|
84
|
+
on the upstream session still being active. A leaked encrypted JWE is therefore valid for the upstream session
|
|
85
|
+
lifetime — treat any accidental disclosure as equivalent to leaking the underlying upstream credential (cookies, PAT,
|
|
86
|
+
etc.) and rotate the upstream session.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const attestation = await nitro.verifyBuyerTeePayment({
|
|
90
|
+
platform: "venmo",
|
|
91
|
+
actionType: "transfer_venmo",
|
|
92
|
+
sessionMaterial: {
|
|
93
|
+
Cookie: "<captured-cookie-header>",
|
|
94
|
+
"User-Agent": "<captured-user-agent>",
|
|
95
|
+
},
|
|
96
|
+
params: { senderId: "<venmo-account-id>", index: 0 },
|
|
97
|
+
chainId,
|
|
98
|
+
intent,
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The helper is a thin wrapper over the existing wire format; callers can still hand-roll the final POST:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const encryptedSessionMaterial = await nitro.createEncryptedBuyerTeeSessionMaterial({
|
|
106
|
+
platform: "wise",
|
|
107
|
+
actionType: "transfer_wise",
|
|
108
|
+
sessionMaterial: { "X-Access-Token": "<captured-token>" },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await fetch(`${attestationServiceUrl}/buyer/verify/wise/transfer_wise`, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { "content-type": "application/json" },
|
|
114
|
+
body: JSON.stringify({
|
|
115
|
+
encryptedSessionMaterial,
|
|
116
|
+
params: { profileId: "41246868", transactionId: "123456789" },
|
|
117
|
+
chainId,
|
|
118
|
+
intent,
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Buyer TEE platform matrix:
|
|
124
|
+
|
|
125
|
+
| Platform | `actionType` | Required encrypted session material | Public params fields |
|
|
126
|
+
|---|---|---|---|
|
|
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` |
|
|
133
|
+
| `revolut` | `transfer_revolut` | `Cookie`, `x-device-id` | `index` |
|
|
134
|
+
| `idfc` | `transfer_idfc` | `Cookie` | `senderId`, `index` |
|
|
135
|
+
| `citi` | `transfer_zelle` | `Cookie` | `index` |
|
|
136
|
+
| `chime` | `transfer_chime` | `Cookie`, `body` | `{}` |
|
|
137
|
+
| `chase` | `transfer_zelle` | `Cookie`, `x-jpmc-channel`, `x-jpmc-csrf-token`, `Referer`, `Origin` | `index` |
|
|
138
|
+
| `bankofamerica` | `transfer_zelle` | `Cookie` | `index` |
|
|
139
|
+
| `mercadopago` | `transfer_mercadopago` | `Cookie` | `paymentId`, `urlParamsFrom` |
|
|
140
|
+
| `paypal` | `transfer_paypal` | `Cookie` | `index` |
|
|
141
|
+
| `alipay` | `transfer_alipay` | `Cookie` | `tradeNo` |
|
|
142
|
+
|
|
143
|
+
Per-platform session-material types require these headers using the canonical names shown above while still allowing
|
|
144
|
+
additional captured headers. Captured request bodies are session material because they can contain sensitive data and
|
|
145
|
+
are encrypted before being sent to the service.
|
|
146
|
+
|
|
69
147
|
## Cache-Friendly Path
|
|
70
148
|
|
|
71
149
|
```ts
|
|
@@ -80,6 +158,19 @@ const encryptedUpload = await nitro.encryptSellerCredentialUpload({
|
|
|
80
158
|
});
|
|
81
159
|
```
|
|
82
160
|
|
|
161
|
+
For buyer TEE flows with a cached attestation:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const encryptedSessionMaterial = await nitro.encryptBuyerTeeSessionMaterial({
|
|
165
|
+
key: verified.attestedSellerUploadKey,
|
|
166
|
+
platform: "venmo",
|
|
167
|
+
actionType: "transfer_venmo",
|
|
168
|
+
sessionMaterial: {
|
|
169
|
+
Cookie: "<captured-cookie-header>",
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
83
174
|
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.
|
|
84
175
|
|
|
85
176
|
## React Native Wiring
|
|
@@ -101,9 +192,13 @@ Any conforming `SubtleCrypto` works. The package does not require `Buffer`.
|
|
|
101
192
|
|
|
102
193
|
```ts
|
|
103
194
|
import {
|
|
195
|
+
createEncryptedBuyerTeeSessionMaterial,
|
|
104
196
|
createEncryptedSellerCredentialUpload,
|
|
197
|
+
encryptBuyerTeeSessionMaterial,
|
|
105
198
|
encryptSellerCredentialUpload,
|
|
106
199
|
fetchAndVerifyAttestation,
|
|
200
|
+
uploadSellerCredential,
|
|
201
|
+
verifyBuyerTeePayment,
|
|
107
202
|
} from "@zkp2p/zkp2p-attestation";
|
|
108
203
|
```
|
|
109
204
|
|
package/dist/cli/verify.js
CHANGED
|
@@ -4470,6 +4470,9 @@ function requireArray(value, label) {
|
|
|
4470
4470
|
fail("PAYLOAD_FIELD_MISSING", `Attestation payload field ${label} must be an array`);
|
|
4471
4471
|
}
|
|
4472
4472
|
|
|
4473
|
+
// src/timeout.ts
|
|
4474
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
4475
|
+
|
|
4473
4476
|
// src/trust/pins.ts
|
|
4474
4477
|
var STAGING_PCR8 = "5636e3bd96f847cf12cfd9de7faa8cad0e6fa00962ce16ba185f8e5ea57105abb3cc9cc34f1e1e4de3444ceabcca7485";
|
|
4475
4478
|
var PRODUCTION_PCR8 = "41a4ae0b9b96752cab5addb7d22689b3070e564e29f90a54316fa33fa38ea51387a6e887ea4f5a4b0cc34f69cea3f40e";
|
|
@@ -4518,7 +4521,6 @@ function findHostPin(hostPins, hostname) {
|
|
|
4518
4521
|
}
|
|
4519
4522
|
|
|
4520
4523
|
// src/fetchAndVerifyAttestation.ts
|
|
4521
|
-
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
4522
4524
|
var DEFAULT_FRESHNESS_WINDOW_MS = 5 * 60 * 1e3;
|
|
4523
4525
|
async function fetchAndVerifyAttestation(params) {
|
|
4524
4526
|
const fetcher = resolveFetch(params.fetch);
|