@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/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getAlpha3Code, registerLocale } from "i18n-iso-countries";
2
- import { getProofData, getCommitmentFromDSCProof, getCommitmentInFromIDDataProof, getCommitmentOutFromIDDataProof, getNullifierFromDisclosureProof, getCommitmentInFromIntegrityProof, getCommitmentOutFromIntegrityProof, getCommitmentInFromDisclosureProof, getMerkleRootFromDSCProof, getCurrentDateFromIntegrityProof, DisclosedData, formatName, getHostedPackagedCircuitByName, getNumberOfPublicInputs, getParameterCommitmentFromDisclosureProof, getCountryParameterCommitment, getDiscloseParameterCommitment, getDateParameterCommitment, getCertificateRegistryRootFromOuterProof, getParamCommitmentsFromOuterProof, getCurrentDateFromCommittedInputs, getMinAgeFromCommittedInputs, getMaxAgeFromCommittedInputs, getAgeParameterCommitment, getMinDateFromCommittedInputs, getMaxDateFromCommittedInputs, getCurrentDateFromOuterProof, getNullifierFromOuterProof, getAgeEVMParameterCommitment, getDateEVMParameterCommitment, getDiscloseEVMParameterCommitment, getCountryEVMParameterCommitment, rightPadArrayWithZeros, getCommittedInputCount, ProofType, getScopeHash, getScopeFromOuterProof, getSubscopeFromOuterProof, getServiceScopeHash, } from "@zkpassport/utils";
2
+ import { getProofData, getCommitmentFromDSCProof, getCommitmentInFromIDDataProof, getCommitmentOutFromIDDataProof, getNullifierFromDisclosureProof, getCommitmentInFromIntegrityProof, getCommitmentOutFromIntegrityProof, getCommitmentInFromDisclosureProof, getMerkleRootFromDSCProof, getCurrentDateFromIntegrityProof, DisclosedData, formatName, getHostedPackagedCircuitByName, getNumberOfPublicInputs, getParameterCommitmentFromDisclosureProof, getCountryParameterCommitment, getDiscloseParameterCommitment, getDateParameterCommitment, getCertificateRegistryRootFromOuterProof, getParamCommitmentsFromOuterProof, getCurrentDateFromCommittedInputs, getMinAgeFromCommittedInputs, getMaxAgeFromCommittedInputs, getAgeParameterCommitment, getMinDateFromCommittedInputs, getMaxDateFromCommittedInputs, getCurrentDateFromOuterProof, getNullifierFromOuterProof, getAgeEVMParameterCommitment, getDateEVMParameterCommitment, getDiscloseEVMParameterCommitment, getCountryEVMParameterCommitment, rightPadArrayWithZeros, getCommittedInputCount, ProofType, getScopeHash, getScopeFromOuterProof, getSubscopeFromOuterProof, getServiceScopeHash, getBindEVMParameterCommitment, getBindParameterCommitment, formatBoundData, } from "@zkpassport/utils";
3
3
  import { bytesToHex } from "@noble/ciphers/utils";
4
4
  import { noLogger as logger } from "./logger";
5
5
  import i18en from "i18n-iso-countries/langs/en.json";
@@ -9,6 +9,7 @@ import { hexToBytes } from "@noble/hashes/utils";
9
9
  import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json";
10
10
  import { RegistryClient } from "@zkpassport/registry";
11
11
  import { Bridge } from "@obsidion/bridge";
12
+ const VERSION = "0.4.2";
12
13
  const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
13
14
  // If Buffer is not defined, then we use the Buffer from the buffer package
14
15
  if (typeof globalThis.Buffer === "undefined") {
@@ -191,6 +192,9 @@ export class ZKPassport {
191
192
  }
192
193
  }
193
194
  }
195
+ if (this.topicToConfig[topic].bind) {
196
+ neededCircuits.push("bind");
197
+ }
194
198
  // From the circuits needed, determine the expected proof count
195
199
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
196
200
  // Each separate needed circuit adds 1 disclosure proof
