@zkpassport/sdk 0.3.4 → 0.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/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];
@@ -197,7 +213,7 @@ class ZKPassport {
197
213
  * @param request The request.
198
214
  * @param outerRequest The outer request.
199
215
  */
200
- async handleEncryptedMessage(topic, request, outerRequest) {
216
+ async handleEncryptedMessage(topic, request) {
201
217
  logger_1.noLogger.debug("Received encrypted message:", request);
202
218
  if (request.method === "accept") {
203
219
  logger_1.noLogger.debug(`User accepted the request and is generating a proof`);
@@ -209,30 +225,8 @@ class ZKPassport {
209
225
  }
210
226
  else if (request.method === "proof") {
211
227
  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)));
228
+ this.topicToProofs[topic].push(request.params);
229
+ await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(request.params)));
236
230
  // If the results were received before all the proofs were generated,
237
231
  // we can handle the result now
238
232
  if (this.topicToResults[topic] &&
@@ -332,7 +326,7 @@ class ZKPassport {
332
326
  done: () => {
333
327
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
334
328
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
335
- const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[topic].publicKey);
329
+ const pubkey = this.topicToPublicKey[topic];
336
330
  this.setExpectedProofCount(topic);
337
331
  return {
338
332
  url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
@@ -344,7 +338,7 @@ class ZKPassport {
344
338
  onResult: (callback) => this.onResultCallbacks[topic].push(callback),
345
339
  onReject: (callback) => this.onRejectCallbacks[topic].push(callback),
346
340
  onError: (callback) => this.onErrorCallbacks[topic].push(callback),
347
- isBridgeConnected: () => this.topicToWebSocketClient[topic].readyState === WebSocket.OPEN,
341
+ isBridgeConnected: () => this.topicToBridge[topic].isBridgeConnected(),
348
342
  requestReceived: () => this.topicToRequestReceived[topic] === true,
349
343
  };
350
344
  },
@@ -358,17 +352,23 @@ class ZKPassport {
358
352
  * @param scope Scope this request to a specific use case
359
353
  * @param validity How many days ago should have the ID been last scanned by the user?
360
354
  * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
355
+ * @param evmChain The EVM chain to use for the request (if using the proof onchain)
361
356
  * @returns The query builder object.
362
357
  */
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
- };
358
+ async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }) {
359
+ const bridge = await bridge_1.Bridge.create({
360
+ keyPair: keyPairOverride,
361
+ bridgeId: topicOverride,
362
+ });
363
+ const topic = bridge.connection.getBridgeId();
370
364
  this.topicToConfig[topic] = {};
371
- this.topicToService[topic] = { name, logo, purpose, scope };
365
+ this.topicToService[topic] = {
366
+ name,
367
+ logo,
368
+ purpose,
369
+ scope,
370
+ chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
371
+ };
372
372
  this.topicToProofs[topic] = [];
373
373
  this.topicToExpectedProofCount[topic] = 0;
374
374
  this.topicToLocalConfig[topic] = {
@@ -384,53 +384,21 @@ class ZKPassport {
384
384
  this.onResultCallbacks[topic] = [];
385
385
  this.onRejectCallbacks[topic] = [];
386
386
  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");
387
+ this.topicToPublicKey[topic] = bridge.getPublicKey();
388
+ this.topicToBridge[topic] = bridge;
389
+ bridge.onConnect(async (reconnection) => {
390
+ logger_1.noLogger.debug("Bridge connected");
391
+ logger_1.noLogger.debug("Is reconnection:", reconnection);
391
392
  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
393
  });
431
- wsClient.onerror = (error) => {
432
- logger_1.noLogger.error("[frontend] WebSocket error:", error);
433
- };
394
+ bridge.onSecureChannelEstablished(async () => {
395
+ logger_1.noLogger.debug("Secure channel established");
396
+ await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()));
397
+ });
398
+ bridge.onSecureMessage(async (message) => {
399
+ logger_1.noLogger.debug("Received message:", message);
400
+ this.handleEncryptedMessage(topic, message);
401
+ });
434
402
  return this.getZkPassportRequest(topic);
435
403
  }
436
404
  checkDiscloseBytesPublicInputs(proof, queryResult) {
@@ -1252,13 +1220,14 @@ class ZKPassport {
1252
1220
  }
1253
1221
  return { isCorrect, queryResultErrors };
1254
1222
  }
