@zkpassport/sdk 0.4.0 → 0.4.2

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
  *
@@ -199,7 +205,7 @@ export declare class ZKPassport {
199
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, evmChain, validity, devMode, topicOverride, keyPairOverride, }: {
208
+ request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, cloudProverUrl, bridgeUrl, }: {
203
209
  name: string;
204
210
  logo: string;
205
211
  purpose: string;
@@ -213,6 +219,8 @@ export declare class ZKPassport {
213
219
  privateKey: Uint8Array;
214
220
  publicKey: Uint8Array;
215
221
  };
222
+ cloudProverUrl?: string;
223
+ bridgeUrl?: string;
216
224
  }): Promise<QueryBuilder>;
217
225
  private checkDiscloseBytesPublicInputs;
218
226
  private checkAgePublicInputs;
@@ -224,6 +232,7 @@ export declare class ZKPassport {
224
232
  private checkIssuingCountryInclusionPublicInputs;
225
233
  private checkScopeFromDisclosureProof;
226
234
  private checkCertificateRegistryRoot;
235
+ private checkBindPublicInputs;
227
236
  private checkPublicInputs;
228
237
  /**
229
238
  * @notice Verify the proofs received from the mobile app.
@@ -273,6 +282,7 @@ export declare class ZKPassport {
273
282
  scope?: string;
274
283
  devMode?: boolean;
275
284
  }): SolidityVerifierParameters;
285
+ private _getUrl;
276
286
  /**
277
287
  * @notice Returns the URL of the request.
278
288
  * @param requestId The request ID.
package/dist/cjs/index.js CHANGED
@@ -13,6 +13,7 @@ const utils_3 = require("@noble/hashes/utils");
13
13
  const ZKPassportVerifier_json_1 = tslib_1.__importDefault(require("./assets/abi/ZKPassportVerifier.json"));
14
14
  const registry_1 = require("@zkpassport/registry");
15
15
  const bridge_1 = require("@obsidion/bridge");
16
+ const VERSION = "0.4.2";
16
17
  const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
17
18
  // If Buffer is not defined, then we use the Buffer from the buffer package
18
19
  if (typeof globalThis.Buffer === "undefined") {
@@ -201,6 +202,9 @@ class ZKPassport {
201
202
  }
202
203
  }
203
204
  }
205
+ if (this.topicToConfig[topic].bind) {
206
+ neededCircuits.push("bind");
207
+ }
204
208
  // From the circuits needed, determine the expected proof count
205
209
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
206
210
  // Each separate needed circuit adds 1 disclosure proof
@@ -323,13 +327,17 @@ class ZKPassport {
323
327
  };
324
328
  return this.getZkPassportRequest(topic);
325
329
  },
330
+ bind: (key, value) => {
331
+ this.topicToConfig[topic].bind = {
332
+ ...this.topicToConfig[topic].bind,
333
+ [key]: value,
334
+ };
335
+ return this.getZkPassportRequest(topic);
336
+ },
326
337
  done: () => {
327
- const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
328
- const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
329
- const pubkey = this.topicToPublicKey[topic];
330
338
  this.setExpectedProofCount(topic);
331
339
  return {
332
- url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
340
+ url: this._getUrl(topic),
333
341
  requestId: topic,
334
342
  onRequestReceived: (callback) => this.onRequestReceivedCallbacks[topic].push(callback),
335
343
  onGeneratingProof: (callback) => this.onGeneratingProofCallbacks[topic].push(callback),
@@ -355,10 +363,11 @@ class ZKPassport {
355
363
  * @param evmChain The EVM chain to use for the request (if using the proof onchain)
356
364
  * @returns The query builder object.
357
365
  */
358
- async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }) {
366
+ async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, cloudProverUrl, bridgeUrl, }) {
359
367
  const bridge = await bridge_1.Bridge.create({
360
368
  keyPair: keyPairOverride,
361
369
  bridgeId: topicOverride,
370
+ bridgeUrl,
362
371
  });
363
372
  const topic = bridge.connection.getBridgeId();
364
373
  this.topicToConfig[topic] = {};
@@ -368,6 +377,8 @@ class ZKPassport {
368
377
  purpose,
369
378
  scope,
370
379
  chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
380
+ cloudProverUrl,
381
+ bridgeUrl,
371
382
  };
372
383
  this.topicToProofs[topic] = [];
373
384
  this.topicToExpectedProofCount[topic] = 0;
@@ -419,6 +430,7 @@ class ZKPassport {
419
430
  fullname: {},
420
431
  document_number: {},
421
432
  outer: {},
433
+ bind: {},
422
434
  };
423
435
  let isCorrect = true;
424
436
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -731,6 +743,7 @@ class ZKPassport {
731
743
  fullname: {},
732
744
  document_number: {},
733
745
  outer: {},
746
+ bind: {},
734
747
  };
735
748
  let isCorrect = true;
736
749
  const currentTime = new Date();
@@ -840,6 +853,7 @@ class ZKPassport {
840
853
  fullname: {},
841
854
  document_number: {},
842
855
  outer: {},
856
+ bind: {},
843
857
  };
844
858
  let isCorrect = true;
845
859
  const currentTime = new Date();
@@ -943,6 +957,7 @@ class ZKPassport {
943
957
  fullname: {},
944
958
  document_number: {},
945
959
  outer: {},
960
+ bind: {},
946
961
  };
947
962
  let isCorrect = true;
948
963
  const currentTime = new Date();
@@ -1046,6 +1061,7 @@ class ZKPassport {
1046
1061
  fullname: {},
1047
1062
  document_number: {},
1048
1063
  outer: {},
1064
+ bind: {},
1049
1065
  };
1050
1066
  let isCorrect = true;
1051
1067
  if (queryResult.nationality &&
@@ -1100,6 +1116,7 @@ class ZKPassport {
1100
1116
  fullname: {},
1101
1117
  document_number: {},
1102
1118
  outer: {},
1119
+ bind: {},
1103
1120
  };
1104
1121
  let isCorrect = true;
1105
1122
  if (queryResult.issuing_country &&
@@ -1154,6 +1171,7 @@ class ZKPassport {
1154
1171
  fullname: {},
1155
1172
  document_number: {},
1156
1173
  outer: {},
1174
+ bind: {},
1157
1175
  };
1158
1176
  let isCorrect = true;
1159
1177
  if (queryResult.nationality &&
@@ -1196,6 +1214,7 @@ class ZKPassport {
1196
1214
  fullname: {},
1197
1215
  document_number: {},
1198
1216
  outer: {},
1217
+ bind: {},
1199
1218
  };
1200
1219
  let isCorrect = true;
1201
1220
  if (queryResult.issuing_country &&
@@ -1271,6 +1290,51 @@ class ZKPassport {
1271
1290
  }
1272
1291
  return { isCorrect, queryResultErrors };
1273
1292
  }
1293
+ checkBindPublicInputs(queryResult, boundData) {
1294
+ const queryResultErrors = {
1295
+ sig_check_dsc: {},
1296
+ sig_check_id_data: {},
1297
+ data_check_integrity: {},
1298
+ disclose: {},
1299
+ age: {},
1300
+ birthdate: {},
1301
+ expiry_date: {},
1302
+ document_type: {},
1303
+ issuing_country: {},
1304
+ gender: {},
1305
+ nationality: {},
1306
+ firstname: {},
1307
+ lastname: {},
1308
+ fullname: {},
1309
+ document_number: {},
1310
+ outer: {},
1311
+ bind: {},
1312
+ };
1313
+ let isCorrect = true;
1314
+ if (queryResult.bind) {
1315
+ if (queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1316
+ boundData.user_address?.toLowerCase().replace("0x", "")) {
1317
+ console.warn("Bound user address does not match the one from the query results");
1318
+ isCorrect = false;
1319
+ queryResultErrors.bind.eq = {
1320
+ expected: queryResult.bind.user_address,
1321
+ received: boundData.user_address,
1322
+ message: "Bound user address does not match the one from the query results",
1323
+ };
1324
+ }
1325
+ if (queryResult.bind.custom_data?.trim().toLowerCase() !==
1326
+ boundData.custom_data?.trim().toLowerCase()) {
1327
+ console.warn("Bound custom data does not match the one from the query results");
1328
+ isCorrect = false;
1329
+ queryResultErrors.bind.eq = {
1330
+ expected: queryResult.bind.custom_data,
1331
+ received: boundData.custom_data,
1332
+ message: "Bound custom data does not match the one from the query results",
1333
+ };
1334
+ }
1335
+ }
1336
+ return { isCorrect, queryResultErrors };
1337
+ }
1274
1338
  async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
1275
1339
  let commitmentIn;
1276
1340
  let commitmentOut;
@@ -1295,6 +1359,7 @@ class ZKPassport {
1295
1359
  fullname: {},
1296
1360
  document_number: {},
1297
1361
  outer: {},
1362
+ bind: {},
1298
1363
  };
1299
1364
  // Since the order is important for the commitments, we need to sort the proofs
1300
1365
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -1311,6 +1376,7 @@ class ZKPassport {
1311
1376
  "inclusion_check_nationality",
1312
1377
  "exclusion_check_issuing_country",
1313
1378
  "inclusion_check_issuing_country",
1379
+ "bind",
1314
1380
  ];
1315
1381
  const getIndex = (proof) => {
1316
1382
  const name = proof.name || "";
@@ -1547,6 +1613,27 @@ class ZKPassport {
1547
1613
  ...queryResultErrorsIssuingCountryExclusion,
1548
1614
  };
1549
1615
  }
1616
+ else if (!!committedInputs?.bind) {
1617
+ const bindCommittedInputs = committedInputs?.bind;
1618
+ const bindParameterCommitment = isForEVM
1619
+ ? await (0, utils_1.getBindEVMParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data))
1620
+ : await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1621
+ if (!paramCommitments.includes(bindParameterCommitment)) {
1622
+ console.warn("This proof does not verify the bound data");
1623
+ isCorrect = false;
1624
+ queryResultErrors.bind.commitment = {
1625
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
1626
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1627
+ message: "This proof does not verify the bound data",
1628
+ };
1629
+ }
1630
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1631
+ isCorrect = isCorrect && isCorrectBind;
1632
+ queryResultErrors = {
1633
+ ...queryResultErrors,
1634
+ ...queryResultErrorsBind,
1635
+ };
1636
+ }
1550
1637
  uniqueIdentifier = (0, utils_1.getNullifierFromOuterProof)(proofData).toString(10);
1551
1638
  }
1552
1639
  else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -1868,6 +1955,27 @@ class ZKPassport {
1868
1955
  };
1869
1956
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1870
1957
  }
1958
+ else if (proof.name === "bind") {
1959
+ const bindCommittedInputs = proof.committedInputs?.bind;
1960
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1961
+ const calculatedParamCommitment = await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1962
+ if (paramCommittment !== calculatedParamCommitment) {
1963
+ console.warn("The bound data does not match the one from the proof");
1964
+ isCorrect = false;
1965
+ queryResultErrors.bind.commitment = {
1966
+ expected: `Commitment: ${calculatedParamCommitment}`,
1967
+ received: `Commitment: ${paramCommittment}`,
1968
+ message: "The bound data does not match the one from the proof",
1969
+ };
1970
+ }
1971
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1972
+ isCorrect = isCorrect && isCorrectBind;
1973
+ queryResultErrors = {
1974
+ ...queryResultErrors,
1975
+ ...queryResultErrorsBind,
1976
+ };
1977
+ uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1978
+ }
1871
1979
  }
