@zkpassport/sdk 0.3.0 → 0.3.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.
@@ -17,6 +17,7 @@ export type QueryResultErrors = {
17
17
  commitment?: QueryResultError<string>;
18
18
  date?: QueryResultError<string>;
19
19
  certificate?: QueryResultError<string>;
20
+ scope?: QueryResultError<string>;
20
21
  };
21
22
  };
22
23
  export type SolidityVerifierParameters = {
@@ -26,6 +27,9 @@ export type SolidityVerifierParameters = {
26
27
  committedInputs: string;
27
28
  committedInputCounts: number[];
28
29
  validityPeriodInDays: number;
30
+ scope: string;
31
+ subscope: string;
32
+ devMode: boolean;
29
33
  };
30
34
  export type EVMChain = "ethereum_sepolia" | "local_anvil";
31
35
  export type * from "@zkpassport/utils";
@@ -192,15 +196,17 @@ export declare class ZKPassport {
192
196
  * @param purpose To explain what you want to do with the user's data
193
197
  * @param scope Scope this request to a specific use case
194
198
  * @param validity How many days ago should have the ID been last scanned by the user?
199
+ * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
195
200
  * @returns The query builder object.
196
201
  */
197
- request({ name, logo, purpose, scope, mode, validity, topicOverride, keyPairOverride, }: {
202
+ request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }: {
198
203
  name: string;
199
204
  logo: string;
200
205
  purpose: string;
201
206
  scope?: string;
202
207
  mode?: ProofMode;
203
208
  validity?: number;
209
+ devMode?: boolean;
204
210
  topicOverride?: string;
205
211
  keyPairOverride?: {
206
212
  privateKey: Uint8Array;
@@ -215,6 +221,8 @@ export declare class ZKPassport {
215
221
  private checkIssuingCountryExclusionPublicInputs;
216
222
  private checkNationalityInclusionPublicInputs;
217
223
  private checkIssuingCountryInclusionPublicInputs;
224
+ private checkScopeFromDisclosureProof;
225
+ private checkCertificateRegistryRoot;
218
226
  private checkPublicInputs;
219
227
  /**
220
228
  * @notice Verify the proofs received from the mobile app.
@@ -224,17 +232,20 @@ export declare class ZKPassport {
224
232
  * @returns An object containing the unique identifier associated to the user
225
233
  * and a boolean indicating whether the proofs were successfully verified.
226
234
  */
227
- verify({ proofs, queryResult, validity, }: {
235
+ verify({ proofs, queryResult, validity, scope, devMode, }: {
228
236
  proofs: Array<ProofResult>;
229
237
  queryResult: QueryResult;
230
238
  validity?: number;
239
+ scope?: string;
240
+ devMode?: boolean;
231
241
  }): Promise<{
232
242
  uniqueIdentifier: string | undefined;
233
243
  verified: boolean;
234
244
  queryResultErrors?: QueryResultErrors;
235
245
  }>;
236
246
  getSolidityVerifierDetails(network: EVMChain): {
237
- address: string;
247
+ address: `0x${string}`;
248
+ functionName: string;
238
249
  abi: {
239
250
  type: "function" | "event" | "constructor";
240
251
  name: string;
@@ -250,7 +261,13 @@ export declare class ZKPassport {
250
261
  }[];
251
262
  }[];
252
263
  };
253
- getSolidityVerifierParameters(proof: ProofResult, validityPeriodInDays?: number): SolidityVerifierParameters;
264
+ getSolidityVerifierParameters({ proof, validityPeriodInDays, domain, scope, devMode, }: {
265
+ proof: ProofResult;
266
+ validityPeriodInDays?: number;
267
+ domain?: string;
268
+ scope?: string;
269
+ devMode?: boolean;
270
+ }): SolidityVerifierParameters;
254
271
  /**
255
272
  * @notice Returns the URL of the request.
256
273
  * @param requestId The request ID.
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomBytes } from "crypto";
2
2
  import { getAlpha3Code, registerLocale } from "i18n-iso-countries";
3
- 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, } from "@zkpassport/utils";
3
+ 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, } from "@zkpassport/utils";
4
4
  import { bytesToHex } from "@noble/ciphers/utils";
5
5
  import { getWebSocketClient } from "./websocket";
6
6
  import { createEncryptedJsonRpcRequest } from "./json-rpc";
@@ -9,9 +9,10 @@ import { noLogger as logger } from "./logger";
9
9
  import { inflate } from "pako";
10
10
  import i18en from "i18n-iso-countries/langs/en.json";
11
11
  import { Buffer } from "buffer/";
12
- import { sha256 } from "@noble/hashes/sha256";
12
+ import { sha256 } from "@noble/hashes/sha2";
13
13
  import { hexToBytes } from "@noble/hashes/utils";
14
14
  import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json";
15
+ import { RegistryClient } from "@zkpassport/registry";
15
16
  const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
16
17
  // If Buffer is not defined, then we use the Buffer from the buffer package
17
18
  if (typeof globalThis.Buffer === "undefined") {
@@ -36,9 +37,12 @@ function hasRequestedAccessToField(credentialsRequest, field) {
36
37
  return false;
37
38
  }
38
39
  function normalizeCountry(country) {
40
+ if (country === "Zero Knowledge Republic") {
41
+ return "ZKR";
42
+ }
39
43
  let normalizedCountry;
40
44
  const alpha3 = getAlpha3Code(country, "en");
41
- normalizedCountry = alpha3 || country;
45
+ normalizedCountry = alpha3 || country || "ZKR";
42
46
  return normalizedCountry;
43
47
  }
44
48
  function numericalCompare(fnName, key, value, requestId, requestIdToConfig) {
@@ -95,6 +99,8 @@ export class ZKPassport {
95
99
  proofs: this.topicToProofs[topic],
96
100
  queryResult: result,
97
101
  validity: this.topicToLocalConfig[topic]?.validity,
102
+ scope: this.topicToService[topic]?.scope,
103
+ devMode: this.topicToLocalConfig[topic]?.devMode,
98
104
  });
99
105
  delete this.topicToProofs[topic];
100
106
  const hasFailedProofs = this.topicToFailedProofCount[topic] > 0;
@@ -341,9 +347,10 @@ export class ZKPassport {
341
347
  * @param purpose To explain what you want to do with the user's data
342
348
  * @param scope Scope this request to a specific use case
343
349
  * @param validity How many days ago should have the ID been last scanned by the user?
350
+ * @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
344
351
  * @returns The query builder object.
345
352
  */
346
- async request({ name, logo, purpose, scope, mode, validity, topicOverride, keyPairOverride, }) {
353
+ async request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }) {
347
354
  const topic = topicOverride || randomBytes(16).toString("hex");
348
355
  const keyPair = keyPairOverride || (await generateECDHKeyPair());
349
356
  this.topicToKeyPair[topic] = {
@@ -358,6 +365,7 @@ export class ZKPassport {
358
365
  // Default to 6 months
359
366
  validity: validity || 6 * 30,
360
367
  mode: mode || "fast",
368
+ devMode: devMode || false,
361
369
  };
362
370
  this.onRequestReceivedCallbacks[topic] = [];
363
371
  this.onGeneratingProofCallbacks[topic] = [];
@@ -1234,16 +1242,61 @@ export class ZKPassport {
1234
1242
  }
1235
1243
  return { isCorrect, queryResultErrors };
1236
1244
  }
1237
- async checkPublicInputs(proofs, queryResult, validity) {
1245
+ checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope) {
1246
+ let isCorrect = true;
1247
+ if (this.domain && getScopeHash(this.domain) !== BigInt(proofData.publicInputs[1])) {
1248
+ console.warn("The proof comes from a different domain than the one expected");
1249
+ isCorrect = false;
1250
+ queryResultErrors[key].scope = {
1251
+ expected: `Scope: ${getScopeHash(this.domain).toString()}`,
1252
+ received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
1253
+ message: "The proof comes from a different domain than the one expected",
1254
+ };
1255
+ }
1256
+ if (scope && getScopeHash(scope) !== BigInt(proofData.publicInputs[2])) {
1257
+ console.warn("The proof uses a different scope than the one expected");
1258
+ isCorrect = false;
1259
+ queryResultErrors[key].scope = {
1260
+ expected: `Scope: ${getScopeHash(scope).toString()}`,
1261
+ received: `Scope: ${BigInt(proofData.publicInputs[2]).toString()}`,
1262
+ message: "The proof uses a different scope than the one expected",
1263
+ };
1264
+ }
1265
+ return { isCorrect, queryResultErrors };
1266
+ }
1267
+ async checkCertificateRegistryRoot(root, queryResultErrors, outer) {
1268
+ let isCorrect = true;
1269
+ try {
1270
+ // Maintained certificate registry settled onchain
1271
+ // Here we use Ethereum Sepolia
1272
+ const registryClient = new RegistryClient({ chainId: 11155111 });
1273
+ await registryClient.getCertificates(`0x${root}`);
1274
+ }
1275
+ catch (error) {
1276
+ console.warn(error);
1277
+ // Check the legacy static roots that were used before the registry was deployed onchain
1278
+ const VALID_CERTIFICATE_REGISTRY_ROOT = [
1279
+ BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
1280
+ BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
1281
+ BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
1282
+ ];
1283
+ if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(BigInt(root))) {
1284
+ console.warn("The ID was signed by an unrecognized root certificate");
1285
+ isCorrect = false;
1286
+ queryResultErrors[outer ? "outer" : "sig_check_dsc"].certificate = {
1287
+ expected: `A valid root from ZKPassport Registry`,
1288
+ received: `Got invalid certificate registry root: ${root}`,
1289
+ message: "The ID was signed by an unrecognized root certificate",
1290
+ };
1291
+ }
1292
+ }
1293
+ return { isCorrect, queryResultErrors };
1294
+ }
1295
+ async checkPublicInputs(proofs, queryResult, validity, scope) {
1238
1296
  let commitmentIn;
1239
1297
  let commitmentOut;
1240
1298
  let isCorrect = true;
1241
1299
  let uniqueIdentifier;
1242
- const VALID_CERTIFICATE_REGISTRY_ROOT = [
1243
- BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
1244
- BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
1245
- BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
1246
- ];
1247
1300
  const currentTime = new Date();
1248
1301
  const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
1249
1302
  let queryResultErrors = {
@@ -1291,15 +1344,12 @@ export class ZKPassport {
1291
1344
  if (proof.name?.startsWith("outer")) {
1292
1345
  const isForEVM = proof.name?.startsWith("outer_evm");
1293
1346
  const certificateRegistryRoot = getCertificateRegistryRootFromOuterProof(proofData);
1294
- if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(certificateRegistryRoot)) {
1295
- console.warn("The ID was signed by an unrecognized root certificate");
1296
- isCorrect = false;
1297
- queryResultErrors.outer.certificate = {
1298
- expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
1299
- received: `Certificate registry root: ${certificateRegistryRoot.toString()}`,
1300
- message: "The ID was signed by an unrecognized root certificate",
1301
- };
1302
- }
1347
+ const { isCorrect: isCorrectCertificateRegistryRoot, queryResultErrors: queryResultErrorsCertificateRegistryRoot, } = await this.checkCertificateRegistryRoot(certificateRegistryRoot.toString(16), queryResultErrors, true);
1348
+ isCorrect = isCorrect && isCorrectCertificateRegistryRoot;
1349
+ queryResultErrors = {
1350
+ ...queryResultErrors,
1351
+ ...queryResultErrorsCertificateRegistryRoot,
1352
+ };
1303
1353
  const currentDate = getCurrentDateFromOuterProof(proofData);
1304
1354
  const todayToCurrentDate = today.getTime() - currentDate.getTime();
1305
1355
  const differenceInDays = validity ?? 180;
@@ -1327,6 +1377,24 @@ export class ZKPassport {
1327
1377
  message: "The proof does not verify all the requested conditions and information",
1328
1378
  };
1329
1379
  }
1380
+ if (this.domain && getScopeHash(this.domain) !== getScopeFromOuterProof(proofData)) {
1381
+ console.warn("The proof comes from a different domain than the one expected");
1382
+ isCorrect = false;
1383
+ queryResultErrors.outer.scope = {
1384
+ expected: `Scope: ${getScopeHash(this.domain).toString()}`,
1385
+ received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
1386
+ message: "The proof comes from a different domain than the one expected",
1387
+ };
1388
+ }
1389
+ if (scope && getScopeHash(scope) !== getSubscopeFromOuterProof(proofData)) {
1390
+ console.warn("The proof uses a different scope than the one expected");
1391
+ isCorrect = false;
1392
+ queryResultErrors.outer.scope = {
1393
+ expected: `Scope: ${getScopeHash(scope).toString()}`,
1394
+ received: `Scope: ${getSubscopeFromOuterProof(proofData).toString()}`,
1395
+ message: "The proof uses a different scope than the one expected",
1396
+ };
1397
+ }
1330
1398
  if (!!committedInputs?.compare_age) {
1331
1399
  const ageCommittedInputs = committedInputs?.compare_age;
1332
1400
  const ageParameterCommitment = isForEVM
@@ -1504,15 +1572,12 @@ export class ZKPassport {
1504
1572
  else if (proof.name?.startsWith("sig_check_dsc")) {
1505
1573
  commitmentOut = getCommitmentFromDSCProof(proofData);
1506
1574
  const merkleRoot = getMerkleRootFromDSCProof(proofData);
1507
- if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(merkleRoot)) {
1508
- console.warn("The ID was signed by an unrecognized root certificate");
1509
- isCorrect = false;
1510
- queryResultErrors.sig_check_dsc.certificate = {
1511
- expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
1512
- received: `Certificate registry root: ${merkleRoot.toString()}`,
1513
- message: "The ID was signed by an unrecognized root certificate",
1514
- };
1515
- }
1575
+ const { isCorrect: isCorrectCertificateRegistryRoot, queryResultErrors: queryResultErrorsCertificateRegistryRoot, } = await this.checkCertificateRegistryRoot(merkleRoot.toString(16), queryResultErrors, false);
1576
+ isCorrect = isCorrect && isCorrectCertificateRegistryRoot;
1577
+ queryResultErrors = {
1578
+ ...queryResultErrors,
1579
+ ...queryResultErrorsCertificateRegistryRoot,
1580
+ };
1516
1581
  }
1517
1582
  else if (proof.name?.startsWith("sig_check_id_data")) {
1518
1583
  commitmentIn = getCommitmentInFromIDDataProof(proofData);
@@ -1577,11 +1642,18 @@ export class ZKPassport {
1577
1642
  message: "The disclosed data does not match the data committed by the proof",
1578
1643
  };
1579
1644
  }
1645
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "disclose", scope);
1646
+ isCorrect = isCorrect && isCorrectScope;
1647
+ queryResultErrors = {
1648
+ ...queryResultErrors,
1649
+ ...queryResultErrorsScope,
1650
+ };
1580
1651
  const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } = this.checkDiscloseBytesPublicInputs(proof, queryResult);
1581
- isCorrect = isCorrect && isCorrectDisclose;
1652
+ isCorrect = isCorrect && isCorrectDisclose && isCorrectScope;
1582
1653
  queryResultErrors = {
1583
1654
  ...queryResultErrors,
1584
1655
  ...queryResultErrorsDisclose,
1656
+ ...queryResultErrorsScope,
1585
1657
  };
1586
1658
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1587
1659
  }
@@ -1608,11 +1680,13 @@ export class ZKPassport {
1608
1680
  message: "The conditions for the age check do not match the conditions checked by the proof",
1609
1681
  };
1610
1682
  }
1683
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "age", scope);
1611
1684
  const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } = this.checkAgePublicInputs(proof, queryResult);
1612
- isCorrect = isCorrect && isCorrectAge;
1685
+ isCorrect = isCorrect && isCorrectAge && isCorrectScope;
1613
1686
  queryResultErrors = {
1614
1687
  ...queryResultErrors,
1615
1688
  ...queryResultErrorsAge,
1689
+ ...queryResultErrorsScope,
1616
1690
  };
1617
1691
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1618
1692
  }
@@ -1639,11 +1713,13 @@ export class ZKPassport {
1639
1713
  message: "The conditions for the birthdate check do not match the conditions checked by the proof",
1640
1714
  };
1641
1715
  }
1716
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "birthdate", scope);
1642
1717
  const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } = this.checkBirthdatePublicInputs(proof, queryResult);