1255
- checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope) {
1223
+ checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope, chainId) {
1256
1224
  let isCorrect = true;
1257
- if (this.domain && (0, utils_1.getScopeHash)(this.domain) !== BigInt(proofData.publicInputs[1])) {
1225
+ if (this.domain &&
1226
+ (0, utils_1.getServiceScopeHash)(this.domain, chainId) !== BigInt(proofData.publicInputs[1])) {
1258
1227
  console.warn("The proof comes from a different domain than the one expected");
1259
1228
  isCorrect = false;
1260
1229
  queryResultErrors[key].scope = {
1261
- expected: `Scope: ${(0, utils_1.getScopeHash)(this.domain).toString()}`,
1230
+ expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
1262
1231
  received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
1263
1232
  message: "The proof comes from a different domain than the one expected",
1264
1233
  };
@@ -1302,7 +1271,7 @@ class ZKPassport {
1302
1271
  }
1303
1272
  return { isCorrect, queryResultErrors };
1304
1273
  }
1305
- async checkPublicInputs(proofs, queryResult, validity, scope) {
1274
+ async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
1306
1275
  let commitmentIn;
1307
1276
  let commitmentOut;
1308
1277
  let isCorrect = true;
@@ -1387,11 +1356,12 @@ class ZKPassport {
1387
1356
  message: "The proof does not verify all the requested conditions and information",
1388
1357
  };
1389
1358
  }
1390
- if (this.domain && (0, utils_1.getScopeHash)(this.domain) !== (0, utils_1.getScopeFromOuterProof)(proofData)) {
1359
+ if (this.domain &&
1360
+ (0, utils_1.getServiceScopeHash)(this.domain, chainId) !== (0, utils_1.getScopeFromOuterProof)(proofData)) {
1391
1361
  console.warn("The proof comes from a different domain than the one expected");
1392
1362
  isCorrect = false;
1393
1363
  queryResultErrors.outer.scope = {
1394
- expected: `Scope: ${(0, utils_1.getScopeHash)(this.domain).toString()}`,
1364
+ expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
1395
1365
  received: `Scope: ${(0, utils_1.getScopeFromOuterProof)(proofData).toString()}`,
1396
1366
  message: "The proof comes from a different domain than the one expected",
1397
1367
  };
@@ -1906,10 +1876,13 @@ class ZKPassport {
1906
1876
  * @param proofs The proofs to verify.
1907
1877
  * @param queryResult The query result to verify against
1908
1878
  * @param validity How many days ago should have the ID been last scanned by the user?
1879
+ * @param scope Scope this request to a specific use case
1880
+ * @param evmChain The EVM chain to use for the verification (if using the proof onchain)
1881
+ * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
1909
1882
  * @returns An object containing the unique identifier associated to the user
1910
1883
  * and a boolean indicating whether the proofs were successfully verified.
1911
1884
  */
1912
- async verify({ proofs, queryResult, validity, scope, devMode = false, }) {
1885
+ async verify({ proofs, queryResult, validity, scope, evmChain, devMode = false, }) {
1913
1886
  // If no proofs were generated, the results can't be trusted.
1914
1887
  // We still return it but verified will be false
1915
1888
  if (!proofs || proofs.length === 0) {
@@ -1931,7 +1904,8 @@ class ZKPassport {
1931
1904
  let verified = true;
1932
1905
  let uniqueIdentifier;
1933
1906
  let queryResultErrors;
1934
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope);
1907
+ const chainId = evmChain ? getChainIdFromEVMChain(evmChain) : undefined;
1908
+ const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope, chainId);
1935
1909
  uniqueIdentifier = uniqueIdentifierFromPublicInputs;
1936
1910
  verified = isCorrect;
1937
1911
  queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
@@ -2009,7 +1983,7 @@ class ZKPassport {
2009
1983
  if (network === "ethereum_sepolia") {
2010
1984
  return {
2011
1985
  ...baseConfig,
2012
- address: "0x8c6982D77f7a8f60aE3133cA9b2FAA6f3e78c394",
1986
+ address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2013
1987
  };
2014
1988
  }
2015
1989
  else if (network === "local_anvil") {
@@ -2158,7 +2132,7 @@ class ZKPassport {
2158
2132
  * @returns The URL of the request.
2159
2133
  */
2160
2134
  getUrl(requestId) {
2161
- const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[requestId].publicKey);
2135
+ const pubkey = this.topicToPublicKey[requestId];
2162
2136
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2163
2137
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2164
2138
  return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
@@ -2168,14 +2142,13 @@ class ZKPassport {
2168
2142
  * @param requestId The request ID.
2169
2143
  */
2170
2144
  cancelRequest(requestId) {
2171
- if (this.topicToWebSocketClient[requestId]) {
2172
- this.topicToWebSocketClient[requestId].close();
2173
- delete this.topicToWebSocketClient[requestId];
2145
+ if (this.topicToBridge[requestId]) {
2146
+ this.topicToBridge[requestId].close();
2147
+ delete this.topicToBridge[requestId];
2174
2148
  }
2175
- delete this.topicToKeyPair[requestId];
2149
+ delete this.topicToPublicKey[requestId];
2176
2150
  delete this.topicToConfig[requestId];
2177
2151
  delete this.topicToLocalConfig[requestId];
2178
- delete this.topicToSharedSecret[requestId];
2179
2152
  delete this.topicToProofs[requestId];
2180
2153
  delete this.topicToExpectedProofCount[requestId];
2181
2154
  delete this.topicToFailedProofCount[requestId];
@@ -2191,7 +2164,7 @@ class ZKPassport {
2191
2164
  * @notice Clears all requests.
2192
2165
  */
2193
2166
  clearAllRequests() {
2194
- for (const requestId in this.topicToWebSocketClient) {
2167
+ for (const requestId in this.topicToBridge) {
2195
2168
  this.cancelRequest(requestId);
2196
2169
  }
2197
2170
  }