1872
1980
  return { isCorrect, uniqueIdentifier, queryResultErrors };
1873
1981
  }
@@ -1983,7 +2091,7 @@ class ZKPassport {
1983
2091
  if (network === "ethereum_sepolia") {
1984
2092
  return {
1985
2093
  ...baseConfig,
1986
- address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2094
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
1987
2095
  };
1988
2096
  }
1989
2097
  else if (network === "local_anvil") {
@@ -2076,6 +2184,14 @@ class ZKPassport {
2076
2184
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2077
2185
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
2078
2186
  }
2187
+ else if (circuitName === "bind_evm") {
2188
+ const value = proof.committedInputs[circuitName];
2189
+ compressedCommittedInputs =
2190
+ utils_1.ProofType.BIND.toString(16).padStart(2, "0") +
2191
+ (0, utils_1.rightPadArrayWithZeros)((0, utils_1.formatBoundData)(value.data), 500)
2192
+ .map((x) => x.toString(16).padStart(2, "0"))
2193
+ .join("");
2194
+ }
2079
2195
  else {
2080
2196
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
2081
2197
  }
@@ -2126,16 +2242,19 @@ class ZKPassport {
2126
2242
  };
2127
2243
  return params;
2128
2244
  }
2245
+ _getUrl(requestId) {
2246
+ const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2247
+ const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2248
+ const pubkey = this.topicToPublicKey[requestId];
2249
+ return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}&v=${VERSION}`;
2250
+ }
2129
2251
  /**
2130
2252
  * @notice Returns the URL of the request.
2131
2253
  * @param requestId The request ID.
2132
2254
  * @returns The URL of the request.
2133
2255
  */
2134
2256
  getUrl(requestId) {
2135
- const pubkey = this.topicToPublicKey[requestId];
2136
- const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2137
- const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2138
- return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
2257
+ return this._getUrl(requestId);
2139
2258
  }
2140
2259
  /**
2141
2260
  * @notice Cancels a request by closing the WebSocket connection and deleting the associated data.