@zkpassport/sdk 0.3.4 → 0.4.1

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.
@@ -1,11 +1,11 @@
1
- import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode } from "@zkpassport/utils";
1
+ import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode, BoundData } from "@zkpassport/utils";
2
2
  export type QueryResultError<T> = {
3
3
  expected?: T;
4
4
  received?: T;
5
5
  message: string;
6
6
  };
7
7
  export type QueryResultErrors = {
8
- [key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose"]: {
8
+ [key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose" | "bind"]: {
9
9
  disclose?: QueryResultError<string | number | Date>;
10
10
  gte?: QueryResultError<number | Date>;
11
11
  lte?: QueryResultError<number | Date>;
@@ -150,6 +150,12 @@ export type QueryBuilder = {
150
150
  * @param key The attribute to disclose.
151
151
  */
152
152
  disclose: (key: DisclosableIDCredential) => QueryBuilder;
153
+ /**
154
+ * Binds a value to the request.
155
+ * @param key The key of the value to bind.
156
+ * @param value The value to bind the request to.
157
+ */
158
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder;
153
159
  /**
154
160
  * Builds the request.
155
161
  *
@@ -163,9 +169,8 @@ export declare class ZKPassport {
163
169
  private domain;
164
170
  private topicToConfig;
165
171
  private topicToLocalConfig;
166
- private topicToKeyPair;
167
- private topicToWebSocketClient;
168
- private topicToSharedSecret;
172
+ private topicToPublicKey;
173
+ private topicToBridge;
169
174
  private topicToRequestReceived;
170
175
  private topicToService;
171
176
  private topicToProofs;
@@ -197,14 +202,16 @@ export declare class ZKPassport {
197
202
  * @param scope Scope this request to a specific use case
198
203
  * @param validity How many days ago should have the ID been last scanned by the user?
199
204
  * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
205
+ * @param evmChain The EVM chain to use for the request (if using the proof onchain)
200
206
  * @returns The query builder object.
201
207
  */
202
- request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }: {
208
+ request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }: {
203
209
  name: string;
204
210
  logo: string;
205
211
  purpose: string;
206
212
  scope?: string;
207
213
  mode?: ProofMode;
214
+ evmChain?: EVMChain;
208
215
  validity?: number;
209
216
  devMode?: boolean;
210
217
  topicOverride?: string;
@@ -223,20 +230,25 @@ export declare class ZKPassport {
223
230
  private checkIssuingCountryInclusionPublicInputs;
224
231
  private checkScopeFromDisclosureProof;
225
232
  private checkCertificateRegistryRoot;
233
+ private checkBindPublicInputs;
226
234
  private checkPublicInputs;
227
235
  /**
228
236
  * @notice Verify the proofs received from the mobile app.
229
237
  * @param proofs The proofs to verify.
230
238
  * @param queryResult The query result to verify against
231
239
  * @param validity How many days ago should have the ID been last scanned by the user?
240
+ * @param scope Scope this request to a specific use case
241
+ * @param evmChain The EVM chain to use for the verification (if using the proof onchain)
242
+ * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
232
243
  * @returns An object containing the unique identifier associated to the user
233
244
  * and a boolean indicating whether the proofs were successfully verified.
234
245
  */
235
- verify({ proofs, queryResult, validity, scope, devMode, }: {
246
+ verify({ proofs, queryResult, validity, scope, evmChain, devMode, }: {
236
247
  proofs: Array<ProofResult>;
237
248
  queryResult: QueryResult;
238
249
  validity?: number;
239
250
  scope?: string;
251
+ evmChain?: EVMChain;
240
252
  devMode?: boolean;
241
253
  }): Promise<{
242
254
  uniqueIdentifier: string | undefined;
package/dist/cjs/index.js CHANGED
@@ -2,21 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZKPassport = exports.MERCOSUR_COUNTRIES = exports.ASEAN_COUNTRIES = exports.SCHENGEN_COUNTRIES = exports.EEA_COUNTRIES = exports.EU_COUNTRIES = exports.SANCTIONED_COUNTRIES = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const crypto_1 = require("crypto");
6
5
  const i18n_iso_countries_1 = require("i18n-iso-countries");
7
6
  const utils_1 = require("@zkpassport/utils");
8
7
  const utils_2 = require("@noble/ciphers/utils");
9
- const websocket_1 = require("./websocket");
10
- const json_rpc_1 = require("./json-rpc");
11
- const encryption_1 = require("./encryption");
12
8
  const logger_1 = require("./logger");
13
- const pako_1 = require("pako");
14
9
  const en_json_1 = tslib_1.__importDefault(require("i18n-iso-countries/langs/en.json"));
15
10
  const buffer_1 = require("buffer/");
16
11
  const sha2_1 = require("@noble/hashes/sha2");
17
12
  const utils_3 = require("@noble/hashes/utils");
18
13
  const ZKPassportVerifier_json_1 = tslib_1.__importDefault(require("./assets/abi/ZKPassportVerifier.json"));
19
14
  const registry_1 = require("@zkpassport/registry");
15
+ const bridge_1 = require("@obsidion/bridge");
20
16
  const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
21
17
  // If Buffer is not defined, then we use the Buffer from the buffer package
22
18
  if (typeof globalThis.Buffer === "undefined") {
@@ -25,6 +21,24 @@ if (typeof globalThis.Buffer === "undefined") {
25
21
  window.Buffer = buffer_1.Buffer;
26
22
  }
27
23
  }
24
+ function getChainIdFromEVMChain(chain) {
25
+ if (chain === "ethereum_sepolia") {
26
+ return 11155111;
27
+ }
28
+ else if (chain === "local_anvil") {
29
+ return 31337;
30
+ }
31
+ throw new Error(`Unsupported chain: ${chain}`);
32
+ }
33
+ function getEVMChainFromChainId(chainId) {
34
+ if (chainId === 11155111) {
35
+ return "ethereum_sepolia";
36
+ }
37
+ else if (chainId === 31337) {
38
+ return "local_anvil";
39
+ }
40
+ throw new Error(`Unsupported chain ID: ${chainId}`);
41
+ }
28
42
  (0, i18n_iso_countries_1.registerLocale)(en_json_1.default);
29
43
  function hasRequestedAccessToField(credentialsRequest, field) {
30
44
  const fieldValue = credentialsRequest[field];
@@ -79,9 +93,8 @@ class ZKPassport {
79
93
  constructor(_domain) {
80
94
  this.topicToConfig = {};
81
95
  this.topicToLocalConfig = {};
82
- this.topicToKeyPair = {};
83
- this.topicToWebSocketClient = {};
84
- this.topicToSharedSecret = {};
96
+ this.topicToPublicKey = {};
97
+ this.topicToBridge = {};
85
98
  this.topicToRequestReceived = {};
86
99
  this.topicToService = {};
87
100
  this.topicToProofs = {};
@@ -110,6 +123,9 @@ class ZKPassport {
110
123
  queryResult: result,
111
124
  validity: this.topicToLocalConfig[topic]?.validity,
112
125
  scope: this.topicToService[topic]?.scope,
126
+ evmChain: this.topicToService[topic]?.chainId
127
+ ? getEVMChainFromChainId(this.topicToService[topic]?.chainId)
128
+ : undefined,
113
129
  devMode: this.topicToLocalConfig[topic]?.devMode,
114
130
  });
115
131
  delete this.topicToProofs[topic];
@@ -185,6 +201,9 @@ class ZKPassport {
185
201
  }
186
202
  }
187
203
  }
204
+ if (this.topicToConfig[topic].bind) {
205
+ neededCircuits.push("bind");
206
+ }
188
207
  // From the circuits needed, determine the expected proof count
189
208
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
190
209
  // Each separate needed circuit adds 1 disclosure proof
@@ -197,7 +216,7 @@ class ZKPassport {
197
216
  * @param request The request.
198
217
  * @param outerRequest The outer request.
199
218
  */
200
- async handleEncryptedMessage(topic, request, outerRequest) {
219
+ async handleEncryptedMessage(topic, request) {
201
220
  logger_1.noLogger.debug("Received encrypted message:", request);
202
221
  if (request.method === "accept") {
203
222
  logger_1.noLogger.debug(`User accepted the request and is generating a proof`);
@@ -209,30 +228,8 @@ class ZKPassport {
209
228
  }
210
229
  else if (request.method === "proof") {
211
230
  logger_1.noLogger.debug(`User generated proof`);
212
- // Uncompress the proof and convert it to a hex string
213
- const bytesProof = buffer_1.Buffer.from(request.params.proof, "base64");
214
- const bytesCommittedInputs = request.params.committedInputs
215
- ? buffer_1.Buffer.from(request.params.committedInputs, "base64")
216
- : null;
217
- const uncompressedProof = (0, pako_1.inflate)(bytesProof);
218
- const uncompressedCommittedInputs = bytesCommittedInputs
219
- ? (0, pako_1.inflate)(bytesCommittedInputs)
220
- : null;
221
- // The gzip lib in the app compress the proof as ASCII
222
- // and since the app passes the proof as a hex string, we can
223
- // just decode the bytes as hex characters using the TextDecoder
224
- const hexProof = new TextDecoder().decode(uncompressedProof);
225
- const processedProof = {
226
- proof: hexProof,
227
- vkeyHash: request.params.vkeyHash,
228
- name: request.params.name,
229
- version: request.params.version,
230
- committedInputs: uncompressedCommittedInputs
231
- ? JSON.parse(new TextDecoder().decode(uncompressedCommittedInputs))
232
- : undefined,
233
- };
234
- this.topicToProofs[topic].push(processedProof);
235
- await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
231
+ this.topicToProofs[topic].push(request.params);
232
+ await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(request.params)));
236
233
  // If the results were received before all the proofs were generated,
237
234
  // we can handle the result now
238
235
  if (this.topicToResults[topic] &&
@@ -329,10 +326,17 @@ class ZKPassport {
329
326
  };
330
327
  return this.getZkPassportRequest(topic);
331
328
  },
329
+ bind: (key, value) => {
330
+ this.topicToConfig[topic].bind = {
331
+ ...this.topicToConfig[topic].bind,
332
+ [key]: value,
333
+ };
334
+ return this.getZkPassportRequest(topic);
335
+ },
332
336
  done: () => {
333
337
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
334
338
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
335
- const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[topic].publicKey);
339
+ const pubkey = this.topicToPublicKey[topic];
336
340
  this.setExpectedProofCount(topic);
337
341
  return {
338
342
  url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
@@ -344,7 +348,7 @@ class ZKPassport {
344
348
  onResult: (callback) => this.onResultCallbacks[topic].push(callback),
345
349
  onReject: (callback) => this.onRejectCallbacks[topic].push(callback),
346
350
  onError: (callback) => this.onErrorCallbacks[topic].push(callback),
347
- isBridgeConnected: () => this.topicToWebSocketClient[topic].readyState === WebSocket.OPEN,
351
+ isBridgeConnected: () => this.topicToBridge[topic].isBridgeConnected(),
348
352
  requestReceived: () => this.topicToRequestReceived[topic] === true,
349
353
  };
350
354
  },
@@ -358,17 +362,23 @@ class ZKPassport {
358
362
  * @param scope Scope this request to a specific use case
359
363
  * @param validity How many days ago should have the ID been last scanned by the user?
360
364
  * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
365
+ * @param evmChain The EVM chain to use for the request (if using the proof onchain)
361
366
  * @returns The query builder object.
362
367
  */
363
- async request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }) {
364
- const topic = topicOverride || (0, crypto_1.randomBytes)(16).toString("hex");
365
- const keyPair = keyPairOverride || (await (0, encryption_1.generateECDHKeyPair)());
366
- this.topicToKeyPair[topic] = {
367
- privateKey: keyPair.privateKey,
368
- publicKey: keyPair.publicKey,
369
- };
368
+ async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }) {
369
+ const bridge = await bridge_1.Bridge.create({
370
+ keyPair: keyPairOverride,
371
+ bridgeId: topicOverride,
372
+ });
373
+ const topic = bridge.connection.getBridgeId();
370
374
  this.topicToConfig[topic] = {};
371
- this.topicToService[topic] = { name, logo, purpose, scope };
375
+ this.topicToService[topic] = {
376
+ name,
377
+ logo,
378
+ purpose,
379
+ scope,
380
+ chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
381
+ };
372
382
  this.topicToProofs[topic] = [];
373
383
  this.topicToExpectedProofCount[topic] = 0;
374
384
  this.topicToLocalConfig[topic] = {
@@ -384,53 +394,21 @@ class ZKPassport {
384
394
  this.onResultCallbacks[topic] = [];
385
395
  this.onRejectCallbacks[topic] = [];
386
396
  this.onErrorCallbacks[topic] = [];
387
- const wsClient = (0, websocket_1.getWebSocketClient)(`wss://bridge.zkpassport.id?topic=${topic}`, this.domain);
388
- this.topicToWebSocketClient[topic] = wsClient;
389
- wsClient.onopen = async () => {
390
- logger_1.noLogger.info("[frontend] WebSocket connection established");
397
+ this.topicToPublicKey[topic] = bridge.getPublicKey();
398
+ this.topicToBridge[topic] = bridge;
399
+ bridge.onConnect(async (reconnection) => {
400
+ logger_1.noLogger.debug("Bridge connected");
401
+ logger_1.noLogger.debug("Is reconnection:", reconnection);
391
402
  await Promise.all(this.onBridgeConnectCallbacks[topic].map((callback) => callback()));
392
- };
393
- wsClient.addEventListener("message", async (event) => {
394
- logger_1.noLogger.debug("[frontend] Received message:", event.data);
395
- try {
396
- const data = JSON.parse(event.data);
397
- // Handshake happens when the mobile app scans the QR code and connects to the bridge
398
- if (data.method === "handshake") {
399
- logger_1.noLogger.debug("[frontend] Received handshake:", event.data);
400
- this.topicToRequestReceived[topic] = true;
401
- this.topicToSharedSecret[topic] = await (0, encryption_1.getSharedSecret)((0, utils_2.bytesToHex)(keyPair.privateKey), data.params.pubkey);
402
- logger_1.noLogger.debug("[frontend] Shared secret:", buffer_1.Buffer.from(this.topicToSharedSecret[topic]).toString("hex"));
403
- const encryptedMessage = await (0, json_rpc_1.createEncryptedJsonRpcRequest)("hello", null, this.topicToSharedSecret[topic], topic);
404
- logger_1.noLogger.debug("[frontend] Sending encrypted message:", encryptedMessage);
405
- wsClient.send(JSON.stringify(encryptedMessage));
406
- await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()));
407
- return;
408
- }
409
- // Handle encrypted messages
410
- if (data.method === "encryptedMessage") {
411
- // Decode the payload from base64 to Uint8Array
412
- const payload = new Uint8Array(atob(data.params.payload)
413
- .split("")
414
- .map((c) => c.charCodeAt(0)));
415
- try {
416
- // Decrypt the payload using the shared secret
417
- const decrypted = await (0, encryption_1.decrypt)(payload, this.topicToSharedSecret[topic], topic);
418
- const decryptedJson = JSON.parse(decrypted);
419
- this.handleEncryptedMessage(topic, decryptedJson, data);
420
- }
421
- catch (error) {
422
- logger_1.noLogger.error("[frontend] Error decrypting message:", error);
423
- }
424
- return;
425
- }
426
- }
427
- catch (error) {
428
- logger_1.noLogger.error("[frontend] Error:", error);
429
- }
430
403
  });
431
- wsClient.onerror = (error) => {
432
- logger_1.noLogger.error("[frontend] WebSocket error:", error);
433
- };
404
+ bridge.onSecureChannelEstablished(async () => {
405
+ logger_1.noLogger.debug("Secure channel established");
406
+ await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()));
407
+ });
408
+ bridge.onSecureMessage(async (message) => {
409
+ logger_1.noLogger.debug("Received message:", message);
410
+ this.handleEncryptedMessage(topic, message);
411
+ });
434
412
  return this.getZkPassportRequest(topic);
435
413
  }
436
414
  checkDiscloseBytesPublicInputs(proof, queryResult) {
@@ -451,6 +429,7 @@ class ZKPassport {
451
429
  fullname: {},
452
430
  document_number: {},
453
431
  outer: {},
432
+ bind: {},
454
433
  };
455
434
  let isCorrect = true;
456
435
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -763,6 +742,7 @@ class ZKPassport {
763
742
  fullname: {},
764
743
  document_number: {},
765
744
  outer: {},
745
+ bind: {},
766
746
  };
767
747
  let isCorrect = true;
768
748
  const currentTime = new Date();
@@ -872,6 +852,7 @@ class ZKPassport {
872
852
  fullname: {},
873
853
  document_number: {},
874
854
  outer: {},
855
+ bind: {},
875
856
  };
876
857
  let isCorrect = true;
877
858
  const currentTime = new Date();
@@ -975,6 +956,7 @@ class ZKPassport {
975
956
  fullname: {},
976
957
  document_number: {},
977
958
  outer: {},
959
+ bind: {},
978
960
  };
979
961
  let isCorrect = true;
980
962
  const currentTime = new Date();
@@ -1078,6 +1060,7 @@ class ZKPassport {
1078
1060
  fullname: {},
1079
1061
  document_number: {},
1080
1062
  outer: {},
1063
+ bind: {},
1081
1064
  };
1082
1065
  let isCorrect = true;
1083
1066
  if (queryResult.nationality &&
@@ -1132,6 +1115,7 @@ class ZKPassport {
1132
1115
  fullname: {},
1133
1116
  document_number: {},
1134
1117
  outer: {},
1118
+ bind: {},
1135
1119
  };
1136
1120
  let isCorrect = true;
1137
1121
  if (queryResult.issuing_country &&
@@ -1186,6 +1170,7 @@ class ZKPassport {
1186
1170
  fullname: {},
1187
1171
  document_number: {},
1188
1172
  outer: {},
1173
+ bind: {},
1189
1174
  };
1190
1175
  let isCorrect = true;
1191
1176
  if (queryResult.nationality &&
@@ -1228,6 +1213,7 @@ class ZKPassport {
1228
1213
  fullname: {},
1229
1214
  document_number: {},
1230
1215
  outer: {},
1216
+ bind: {},
1231
1217
  };
1232
1218
  let isCorrect = true;
1233
1219
  if (queryResult.issuing_country &&
@@ -1252,13 +1238,14 @@ class ZKPassport {
1252
1238
  }
1253
1239
  return { isCorrect, queryResultErrors };
1254
1240
  }
1255
- checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope) {
1241
+ checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope, chainId) {
1256
1242
  let isCorrect = true;
1257
- if (this.domain && (0, utils_1.getScopeHash)(this.domain) !== BigInt(proofData.publicInputs[1])) {
1243
+ if (this.domain &&
1244
+ (0, utils_1.getServiceScopeHash)(this.domain, chainId) !== BigInt(proofData.publicInputs[1])) {
1258
1245
  console.warn("The proof comes from a different domain than the one expected");
1259
1246
  isCorrect = false;
1260
1247
  queryResultErrors[key].scope = {
1261
- expected: `Scope: ${(0, utils_1.getScopeHash)(this.domain).toString()}`,
1248
+ expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
1262
1249
  received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
1263
1250
  message: "The proof comes from a different domain than the one expected",
1264
1251
  };
@@ -1302,7 +1289,52 @@ class ZKPassport {
1302
1289
  }
1303
1290
  return { isCorrect, queryResultErrors };
1304
1291
  }
1305
- async checkPublicInputs(proofs, queryResult, validity, scope) {
1292
+ checkBindPublicInputs(queryResult, boundData) {
1293
+ const queryResultErrors = {
1294
+ sig_check_dsc: {},
1295
+ sig_check_id_data: {},
1296
+ data_check_integrity: {},
1297
+ disclose: {},
1298
+ age: {},
1299
+ birthdate: {},
1300
+ expiry_date: {},
1301
+ document_type: {},
1302
+ issuing_country: {},
1303
+ gender: {},
1304
+ nationality: {},
1305
+ firstname: {},
1306
+ lastname: {},
1307
+ fullname: {},
1308
+ document_number: {},
1309
+ outer: {},
1310
+ bind: {},
1311
+ };
1312
+ let isCorrect = true;
1313
+ if (queryResult.bind) {
1314
+ if (queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1315
+ boundData.user_address?.toLowerCase().replace("0x", "")) {
1316
+ console.warn("Bound user address does not match the one from the query results");
1317
+ isCorrect = false;
1318
+ queryResultErrors.bind.eq = {
1319
+ expected: queryResult.bind.user_address,
1320
+ received: boundData.user_address,
1321
+ message: "Bound user address does not match the one from the query results",
1322
+ };
1323
+ }
1324
+ if (queryResult.bind.custom_data?.trim().toLowerCase() !==
1325
+ boundData.custom_data?.trim().toLowerCase()) {
1326
+ console.warn("Bound custom data does not match the one from the query results");
1327
+ isCorrect = false;
1328
+ queryResultErrors.bind.eq = {
1329
+ expected: queryResult.bind.custom_data,
1330
+ received: boundData.custom_data,
1331
+ message: "Bound custom data does not match the one from the query results",
1332
+ };
1333
+ }
1334
+ }
1335
+ return { isCorrect, queryResultErrors };
1336
+ }
1337
+ async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
1306
1338
  let commitmentIn;
1307
1339
  let commitmentOut;
1308
1340
  let isCorrect = true;
@@ -1326,6 +1358,7 @@ class ZKPassport {
1326
1358
  fullname: {},
1327
1359
  document_number: {},
1328
1360
  outer: {},
1361
+ bind: {},
1329
1362
  };
1330
1363
  // Since the order is important for the commitments, we need to sort the proofs
1331
1364
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -1342,6 +1375,7 @@ class ZKPassport {
1342
1375
  "inclusion_check_nationality",
1343
1376
  "exclusion_check_issuing_country",
1344
1377
  "inclusion_check_issuing_country",
1378
+ "bind",
1345
1379
  ];
1346
1380
  const getIndex = (proof) => {
1347
1381
  const name = proof.name || "";
@@ -1387,11 +1421,12 @@ class ZKPassport {
1387
1421
  message: "The proof does not verify all the requested conditions and information",
1388
1422
  };
1389
1423
  }
1390
- if (this.domain && (0, utils_1.getScopeHash)(this.domain) !== (0, utils_1.getScopeFromOuterProof)(proofData)) {
1424
+ if (this.domain &&
1425
+ (0, utils_1.getServiceScopeHash)(this.domain, chainId) !== (0, utils_1.getScopeFromOuterProof)(proofData)) {
1391
1426
  console.warn("The proof comes from a different domain than the one expected");
1392
1427
  isCorrect = false;
1393
1428
  queryResultErrors.outer.scope = {
1394
- expected: `Scope: ${(0, utils_1.getScopeHash)(this.domain).toString()}`,
1429
+ expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
1395
1430
  received: `Scope: ${(0, utils_1.getScopeFromOuterProof)(proofData).toString()}`,
1396
1431
  message: "The proof comes from a different domain than the one expected",
1397
1432
  };
@@ -1577,6 +1612,27 @@ class ZKPassport {
1577
1612
  ...queryResultErrorsIssuingCountryExclusion,
1578
1613
  };
1579
1614
  }
1615
+ else if (!!committedInputs?.bind) {
1616
+ const bindCommittedInputs = committedInputs?.bind;
1617
+ const bindParameterCommitment = isForEVM
1618
+ ? await (0, utils_1.getBindEVMParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data))
1619
+ : await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1620
+ if (!paramCommitments.includes(bindParameterCommitment)) {
1621
+ console.warn("This proof does not verify the bound data");
1622
+ isCorrect = false;
1623
+ queryResultErrors.bind.commitment = {
1624
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
1625
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1626
+ message: "This proof does not verify the bound data",
1627
+ };
1628
+ }
1629
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1630
+ isCorrect = isCorrect && isCorrectBind;
1631
+ queryResultErrors = {
1632
+ ...queryResultErrors,
1633
+ ...queryResultErrorsBind,
1634
+ };
1635
+ }
1580
1636
  uniqueIdentifier = (0, utils_1.getNullifierFromOuterProof)(proofData).toString(10);
1581
1637
  }
1582
1638
  else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -1898,6 +1954,27 @@ class ZKPassport {
1898
1954
  };
1899
1955
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1900
1956
  }
1957
+ else if (proof.name === "bind") {
1958
+ const bindCommittedInputs = proof.committedInputs?.bind;
1959
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1960
+ const calculatedParamCommitment = await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1961
+ if (paramCommittment !== calculatedParamCommitment) {
1962
+ console.warn("The bound data does not match the one from the proof");
1963
+ isCorrect = false;
1964
+ queryResultErrors.bind.commitment = {
1965
+ expected: `Commitment: ${calculatedParamCommitment}`,
1966
+ received: `Commitment: ${paramCommittment}`,
1967
+ message: "The bound data does not match the one from the proof",
1968
+ };
1969
+ }
1970
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1971
+ isCorrect = isCorrect && isCorrectBind;
1972
+ queryResultErrors = {
1973
+ ...queryResultErrors,
1974
+ ...queryResultErrorsBind,
1975
+ };
1976
+ uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1977
+ }
1901
1978
  }
1902
1979
  return { isCorrect, uniqueIdentifier, queryResultErrors };
1903
1980
  }
@@ -1906,10 +1983,13 @@ class ZKPassport {
1906
1983
  * @param proofs The proofs to verify.
1907
1984
  * @param queryResult The query result to verify against
1908
1985
  * @param validity How many days ago should have the ID been last scanned by the user?
1986
+ * @param scope Scope this request to a specific use case
1987
+ * @param evmChain The EVM chain to use for the verification (if using the proof onchain)
1988
+ * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
1909
1989
  * @returns An object containing the unique identifier associated to the user
1910
1990
  * and a boolean indicating whether the proofs were successfully verified.
1911
1991
  */
1912
- async verify({ proofs, queryResult, validity, scope, devMode = false, }) {
1992
+ async verify({ proofs, queryResult, validity, scope, evmChain, devMode = false, }) {
1913
1993
  // If no proofs were generated, the results can't be trusted.
1914
1994
  // We still return it but verified will be false
1915
1995
  if (!proofs || proofs.length === 0) {
@@ -1931,7 +2011,8 @@ class ZKPassport {
1931
2011
  let verified = true;
1932
2012
  let uniqueIdentifier;
1933
2013
  let queryResultErrors;
1934
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope);
2014
+ const chainId = evmChain ? getChainIdFromEVMChain(evmChain) : undefined;
2015
+ const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope, chainId);
1935
2016
  uniqueIdentifier = uniqueIdentifierFromPublicInputs;
1936
2017
  verified = isCorrect;
1937
2018
  queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
@@ -2009,7 +2090,7 @@ class ZKPassport {
2009
2090
  if (network === "ethereum_sepolia") {
2010
2091
  return {
2011
2092
  ...baseConfig,
2012
- address: "0x8c6982D77f7a8f60aE3133cA9b2FAA6f3e78c394",
2093
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
2013
2094
  };
2014
2095
  }
2015
2096
  else if (network === "local_anvil") {
@@ -2102,6 +2183,14 @@ class ZKPassport {
2102
2183
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2103
2184
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
2104
2185
  }
2186
+ else if (circuitName === "bind_evm") {
2187
+ const value = proof.committedInputs[circuitName];
2188
+ compressedCommittedInputs =
2189
+ utils_1.ProofType.BIND.toString(16).padStart(2, "0") +
2190
+ (0, utils_1.rightPadArrayWithZeros)((0, utils_1.formatBoundData)(value.data), 500)
2191
+ .map((x) => x.toString(16).padStart(2, "0"))
2192
+ .join("");
2193
+ }
2105
2194
  else {
2106
2195
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
2107
2196
  }
@@ -2158,7 +2247,7 @@ class ZKPassport {
2158
2247
  * @returns The URL of the request.
2159
2248
  */
2160
2249
  getUrl(requestId) {
2161
- const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[requestId].publicKey);
2250
+ const pubkey = this.topicToPublicKey[requestId];
2162
2251
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2163
2252
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2164
2253
  return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
@@ -2168,14 +2257,13 @@ class ZKPassport {
2168
2257
  * @param requestId The request ID.
2169
2258
  */
2170
2259
  cancelRequest(requestId) {
2171
- if (this.topicToWebSocketClient[requestId]) {
2172
- this.topicToWebSocketClient[requestId].close();
2173
- delete this.topicToWebSocketClient[requestId];
2260
+ if (this.topicToBridge[requestId]) {
2261
+ this.topicToBridge[requestId].close();
2262
+ delete this.topicToBridge[requestId];
2174
2263
  }
2175
- delete this.topicToKeyPair[requestId];
2264
+ delete this.topicToPublicKey[requestId];
2176
2265
  delete this.topicToConfig[requestId];
2177
2266
  delete this.topicToLocalConfig[requestId];
2178
- delete this.topicToSharedSecret[requestId];
2179
2267
  delete this.topicToProofs[requestId];
2180
2268
  delete this.topicToExpectedProofCount[requestId];
2181
2269
  delete this.topicToFailedProofCount[requestId];
@@ -2191,7 +2279,7 @@ class ZKPassport {
2191
2279
  * @notice Clears all requests.
2192
2280
  */
2193
2281
  clearAllRequests() {
2194
- for (const requestId in this.topicToWebSocketClient) {
2282
+ for (const requestId in this.topicToBridge) {
2195
2283
  this.cancelRequest(requestId);
2196
2284
  }
2197
2285
  }