@@ -313,13 +317,17 @@ export class ZKPassport {
313
317
  };
314
318
  return this.getZkPassportRequest(topic);
315
319
  },
320
+ bind: (key, value) => {
321
+ this.topicToConfig[topic].bind = {
322
+ ...this.topicToConfig[topic].bind,
323
+ [key]: value,
324
+ };
325
+ return this.getZkPassportRequest(topic);
326
+ },
316
327
  done: () => {
317
- const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
318
- const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
319
- const pubkey = this.topicToPublicKey[topic];
320
328
  this.setExpectedProofCount(topic);
321
329
  return {
322
- url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
330
+ url: this._getUrl(topic),
323
331
  requestId: topic,
324
332
  onRequestReceived: (callback) => this.onRequestReceivedCallbacks[topic].push(callback),
325
333
  onGeneratingProof: (callback) => this.onGeneratingProofCallbacks[topic].push(callback),
@@ -345,10 +353,11 @@ export class ZKPassport {
345
353
  * @param evmChain The EVM chain to use for the request (if using the proof onchain)
346
354
  * @returns The query builder object.
347
355
  */
348
- async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }) {
356
+ async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, cloudProverUrl, bridgeUrl, }) {
349
357
  const bridge = await Bridge.create({
350
358
  keyPair: keyPairOverride,
351
359
  bridgeId: topicOverride,
360
+ bridgeUrl,
352
361
  });
353
362
  const topic = bridge.connection.getBridgeId();
354
363
  this.topicToConfig[topic] = {};
@@ -358,6 +367,8 @@ export class ZKPassport {
358
367
  purpose,
359
368
  scope,
360
369
  chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
370
+ cloudProverUrl,
371
+ bridgeUrl,
361
372
  };
362
373
  this.topicToProofs[topic] = [];
363
374
  this.topicToExpectedProofCount[topic] = 0;
@@ -409,6 +420,7 @@ export class ZKPassport {
409
420
  fullname: {},
410
421
  document_number: {},
411
422
  outer: {},
423
+ bind: {},
412
424
  };
413
425
  let isCorrect = true;
414
426
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -721,6 +733,7 @@ export class ZKPassport {
721
733
  fullname: {},
722
734
  document_number: {},
723
735
  outer: {},
736
+ bind: {},
724
737
  };
725
738
  let isCorrect = true;
726
739
  const currentTime = new Date();
@@ -830,6 +843,7 @@ export class ZKPassport {
830
843
  fullname: {},
831
844
  document_number: {},
832
845
  outer: {},
846
+ bind: {},
833
847
  };
834
848
  let isCorrect = true;
835
849
  const currentTime = new Date();
@@ -933,6 +947,7 @@ export class ZKPassport {
933
947
  fullname: {},
934
948
  document_number: {},
935
949
  outer: {},
950
+ bind: {},
936
951
  };
937
952
  let isCorrect = true;
938
953
  const currentTime = new Date();
@@ -1036,6 +1051,7 @@ export class ZKPassport {
1036
1051
  fullname: {},
1037
1052
  document_number: {},
1038
1053
  outer: {},
1054
+ bind: {},
1039
1055
  };
1040
1056
  let isCorrect = true;
1041
1057
  if (queryResult.nationality &&
@@ -1090,6 +1106,7 @@ export class ZKPassport {
1090
1106
  fullname: {},
1091
1107
  document_number: {},
1092
1108
  outer: {},
1109
+ bind: {},
1093
1110
  };
1094
1111
  let isCorrect = true;
1095
1112
  if (queryResult.issuing_country &&
@@ -1144,6 +1161,7 @@ export class ZKPassport {
1144
1161
  fullname: {},
1145
1162
  document_number: {},
1146
1163
  outer: {},
1164
+ bind: {},
1147
1165
  };
1148
1166
  let isCorrect = true;
1149
1167
  if (queryResult.nationality &&
@@ -1186,6 +1204,7 @@ export class ZKPassport {
1186
1204
  fullname: {},
1187
1205
  document_number: {},
1188
1206
  outer: {},
1207
+ bind: {},
1189
1208
  };
1190
1209
  let isCorrect = true;
1191
1210
  if (queryResult.issuing_country &&
@@ -1261,6 +1280,51 @@ export class ZKPassport {
1261
1280
  }
1262
1281
  return { isCorrect, queryResultErrors };
1263
1282
  }
1283
+ checkBindPublicInputs(queryResult, boundData) {
1284
+ const queryResultErrors = {
1285
+ sig_check_dsc: {},
1286
+ sig_check_id_data: {},
1287
+ data_check_integrity: {},
1288
+ disclose: {},
1289
+ age: {},
1290
+ birthdate: {},
1291
+ expiry_date: {},
1292
+ document_type: {},
1293
+ issuing_country: {},
1294
+ gender: {},
1295
+ nationality: {},
1296
+ firstname: {},
1297
+ lastname: {},
1298
+ fullname: {},
1299
+ document_number: {},
1300
+ outer: {},
1301
+ bind: {},
1302
+ };
1303
+ let isCorrect = true;
1304
+ if (queryResult.bind) {
1305
+ if (queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1306
+ boundData.user_address?.toLowerCase().replace("0x", "")) {
1307
+ console.warn("Bound user address does not match the one from the query results");
1308
+ isCorrect = false;
1309
+ queryResultErrors.bind.eq = {
1310
+ expected: queryResult.bind.user_address,
1311
+ received: boundData.user_address,
1312
+ message: "Bound user address does not match the one from the query results",
1313
+ };
1314
+ }
1315
+ if (queryResult.bind.custom_data?.trim().toLowerCase() !==
1316
+ boundData.custom_data?.trim().toLowerCase()) {
1317
+ console.warn("Bound custom data does not match the one from the query results");
1318
+ isCorrect = false;
1319
+ queryResultErrors.bind.eq = {
1320
+ expected: queryResult.bind.custom_data,
1321
+ received: boundData.custom_data,
1322
+ message: "Bound custom data does not match the one from the query results",
1323
+ };
1324
+ }
1325
+ }
1326
+ return { isCorrect, queryResultErrors };
1327
+ }
1264
1328
  async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
1265
1329
  let commitmentIn;
1266
1330
  let commitmentOut;
@@ -1285,6 +1349,7 @@ export class ZKPassport {
1285
1349
  fullname: {},
1286
1350
  document_number: {},
1287
1351
  outer: {},
1352
+ bind: {},
1288
1353
  };
1289
1354
  // Since the order is important for the commitments, we need to sort the proofs
1290
1355
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -1301,6 +1366,7 @@ export class ZKPassport {
1301
1366
  "inclusion_check_nationality",
1302
1367
  "exclusion_check_issuing_country",
1303
1368
  "inclusion_check_issuing_country",
1369
+ "bind",
1304
1370
  ];
1305
1371
  const getIndex = (proof) => {
1306
1372
  const name = proof.name || "";
@@ -1537,6 +1603,27 @@ export class ZKPassport {
1537
1603
  ...queryResultErrorsIssuingCountryExclusion,
1538
1604
  };
1539
1605
  }
1606
+ else if (!!committedInputs?.bind) {
1607
+ const bindCommittedInputs = committedInputs?.bind;
1608
+ const bindParameterCommitment = isForEVM
1609
+ ? await getBindEVMParameterCommitment(formatBoundData(bindCommittedInputs.data))
1610
+ : await getBindParameterCommitment(formatBoundData(bindCommittedInputs.data));
1611
+ if (!paramCommitments.includes(bindParameterCommitment)) {
1612
+ console.warn("This proof does not verify the bound data");
1613
+ isCorrect = false;
1614
+ queryResultErrors.bind.commitment = {
1615
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
1616
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1617
+ message: "This proof does not verify the bound data",
1618
+ };
1619
+ }
1620
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1621
+ isCorrect = isCorrect && isCorrectBind;
1622
+ queryResultErrors = {
1623
+ ...queryResultErrors,
1624
+ ...queryResultErrorsBind,
1625
+ };
1626
+ }
1540
1627
  uniqueIdentifier = getNullifierFromOuterProof(proofData).toString(10);
1541
1628
  }
1542
1629
  else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -1858,6 +1945,27 @@ export class ZKPassport {
1858
1945
  };
1859
1946
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1860
1947
  }
1948
+ else if (proof.name === "bind") {
1949
+ const bindCommittedInputs = proof.committedInputs?.bind;
1950
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData);
1951
+ const calculatedParamCommitment = await getBindParameterCommitment(formatBoundData(bindCommittedInputs.data));
1952
+ if (paramCommittment !== calculatedParamCommitment) {
1953
+ console.warn("The bound data does not match the one from the proof");
1954
+ isCorrect = false;
1955
+ queryResultErrors.bind.commitment = {
1956
+ expected: `Commitment: ${calculatedParamCommitment}`,
1957
+ received: `Commitment: ${paramCommittment}`,
1958
+ message: "The bound data does not match the one from the proof",
1959
+ };
1960
+ }
1961
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1962
+ isCorrect = isCorrect && isCorrectBind;
1963
+ queryResultErrors = {
1964
+ ...queryResultErrors,
1965
+ ...queryResultErrorsBind,
1966
+ };
1967
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1968
+ }
1861
1969
  }