1643
- isCorrect = isCorrect && isCorrectBirthdate;
1718
+ isCorrect = isCorrect && isCorrectBirthdate && isCorrectScope;
1644
1719
  queryResultErrors = {
1645
1720
  ...queryResultErrors,
1646
1721
  ...queryResultErrorsBirthdate,
1722
+ ...queryResultErrorsScope,
1647
1723
  };
1648
1724
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1649
1725
  }
@@ -1670,11 +1746,13 @@ export class ZKPassport {
1670
1746
  message: "The conditions for the expiry date check do not match the conditions checked by the proof",
1671
1747
  };
1672
1748
  }
1749
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "expiry_date", scope);
1673
1750
  const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } = this.checkExpiryDatePublicInputs(proof, queryResult);
1674
- isCorrect = isCorrect && isCorrectExpiryDate;
1751
+ isCorrect = isCorrect && isCorrectExpiryDate && isCorrectScope;
1675
1752
  queryResultErrors = {
1676
1753
  ...queryResultErrors,
1677
1754
  ...queryResultErrorsExpiryDate,
1755
+ ...queryResultErrorsScope,
1678
1756
  };
1679
1757
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1680
1758
  }
@@ -1701,11 +1779,13 @@ export class ZKPassport {
1701
1779
  message: "The committed country list for the exclusion check does not match the one from the proof",
1702
1780
  };
