@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.
package/src/index.ts CHANGED
@@ -57,10 +57,15 @@ import {
57
57
  getScopeFromOuterProof,
58
58
  getSubscopeFromOuterProof,
59
59
  getServiceScopeHash,
60
+ BoundData,
61
+ BindCommittedInputs,
62
+ getBindEVMParameterCommitment,
63
+ getBindParameterCommitment,
64
+ formatBoundData,
65
+ Service,
60
66
  } from "@zkpassport/utils"
61
67
  import { bytesToHex } from "@noble/ciphers/utils"
62
68
  import { noLogger as logger } from "./logger"
63
- import { inflate } from "pako"
64
69
  import i18en from "i18n-iso-countries/langs/en.json"
65
70
  import { Buffer } from "buffer/"
66
71
  import { sha256 } from "@noble/hashes/sha2"
@@ -69,6 +74,8 @@ import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json"
69
74
  import { RegistryClient } from "@zkpassport/registry"
70
75
  import { Bridge, BridgeInterface } from "@obsidion/bridge"
71
76
 
77
+ const VERSION = "0.4.2"
78
+
72
79
  const DEFAULT_DATE_VALUE = new Date(1111, 10, 11)
73
80
 
74
81
  // If Buffer is not defined, then we use the Buffer from the buffer package