1862
1970
  return { isCorrect, uniqueIdentifier, queryResultErrors };
1863
1971
  }
@@ -1973,7 +2081,7 @@ export class ZKPassport {
1973
2081
  if (network === "ethereum_sepolia") {
1974
2082
  return {
1975
2083
  ...baseConfig,
1976
- address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2084
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
1977
2085
  };
1978
2086
  }
1979
2087
  else if (network === "local_anvil") {
@@ -2066,6 +2174,14 @@ export class ZKPassport {
2066
2174
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2067
2175
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
2068
2176
  }
2177
+ else if (circuitName === "bind_evm") {
2178
+ const value = proof.committedInputs[circuitName];
2179
+ compressedCommittedInputs =
2180
+ ProofType.BIND.toString(16).padStart(2, "0") +
2181
+ rightPadArrayWithZeros(formatBoundData(value.data), 500)
2182
+ .map((x) => x.toString(16).padStart(2, "0"))
2183
+ .join("");
2184
+ }
2069
2185
  else {
2070
2186
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
2071
2187
  }
@@ -2116,16 +2232,19 @@ export class ZKPassport {
2116
2232
  };
2117
2233
  return params;
2118
2234
  }
2235
+ _getUrl(requestId) {
2236
+ const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2237
+ const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2238
+ const pubkey = this.topicToPublicKey[requestId];
2239
+ return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}&v=${VERSION}`;
2240
+ }
2119
2241
  /**
2120
2242
  * @notice Returns the URL of the request.
2121
2243
  * @param requestId The request ID.
2122
2244
  * @returns The URL of the request.
2123
2245
  */
2124
2246
  getUrl(requestId) {
2125
- const pubkey = this.topicToPublicKey[requestId];
2126
- const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
2127
- const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
2128
- return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
2247
+ return this._getUrl(requestId);
2129
2248
  }
2130
2249
  /**
2131
2250
  * @notice Cancels a request by closing the WebSocket connection and deleting the associated data.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkpassport/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Privacy-preserving identity verification using passports and ID cards",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -42,9 +42,9 @@
42
42
  "@noble/ciphers": "^1.2.1",
43
43
  "@noble/hashes": "^1.7.2",
44
44
  "@noble/secp256k1": "^2.2.3",
45
- "@obsidion/bridge": "^0.9.0",
46
- "@zkpassport/registry": "^0.1.9",
47
- "@zkpassport/utils": "^0.8.2",
45
+ "@obsidion/bridge": "^0.10.0",
46
+ "@zkpassport/registry": "^0.2.1",
47
+ "@zkpassport/utils": "^0.9.6",
48
48
  "buffer": "^6.0.3",
49
49
  "i18n-iso-countries": "^7.12.0",
50
50
  "pako": "^2.1.0",