1703
1781
  }
1782
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
1704
1783
  const { isCorrect: isCorrectNationalityExclusion, queryResultErrors: queryResultErrorsNationalityExclusion, } = this.checkNationalityExclusionPublicInputs(queryResult, countryList);
1705
- isCorrect = isCorrect && isCorrectNationalityExclusion;
1784
+ isCorrect = isCorrect && isCorrectNationalityExclusion && isCorrectScope;
1706
1785
  queryResultErrors = {
1707
1786
  ...queryResultErrors,
1708
1787
  ...queryResultErrorsNationalityExclusion,
1788
+ ...queryResultErrorsScope,
1709
1789
  };
1710
1790
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1711
1791
  }
@@ -1732,11 +1812,13 @@ export class ZKPassport {
1732
1812
  message: "The committed country list for the issuing country exclusion check does not match the one from the proof",
1733
1813
  };
1734
1814
  }
1815
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
1735
1816
  const { isCorrect: isCorrectIssuingCountryExclusion, queryResultErrors: queryResultErrorsIssuingCountryExclusion, } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList);
1736
- isCorrect = isCorrect && isCorrectIssuingCountryExclusion;
1817
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion && isCorrectScope;
1737
1818
  queryResultErrors = {
1738
1819
  ...queryResultErrors,
1739
1820
  ...queryResultErrorsIssuingCountryExclusion,
1821
+ ...queryResultErrorsScope,
1740
1822
  };