@@ -92,7 +99,8 @@ export type QueryResultErrors = {
92
99
  | "sig_check_id_data"
93
100
  | "data_check_integrity"
94
101
  | "outer"
95
- | "disclose"]: {
102
+ | "disclose"
103
+ | "bind"]: {
96
104
  disclose?: QueryResultError<string | number | Date>
97
105
  gte?: QueryResultError<number | Date>
98
106
  lte?: QueryResultError<number | Date>
@@ -346,6 +354,12 @@ export type QueryBuilder = {
346
354
  * @param key The attribute to disclose.
347
355
  */
348
356
  disclose: (key: DisclosableIDCredential) => QueryBuilder
357
+ /**
358
+ * Binds a value to the request.
359
+ * @param key The key of the value to bind.
360
+ * @param value The value to bind the request to.
361
+ */
362
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder
349
363
  /**
350
364
  * Builds the request.
351
365
  *
@@ -370,10 +384,7 @@ export class ZKPassport {
370
384
  private topicToPublicKey: Record<string, string> = {}
371
385
  private topicToBridge: Record<string, BridgeInterface> = {}
372
386
  private topicToRequestReceived: Record<string, boolean> = {}
373
- private topicToService: Record<
374
- string,
375
- { name: string; logo: string; purpose: string; scope?: string; chainId?: number }
376
- > = {}
387
+ private topicToService: Record<string, Service> = {}
377
388
  private topicToProofs: Record<string, Array<ProofResult>> = {}
378
389
  private topicToExpectedProofCount: Record<string, number> = {}
379
390
  private topicToFailedProofCount: Record<string, number> = {}
@@ -503,6 +514,9 @@ export class ZKPassport {
503
514
  }
504
515
  }
505
516
  }
517
+ if ((this.topicToConfig[topic] as Query).bind) {
518
+ neededCircuits.push("bind")
519
+ }
506
520
  // From the circuits needed, determine the expected proof count
507
521
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
508
522
  // Each separate needed circuit adds 1 disclosure proof
@@ -636,17 +650,17 @@ export class ZKPassport {
636
650
  }
637
651
  return this.getZkPassportRequest(topic)
638
652
  },
653
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => {
654
+ this.topicToConfig[topic].bind = {
655
+ ...this.topicToConfig[topic].bind,
656
+ [key]: value,
657
+ }
658
+ return this.getZkPassportRequest(topic)
659
+ },
639
660
  done: () => {
640
- const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString(
641
- "base64",
642
- )
643
- const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString(
644
- "base64",
645
- )
646
- const pubkey = this.topicToPublicKey[topic]
647
661
  this.setExpectedProofCount(topic)
648
662
  return {
649
- url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
663
+ url: this._getUrl(topic),
650
664
  requestId: topic,
651
665
  onRequestReceived: (callback: () => void) =>
652
666
  this.onRequestReceivedCallbacks[topic].push(callback),
@@ -696,6 +710,8 @@ export class ZKPassport {
696
710
  devMode,
697
711
  topicOverride,
698
712
  keyPairOverride,
713
+ cloudProverUrl,
714
+ bridgeUrl,
699
715
  }: {
700
716
  name: string
701
717
  logo: string
@@ -707,10 +723,13 @@ export class ZKPassport {
707
723
  devMode?: boolean
708
724
  topicOverride?: string
709
725
  keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
726
+ cloudProverUrl?: string
727
+ bridgeUrl?: string
710
728
  }): Promise<QueryBuilder> {
711
729
  const bridge = await Bridge.create({
712
730
  keyPair: keyPairOverride,
713
731
  bridgeId: topicOverride,
732
+ bridgeUrl,
714
733
  })
715
734
 
716
735
  const topic = bridge.connection.getBridgeId()
@@ -722,6 +741,8 @@ export class ZKPassport {
722
741
  purpose,
723
742
  scope,
724
743
  chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
744
+ cloudProverUrl,
745
+ bridgeUrl,
725
746
  }
726
747
  this.topicToProofs[topic] = []
727
748
  this.topicToExpectedProofCount[topic] = 0
@@ -777,6 +798,7 @@ export class ZKPassport {
777
798
  fullname: {},
778
799
  document_number: {},
779
800
  outer: {},
801
+ bind: {},
780
802
  }
781
803
  let isCorrect = true
782
804
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -1138,6 +1160,7 @@ export class ZKPassport {
1138
1160
  fullname: {},
1139
1161
  document_number: {},
1140
1162
  outer: {},
1163
+ bind: {},
1141
1164
  }
1142
1165
  let isCorrect = true
1143
1166
  const currentTime = new Date()
@@ -1271,6 +1294,7 @@ export class ZKPassport {
1271
1294
  fullname: {},
1272
1295
  document_number: {},
1273
1296
  outer: {},
1297
+ bind: {},
1274
1298
  }
1275
1299
  let isCorrect = true
1276
1300
  const currentTime = new Date()
@@ -1399,6 +1423,7 @@ export class ZKPassport {
1399
1423
  fullname: {},
1400
1424
  document_number: {},
1401
1425
  outer: {},
1426
+ bind: {},
1402
1427
  }
1403
1428
  let isCorrect = true
1404
1429
  const currentTime = new Date()
@@ -1527,6 +1552,7 @@ export class ZKPassport {
1527
1552
  fullname: {},
1528
1553
  document_number: {},
1529
1554
  outer: {},
1555
+ bind: {},
1530
1556
  }
1531
1557
  let isCorrect = true
1532
1558
  if (
@@ -1591,6 +1617,7 @@ export class ZKPassport {
1591
1617
  fullname: {},
1592
1618
  document_number: {},
1593
1619
  outer: {},
1620
+ bind: {},
1594
1621
  }
1595
1622
  let isCorrect = true
1596
1623
 
@@ -1653,6 +1680,7 @@ export class ZKPassport {
1653
1680
  fullname: {},
1654
1681
  document_number: {},
1655
1682
  outer: {},
1683
+ bind: {},
1656
1684
  }
1657
1685
  let isCorrect = true
1658
1686
  if (
@@ -1700,6 +1728,7 @@ export class ZKPassport {
1700
1728
  fullname: {},
1701
1729
  document_number: {},
1702
1730
  outer: {},
1731
+ bind: {},
1703
1732
  }
1704
1733
  let isCorrect = true
1705
1734
 
@@ -1793,6 +1822,57 @@ export class ZKPassport {
1793
1822
  return { isCorrect, queryResultErrors }
1794
1823
  }
1795
1824
 
1825
+ private checkBindPublicInputs(queryResult: QueryResult, boundData: BoundData) {
1826
+ const queryResultErrors: QueryResultErrors = {
1827
+ sig_check_dsc: {},
1828
+ sig_check_id_data: {},
1829
+ data_check_integrity: {},
1830
+ disclose: {},
1831
+ age: {},
1832
+ birthdate: {},
1833
+ expiry_date: {},
1834
+ document_type: {},
1835
+ issuing_country: {},
1836
+ gender: {},
1837
+ nationality: {},
1838
+ firstname: {},
1839
+ lastname: {},
1840
+ fullname: {},
1841
+ document_number: {},
1842
+ outer: {},
1843
+ bind: {},
1844
+ }
1845
+ let isCorrect = true
1846
+
1847
+ if (queryResult.bind) {
1848
+ if (
1849
+ queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1850
+ boundData.user_address?.toLowerCase().replace("0x", "")
1851
+ ) {
1852
+ console.warn("Bound user address does not match the one from the query results")
1853
+ isCorrect = false
1854
+ queryResultErrors.bind.eq = {
1855
+ expected: queryResult.bind.user_address,
1856
+ received: boundData.user_address,
1857
+ message: "Bound user address does not match the one from the query results",
1858
+ }
1859
+ }
1860
+ if (
1861
+ queryResult.bind.custom_data?.trim().toLowerCase() !==
1862
+ boundData.custom_data?.trim().toLowerCase()
1863
+ ) {
1864
+ console.warn("Bound custom data does not match the one from the query results")
1865
+ isCorrect = false
1866
+ queryResultErrors.bind.eq = {
1867
+ expected: queryResult.bind.custom_data,
1868
+ received: boundData.custom_data,
1869
+ message: "Bound custom data does not match the one from the query results",
1870
+ }
1871
+ }
1872
+ }
1873
+ return { isCorrect, queryResultErrors }
1874
+ }
1875
+
1796
1876
  private async checkPublicInputs(
1797
1877
  proofs: Array<ProofResult>,
1798
1878
  queryResult: QueryResult,
@@ -1831,6 +1911,7 @@ export class ZKPassport {
1831
1911
  fullname: {},
1832
1912
  document_number: {},
1833
1913
  outer: {},
1914
+ bind: {},
1834
1915
  }
1835
1916
 
1836
1917
  // Since the order is important for the commitments, we need to sort the proofs
@@ -1848,6 +1929,7 @@ export class ZKPassport {
1848
1929
  "inclusion_check_nationality",
1849
1930
  "exclusion_check_issuing_country",
1850
1931
  "inclusion_check_issuing_country",
1932
+ "bind",
1851
1933
  ]
1852
1934
  const getIndex = (proof: ProofResult) => {
1853
1935
  const name = proof.name || ""
@@ -2167,6 +2249,27 @@ export class ZKPassport {
2167
2249
  ...queryResultErrors,
2168
2250
  ...queryResultErrorsIssuingCountryExclusion,
2169
2251
  }
2252
+ } else if (!!committedInputs?.bind) {
2253
+ const bindCommittedInputs = committedInputs?.bind as BindCommittedInputs
2254
+ const bindParameterCommitment = isForEVM
2255
+ ? await getBindEVMParameterCommitment(formatBoundData(bindCommittedInputs.data))
2256
+ : await getBindParameterCommitment(formatBoundData(bindCommittedInputs.data))
2257
+ if (!paramCommitments.includes(bindParameterCommitment)) {
2258
+ console.warn("This proof does not verify the bound data")
2259
+ isCorrect = false
2260
+ queryResultErrors.bind.commitment = {
2261
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
2262
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2263
+ message: "This proof does not verify the bound data",
2264
+ }
2265
+ }
2266
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
2267
+ this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
2268
+ isCorrect = isCorrect && isCorrectBind
2269
+ queryResultErrors = {
2270
+ ...queryResultErrors,
2271
+ ...queryResultErrorsBind,
2272
+ }
2170
2273
  }
2171
2274
  uniqueIdentifier = getNullifierFromOuterProof(proofData).toString(10)
2172
2275
  } else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -2598,6 +2701,29 @@ export class ZKPassport {
2598
2701
  ...queryResultErrorsScope,
2599
2702
  }
2600
2703
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2704
+ } else if (proof.name === "bind") {
2705
+ const bindCommittedInputs = proof.committedInputs?.bind as BindCommittedInputs
2706
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2707
+ const calculatedParamCommitment = await getBindParameterCommitment(
2708
+ formatBoundData(bindCommittedInputs.data),
2709
+ )
2710
+ if (paramCommittment !== calculatedParamCommitment) {
2711
+ console.warn("The bound data does not match the one from the proof")
2712
+ isCorrect = false
2713
+ queryResultErrors.bind.commitment = {
2714
+ expected: `Commitment: ${calculatedParamCommitment}`,
2715
+ received: `Commitment: ${paramCommittment}`,
2716
+ message: "The bound data does not match the one from the proof",
2717
+ }
2718
+ }
2719
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
2720
+ this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
2721
+ isCorrect = isCorrect && isCorrectBind
2722
+ queryResultErrors = {
2723
+ ...queryResultErrors,
2724
+ ...queryResultErrorsBind,
2725
+ }
2726
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2601
2727
  }
2602
2728
  }
2603
2729
  return { isCorrect, uniqueIdentifier, queryResultErrors }
@@ -2758,7 +2884,7 @@ export class ZKPassport {
2758
2884
  if (network === "ethereum_sepolia") {
2759
2885
  return {
2760
2886
  ...baseConfig,
2761
- address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2887
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
2762
2888
  }
2763
2889
  } else if (network === "local_anvil") {
2764
2890
  return {
@@ -2869,6 +2995,13 @@ export class ZKPassport {
2869
2995
  ProofType.DISCLOSE.toString(16).padStart(2, "0") +
2870
2996
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2871
2997
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
2998
+ } else if (circuitName === "bind_evm") {
2999
+ const value = proof.committedInputs[circuitName] as BindCommittedInputs
3000
+ compressedCommittedInputs =
3001
+ ProofType.BIND.toString(16).padStart(2, "0") +
3002
+ rightPadArrayWithZeros(formatBoundData(value.data), 500)
3003
+ .map((x) => x.toString(16).padStart(2, "0"))
3004
+ .join("")
2872
3005
  } else {
2873
3006
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`)
2874
3007
  }
@@ -2921,20 +3054,24 @@ export class ZKPassport {
2921
3054
  return params
2922
3055
  }
2923
3056
 
2924
- /**
2925
- * @notice Returns the URL of the request.
2926
- * @param requestId The request ID.
2927
- * @returns The URL of the request.
2928
- */
2929
- public getUrl(requestId: string) {
2930
- const pubkey = this.topicToPublicKey[requestId]
3057
+ private _getUrl(requestId: string) {
2931
3058
  const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString(
2932
3059
  "base64",
2933
3060
  )
2934
3061
  const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString(
2935
3062
  "base64",
2936
3063
  )
2937
- return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`
3064
+ const pubkey = this.topicToPublicKey[requestId]
3065
+ return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}&v=${VERSION}`
3066
+ }
3067
+
3068
+ /**
3069
+ * @notice Returns the URL of the request.
3070
+ * @param requestId The request ID.
3071
+ * @returns The URL of the request.
3072
+ */
3073
+ public getUrl(requestId: string) {
3074
+ return this._getUrl(requestId)
2938
3075
  }
2939
3076
 
2940
3077
  /**