@zkpassport/sdk 0.4.0 → 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.
package/src/index.ts CHANGED
@@ -57,6 +57,11 @@ import {
57
57
  getScopeFromOuterProof,
58
58
  getSubscopeFromOuterProof,
59
59
  getServiceScopeHash,
60
+ BoundData,
61
+ BindCommittedInputs,
62
+ getBindEVMParameterCommitment,
63
+ getBindParameterCommitment,
64
+ formatBoundData,
60
65
  } from "@zkpassport/utils"
61
66
  import { bytesToHex } from "@noble/ciphers/utils"
62
67
  import { noLogger as logger } from "./logger"
@@ -92,7 +97,8 @@ export type QueryResultErrors = {
92
97
  | "sig_check_id_data"
93
98
  | "data_check_integrity"
94
99
  | "outer"
95
- | "disclose"]: {
100
+ | "disclose"
101
+ | "bind"]: {
96
102
  disclose?: QueryResultError<string | number | Date>
97
103
  gte?: QueryResultError<number | Date>
98
104
  lte?: QueryResultError<number | Date>
@@ -346,6 +352,12 @@ export type QueryBuilder = {
346
352
  * @param key The attribute to disclose.
347
353
  */
348
354
  disclose: (key: DisclosableIDCredential) => QueryBuilder
355
+ /**
356
+ * Binds a value to the request.
357
+ * @param key The key of the value to bind.
358
+ * @param value The value to bind the request to.
359
+ */
360
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder
349
361
  /**
350
362
  * Builds the request.
351
363
  *
@@ -503,6 +515,9 @@ export class ZKPassport {
503
515
  }
504
516
  }
505
517
  }
518
+ if ((this.topicToConfig[topic] as Query).bind) {
519
+ neededCircuits.push("bind")
520
+ }
506
521
  // From the circuits needed, determine the expected proof count
507
522
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
508
523
  // Each separate needed circuit adds 1 disclosure proof
@@ -636,6 +651,13 @@ export class ZKPassport {
636
651
  }
637
652
  return this.getZkPassportRequest(topic)
638
653
  },
654
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => {
655
+ this.topicToConfig[topic].bind = {
656
+ ...this.topicToConfig[topic].bind,
657
+ [key]: value,
658
+ }
659
+ return this.getZkPassportRequest(topic)
660
+ },
639
661
  done: () => {
640
662
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString(
641
663
  "base64",
@@ -777,6 +799,7 @@ export class ZKPassport {
777
799
  fullname: {},
778
800
  document_number: {},
779
801
  outer: {},
802
+ bind: {},
780
803
  }
781
804
  let isCorrect = true
782
805
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -1138,6 +1161,7 @@ export class ZKPassport {
1138
1161
  fullname: {},
1139
1162
  document_number: {},
1140
1163
  outer: {},
1164
+ bind: {},
1141
1165
  }
1142
1166
  let isCorrect = true
1143
1167
  const currentTime = new Date()
@@ -1271,6 +1295,7 @@ export class ZKPassport {
1271
1295
  fullname: {},
1272
1296
  document_number: {},
1273
1297
  outer: {},
1298
+ bind: {},
1274
1299
  }
1275
1300
  let isCorrect = true
1276
1301
  const currentTime = new Date()
@@ -1399,6 +1424,7 @@ export class ZKPassport {
1399
1424
  fullname: {},
1400
1425
  document_number: {},
1401
1426
  outer: {},
1427
+ bind: {},
1402
1428
  }
1403
1429
  let isCorrect = true
1404
1430
  const currentTime = new Date()
@@ -1527,6 +1553,7 @@ export class ZKPassport {
1527
1553
  fullname: {},
1528
1554
  document_number: {},
1529
1555
  outer: {},
1556
+ bind: {},
1530
1557
  }
1531
1558
  let isCorrect = true
1532
1559
  if (
@@ -1591,6 +1618,7 @@ export class ZKPassport {
1591
1618
  fullname: {},
1592
1619
  document_number: {},
1593
1620
  outer: {},
1621
+ bind: {},
1594
1622
  }
1595
1623
  let isCorrect = true
1596
1624
 
@@ -1653,6 +1681,7 @@ export class ZKPassport {
1653
1681
  fullname: {},
1654
1682
  document_number: {},
1655
1683
  outer: {},
1684
+ bind: {},
1656
1685
  }
1657
1686
  let isCorrect = true
1658
1687
  if (
@@ -1700,6 +1729,7 @@ export class ZKPassport {
1700
1729
  fullname: {},
1701
1730
  document_number: {},
1702
1731
  outer: {},
1732
+ bind: {},
1703
1733
  }
1704
1734
  let isCorrect = true
1705
1735
 
@@ -1793,6 +1823,57 @@ export class ZKPassport {
1793
1823
  return { isCorrect, queryResultErrors }
1794
1824
  }
1795
1825
 
1826
+ private checkBindPublicInputs(queryResult: QueryResult, boundData: BoundData) {
1827
+ const queryResultErrors: QueryResultErrors = {
1828
+ sig_check_dsc: {},
1829
+ sig_check_id_data: {},
1830
+ data_check_integrity: {},
1831
+ disclose: {},
1832
+ age: {},
1833
+ birthdate: {},
1834
+ expiry_date: {},
1835
+ document_type: {},
1836
+ issuing_country: {},
1837
+ gender: {},
1838
+ nationality: {},
1839
+ firstname: {},
1840
+ lastname: {},
1841
+ fullname: {},
1842
+ document_number: {},
1843
+ outer: {},
1844
+ bind: {},
1845
+ }
1846
+ let isCorrect = true
1847
+
1848
+ if (queryResult.bind) {
1849
+ if (
1850
+ queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1851
+ boundData.user_address?.toLowerCase().replace("0x", "")
1852
+ ) {
1853
+ console.warn("Bound user address does not match the one from the query results")
1854
+ isCorrect = false
1855
+ queryResultErrors.bind.eq = {
1856
+ expected: queryResult.bind.user_address,
1857
+ received: boundData.user_address,
1858
+ message: "Bound user address does not match the one from the query results",
1859
+ }
1860
+ }
1861
+ if (
1862
+ queryResult.bind.custom_data?.trim().toLowerCase() !==
1863
+ boundData.custom_data?.trim().toLowerCase()
1864
+ ) {
1865
+ console.warn("Bound custom data does not match the one from the query results")
1866
+ isCorrect = false
1867
+ queryResultErrors.bind.eq = {
1868
+ expected: queryResult.bind.custom_data,
1869
+ received: boundData.custom_data,
1870
+ message: "Bound custom data does not match the one from the query results",
1871
+ }
1872
+ }
1873
+ }
1874
+ return { isCorrect, queryResultErrors }
1875
+ }
1876
+
1796
1877
  private async checkPublicInputs(
1797
1878
  proofs: Array<ProofResult>,
1798
1879
  queryResult: QueryResult,
@@ -1831,6 +1912,7 @@ export class ZKPassport {
1831
1912
  fullname: {},
1832
1913
  document_number: {},
1833
1914
  outer: {},
1915
+ bind: {},
1834
1916
  }
1835
1917
 
1836
1918
  // Since the order is important for the commitments, we need to sort the proofs
@@ -1848,6 +1930,7 @@ export class ZKPassport {
1848
1930
  "inclusion_check_nationality",
1849
1931
  "exclusion_check_issuing_country",
1850
1932
  "inclusion_check_issuing_country",
1933
+ "bind",
1851
1934
  ]
1852
1935
  const getIndex = (proof: ProofResult) => {
1853
1936
  const name = proof.name || ""
@@ -2167,6 +2250,27 @@ export class ZKPassport {
2167
2250
  ...queryResultErrors,
2168
2251
  ...queryResultErrorsIssuingCountryExclusion,
2169
2252
  }
2253
+ } else if (!!committedInputs?.bind) {
2254
+ const bindCommittedInputs = committedInputs?.bind as BindCommittedInputs
2255
+ const bindParameterCommitment = isForEVM
2256
+ ? await getBindEVMParameterCommitment(formatBoundData(bindCommittedInputs.data))
2257
+ : await getBindParameterCommitment(formatBoundData(bindCommittedInputs.data))
2258
+ if (!paramCommitments.includes(bindParameterCommitment)) {
2259
+ console.warn("This proof does not verify the bound data")
2260
+ isCorrect = false
2261
+ queryResultErrors.bind.commitment = {
2262
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
2263
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2264
+ message: "This proof does not verify the bound data",
2265
+ }
2266
+ }
2267
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
2268
+ this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
2269
+ isCorrect = isCorrect && isCorrectBind
2270
+ queryResultErrors = {
2271
+ ...queryResultErrors,
2272
+ ...queryResultErrorsBind,
2273
+ }
2170
2274
  }
2171
2275
  uniqueIdentifier = getNullifierFromOuterProof(proofData).toString(10)
2172
2276
  } else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -2598,6 +2702,29 @@ export class ZKPassport {
2598
2702
  ...queryResultErrorsScope,
2599
2703
  }
2600
2704
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2705
+ } else if (proof.name === "bind") {
2706
+ const bindCommittedInputs = proof.committedInputs?.bind as BindCommittedInputs
2707
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2708
+ const calculatedParamCommitment = await getBindParameterCommitment(
2709
+ formatBoundData(bindCommittedInputs.data),
2710
+ )
2711
+ if (paramCommittment !== calculatedParamCommitment) {
2712
+ console.warn("The bound data does not match the one from the proof")
2713
+ isCorrect = false
2714
+ queryResultErrors.bind.commitment = {
2715
+ expected: `Commitment: ${calculatedParamCommitment}`,
2716
+ received: `Commitment: ${paramCommittment}`,
2717
+ message: "The bound data does not match the one from the proof",
2718
+ }
2719
+ }
2720
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
2721
+ this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
2722
+ isCorrect = isCorrect && isCorrectBind
2723
+ queryResultErrors = {
2724
+ ...queryResultErrors,
2725
+ ...queryResultErrorsBind,
2726
+ }
2727
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2601
2728
  }
2602
2729
  }
2603
2730
  return { isCorrect, uniqueIdentifier, queryResultErrors }
@@ -2758,7 +2885,7 @@ export class ZKPassport {
2758
2885
  if (network === "ethereum_sepolia") {
2759
2886
  return {
2760
2887
  ...baseConfig,
2761
- address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2888
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
2762
2889
  }
2763
2890
  } else if (network === "local_anvil") {
2764
2891
  return {
@@ -2869,6 +2996,13 @@ export class ZKPassport {
2869
2996
  ProofType.DISCLOSE.toString(16).padStart(2, "0") +
2870
2997
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2871
2998
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
2999
+ } else if (circuitName === "bind_evm") {
3000
+ const value = proof.committedInputs[circuitName] as BindCommittedInputs
3001
+ compressedCommittedInputs =
3002
+ ProofType.BIND.toString(16).padStart(2, "0") +
3003
+ rightPadArrayWithZeros(formatBoundData(value.data), 500)
3004
+ .map((x) => x.toString(16).padStart(2, "0"))
3005
+ .join("")
2872
3006
  } else {
2873
3007
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`)
2874
3008
  }