1741
1823
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1742
1824
  }
@@ -1763,11 +1845,13 @@ export class ZKPassport {
1763
1845
  message: "The committed country list for the nationality inclusion check does not match the one from the proof",
1764
1846
  };
1765
1847
  }
1848
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
1766
1849
  const { isCorrect: isCorrectNationalityInclusion, queryResultErrors: queryResultErrorsNationalityInclusion, } = this.checkNationalityInclusionPublicInputs(queryResult, countryList);
1767
- isCorrect = isCorrect && isCorrectNationalityInclusion;
1850
+ isCorrect = isCorrect && isCorrectNationalityInclusion && isCorrectScope;
1768
1851
  queryResultErrors = {
1769
1852
  ...queryResultErrors,
1770
1853
  ...queryResultErrorsNationalityInclusion,
1854
+ ...queryResultErrorsScope,
1771
1855
  };
1772
1856
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1773
1857
  }
@@ -1794,11 +1878,13 @@ export class ZKPassport {
1794
1878
  message: "The committed country list for the issuing country inclusion check does not match the one from the proof",
1795
1879
  };
1796
1880
  }
1881
+ const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
1797
1882
  const { isCorrect: isCorrectIssuingCountryInclusion, queryResultErrors: queryResultErrorsIssuingCountryInclusion, } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList);
1798
- isCorrect = isCorrect && isCorrectIssuingCountryInclusion;
1883
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion && isCorrectScope;
1799
1884
  queryResultErrors = {
1800
1885
  ...queryResultErrors,
1801
1886
  ...queryResultErrorsIssuingCountryInclusion,
1887
+ ...queryResultErrorsScope,
1802
1888
  };
1803
1889
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
1804
1890
  }
@@ -1813,7 +1899,7 @@ export class ZKPassport {
1813
1899
  * @returns An object containing the unique identifier associated to the user
1814
1900
  * and a boolean indicating whether the proofs were successfully verified.
1815
1901
  */
1816
- async verify({ proofs, queryResult, validity, }) {
1902
+ async verify({ proofs, queryResult, validity, scope, devMode = false, }) {
1817
1903
  const formattedResult = queryResult;
1818
1904
  // Make sure to reconvert the dates to Date objects
1819
1905
  if (formattedResult.birthdate && formattedResult.birthdate.disclose) {
@@ -1827,10 +1913,17 @@ export class ZKPassport {
1827
1913
  let verified = true;
1828
1914
  let uniqueIdentifier;
1829
1915
  let queryResultErrors;
1830
- const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity);
1916
+ const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope);
1831
1917
  uniqueIdentifier = uniqueIdentifierFromPublicInputs;
1832
1918
  verified = isCorrect;
1833
1919
  queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
1920
+ if (uniqueIdentifier && BigInt(uniqueIdentifier) === BigInt(0) && !devMode) {
1921
+ // If the unique identifier is 0 and it is not in dev mode,
1922
+ // the proofs are considered invalid as these are mock proofs only meant
1923
+ // for testing purposes
1924
+ verified = false;
1925
+ console.warn("You are trying to verify a mock proof. This is only allowed in dev mode. To enable dev mode, set the `devMode` parameter to `true` in the request function parameters.");
1926
+ }
1834
1927
  // Only proceed with the proof verification if the public inputs are correct
1835
1928
  if (verified) {
1836
1929
  for (const proof of proofs) {
@@ -1840,24 +1933,23 @@ export class ZKPassport {
1840
1933
  try {
1841
1934
  const { createPublicClient, http } = await import("viem");
1842
1935
  const { sepolia } = await import("viem/chains");
1843
- const verifierDetails = this.getSolidityVerifierDetails("ethereum_sepolia");
1936
+ const { address, abi, functionName } = this.getSolidityVerifierDetails("ethereum_sepolia");
1844
1937
  const client = createPublicClient({
1845
1938
  chain: sepolia,
1846
1939
  transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
1847
1940
  });
1848
- const params = this.getSolidityVerifierParameters(proof);
1941
+ const params = this.getSolidityVerifierParameters({
1942
+ proof,
1943
+ validityPeriodInDays: validity,
1944
+ domain: this.domain,
1945
+ scope,
1946
+ devMode,
1947
+ });
1849
1948
  const result = await client.readContract({
1850
- address: verifierDetails.address,
1851
- abi: verifierDetails.abi,
1852
- functionName: "verifyProof",
1853
- args: [
1854
- params.vkeyHash,
1855
- params.proof,
1856
- params.publicInputs,
1857
- params.committedInputs,
1858
- params.committedInputCounts,
1859
- params.validityPeriodInDays,
1860
- ],
1949
+ address,
1950
+ abi,
1951
+ functionName,
1952
+ args: [params],
1861
1953
  });
1862
1954
  const isVerified = Array.isArray(result) ? Boolean(result[0]) : false;
1863
1955
  verified = isVerified;
@@ -1892,21 +1984,25 @@ export class ZKPassport {
1892
1984
  return { uniqueIdentifier, verified, queryResultErrors };
1893
1985
  }
1894
1986
  getSolidityVerifierDetails(network) {
1987
+ const baseConfig = {
1988
+ functionName: "verifyProof",
1989
+ abi: ZKPassportVerifierAbi.abi,
1990
+ };
1895
1991
  if (network === "ethereum_sepolia") {
1896
1992
  return {
1897
- address: "0xca644D3424c2ee577FaaF2b56C0f9D1937E8e87C",
1898
- abi: ZKPassportVerifierAbi.abi,
1993
+ ...baseConfig,
1994
+ address: "0x8c6982D77f7a8f60aE3133cA9b2FAA6f3e78c394",
1899
1995
  };
1900
1996
  }
1901
1997
  else if (network === "local_anvil") {
1902
1998
  return {
1999
+ ...baseConfig,
1903
2000
  address: "0x0",
1904
- abi: ZKPassportVerifierAbi.abi,
1905
2001
  };
1906
2002
  }
1907
2003
  throw new Error(`Unsupported network: ${network}`);
1908
2004
  }
1909
- getSolidityVerifierParameters(proof, validityPeriodInDays = 7) {
2005
+ getSolidityVerifierParameters({ proof, validityPeriodInDays = 7, domain, scope, devMode = false, }) {
1910
2006
  if (!proof.name?.startsWith("outer_evm")) {
1911
2007
  throw new Error("This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.");
1912
2008
  }
@@ -2032,6 +2128,9 @@ export class ZKPassport {
2032
2128
  committedInputs: `0x${compressedCommittedInputs}`,
2033
2129
  committedInputCounts: committedInputCountsArray,
2034
2130
  validityPeriodInDays,
2131
+ scope: domain ?? this.domain,
2132
+ subscope: scope ?? "",
2133
+ devMode,
2035
2134
  };
2036
2135
  return params;
2037
2136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkpassport/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.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,7 +42,8 @@
42
42
  "@noble/ciphers": "^1.2.1",
43
43
  "@noble/hashes": "^1.7.2",
44
44
  "@noble/secp256k1": "^2.2.3",
45
- "@zkpassport/utils": "^0.4.7",
45
+ "@zkpassport/registry": "^0.1.8",
46
+ "@zkpassport/utils": "^0.7.3",
46
47
  "buffer": "^6.0.3",
47
48
  "i18n-iso-countries": "^7.12.0",
48
49
  "pako": "^2.1.0",