@zkpassport/sdk 0.2.14 → 0.3.0

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
@@ -20,17 +20,39 @@ import {
20
20
  getCommitmentInFromDisclosureProof,
21
21
  getMerkleRootFromDSCProof,
22
22
  getCurrentDateFromIntegrityProof,
23
- getMaxAgeFromProof,
24
- getMinAgeFromProof,
25
- getCurrentDateFromAgeProof,
26
- getMinDateFromProof,
27
- getMaxDateFromProof,
28
- getCountryListFromExclusionProof,
29
- getCountryListFromInclusionProof,
30
23
  DisclosedData,
31
24
  formatName,
32
25
  getHostedPackagedCircuitByName,
33
26
  Query,
27
+ getNumberOfPublicInputs,
28
+ getParameterCommitmentFromDisclosureProof,
29
+ getCountryParameterCommitment,
30
+ getDiscloseParameterCommitment,
31
+ getDateParameterCommitment,
32
+ getFormattedDate,
33
+ getCertificateRegistryRootFromOuterProof,
34
+ getParamCommitmentsFromOuterProof,
35
+ AgeCommittedInputs,
36
+ DiscloseCommittedInputs,
37
+ getCurrentDateFromCommittedInputs,
38
+ getMinAgeFromCommittedInputs,
39
+ getMaxAgeFromCommittedInputs,
40
+ getAgeParameterCommitment,
41
+ DateCommittedInputs,
42
+ CountryCommittedInputs,
43
+ getMinDateFromCommittedInputs,
44
+ getMaxDateFromCommittedInputs,
45
+ getCurrentDateFromOuterProof,
46
+ getNullifierFromOuterProof,
47
+ DisclosureCircuitName,
48
+ getAgeEVMParameterCommitment,
49
+ getDateEVMParameterCommitment,
50
+ getDiscloseEVMParameterCommitment,
51
+ getCountryEVMParameterCommitment,
52
+ rightPadArrayWithZeros,
53
+ getCommittedInputCount,
54
+ ProofMode,
55
+ ProofType,
34
56
  } from "@zkpassport/utils"
35
57
  import { bytesToHex } from "@noble/ciphers/utils"
36
58
  import { getWebSocketClient, WebSocketClient } from "./websocket"
@@ -40,6 +62,11 @@ import { noLogger as logger } from "./logger"
40
62
  import { inflate } from "pako"
41
63
  import i18en from "i18n-iso-countries/langs/en.json"
42
64
  import { Buffer } from "buffer/"
65
+ import { sha256 } from "@noble/hashes/sha256"
66
+ import { hexToBytes } from "@noble/hashes/utils"
67
+ import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json"
68
+
69
+ const DEFAULT_DATE_VALUE = new Date(1111, 10, 11)
43
70
 
44
71
  // If Buffer is not defined, then we use the Buffer from the buffer package
45
72
  if (typeof globalThis.Buffer === "undefined") {
@@ -61,6 +88,7 @@ export type QueryResultErrors = {
61
88
  | "sig_check_dsc"
62
89
  | "sig_check_id_data"
63
90
  | "data_check_integrity"
91
+ | "outer"
64
92
  | "disclose"]: {
65
93
  disclose?: QueryResultError<string | number | Date>
66
94
  gte?: QueryResultError<number | Date>
@@ -76,6 +104,17 @@ export type QueryResultErrors = {
76
104
  }
77
105
  }
78
106
 
107
+ export type SolidityVerifierParameters = {
108
+ vkeyHash: string
109
+ proof: string
110
+ publicInputs: string[]
111
+ committedInputs: string
112
+ committedInputCounts: number[]
113
+ validityPeriodInDays: number
114
+ }
115
+
116
+ export type EVMChain = "ethereum_sepolia" | "local_anvil"
117
+
79
118
  registerLocale(i18en)
80
119
 
81
120
  function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
@@ -296,6 +335,7 @@ export class ZKPassport {
296
335
  string,
297
336
  {
298
337
  validity: number
338
+ mode: ProofMode
299
339
  }
300
340
  > = {}
301
341
  private topicToKeyPair: Record<string, { privateKey: Uint8Array; publicKey: Uint8Array }> = {}
@@ -367,6 +407,11 @@ export class ZKPassport {
367
407
  }
368
408
 
369
409
  private setExpectedProofCount(topic: string) {
410
+ // If the mode is not fast, we'll receive only 1 compressed proof
411
+ if (this.topicToLocalConfig[topic].mode !== "fast") {
412
+ this.topicToExpectedProofCount[topic] = 1
413
+ return
414
+ }
370
415
  const fields = Object.keys(this.topicToConfig[topic] as Query).filter((key) =>
371
416
  hasRequestedAccessToField(this.topicToConfig[topic] as Query, key as IDCredential),
372
417
  )
@@ -454,7 +499,13 @@ export class ZKPassport {
454
499
  logger.debug(`User generated proof`)
455
500
  // Uncompress the proof and convert it to a hex string
456
501
  const bytesProof = Buffer.from(request.params.proof, "base64")
502
+ const bytesCommittedInputs = request.params.committedInputs
503
+ ? Buffer.from(request.params.committedInputs, "base64")
504
+ : null
457
505
  const uncompressedProof = inflate(bytesProof)
506
+ const uncompressedCommittedInputs = bytesCommittedInputs
507
+ ? inflate(bytesCommittedInputs)
508
+ : null
458
509
  // The gzip lib in the app compress the proof as ASCII
459
510
  // and since the app passes the proof as a hex string, we can
460
511
  // just decode the bytes as hex characters using the TextDecoder
@@ -464,6 +515,9 @@ export class ZKPassport {
464
515
  vkeyHash: request.params.vkeyHash,
465
516
  name: request.params.name,
466
517
  version: request.params.version,
518
+ committedInputs: uncompressedCommittedInputs
519
+ ? JSON.parse(new TextDecoder().decode(uncompressedCommittedInputs))
520
+ : undefined,
467
521
  }
468
522
  this.topicToProofs[topic].push(processedProof)
469
523
  await Promise.all(
@@ -585,7 +639,7 @@ export class ZKPassport {
585
639
  const pubkey = bytesToHex(this.topicToKeyPair[topic].publicKey)
586
640
  this.setExpectedProofCount(topic)
587
641
  return {
588
- url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
642
+ url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
589
643
  requestId: topic,
590
644
  onRequestReceived: (callback: () => void) =>
591
645
  this.onRequestReceivedCallbacks[topic].push(callback),
@@ -627,6 +681,7 @@ export class ZKPassport {
627
681
  logo,
628
682
  purpose,
629
683
  scope,
684
+ mode,
630
685
  validity,
631
686
  topicOverride,
632
687
  keyPairOverride,
@@ -635,6 +690,7 @@ export class ZKPassport {
635
690
  logo: string
636
691
  purpose: string
637
692
  scope?: string
693
+ mode?: ProofMode
638
694
  validity?: number
639
695
  topicOverride?: string
640
696
  keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
@@ -654,6 +710,7 @@ export class ZKPassport {
654
710
  this.topicToLocalConfig[topic] = {
655
711
  // Default to 6 months
656
712
  validity: validity || 6 * 30,
713
+ mode: mode || "fast",
657
714
  }
658
715
 
659
716
  this.onRequestReceivedCallbacks[topic] = []
@@ -729,31 +786,7 @@ export class ZKPassport {
729
786
  return this.getZkPassportRequest(topic)
730
787
  }
731
788
 
732
- private async checkPublicInputs(
733
- proofs: Array<ProofResult>,
734
- queryResult: QueryResult,
735
- validity?: number,
736
- ) {
737
- let commitmentIn: bigint | undefined
738
- let commitmentOut: bigint | undefined
739
- let isCorrect = true
740
- let uniqueIdentifier: string | undefined
741
- const VALID_CERTIFICATE_REGISTRY_ROOT = [
742
- BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
743
- BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
744
- BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
745
- ]
746
- const defaultDateValue = new Date(1111, 10, 11)
747
- const currentTime = new Date()
748
- const today = new Date(
749
- currentTime.getFullYear(),
750
- currentTime.getMonth(),
751
- currentTime.getDate(),
752
- 0,
753
- 0,
754
- 0,
755
- 0,
756
- )
789
+ private checkDiscloseBytesPublicInputs(proof: ProofResult, queryResult: QueryResult) {
757
790
  const queryResultErrors: QueryResultErrors = {
758
791
  sig_check_dsc: {},
759
792
  sig_check_id_data: {},
@@ -770,553 +803,1445 @@ export class ZKPassport {
770
803
  lastname: {},
771
804
  fullname: {},
772
805
  document_number: {},
806
+ outer: {},
773
807
  }
774
-
775
- // Since the order is important for the commitments, we need to sort the proofs
776
- // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
777
- const sortedProofs = proofs.sort((a, b) => {
778
- const proofOrder = [
779
- "sig_check_dsc",
780
- "sig_check_id_data",
781
- "data_check_integrity",
782
- "disclose_bytes",
783
- "compare_age",
784
- "compare_birthdate",
785
- "compare_expiry",
786
- "exclusion_check_nationality",
787
- "inclusion_check_nationality",
788
- "exclusion_check_issuing_country",
789
- "inclusion_check_issuing_country",
790
- ]
791
- const getIndex = (proof: ProofResult) => {
792
- const name = proof.name || ""
793
- return proofOrder.findIndex((p) => name.startsWith(p))
808
+ let isCorrect = true
809
+ // We can't be certain that the disclosed data is for a passport or an ID card
810
+ // so we need to check both (unless the document type is revealed)
811
+ const disclosedDataPassport = DisclosedData.fromDisclosedBytes(
812
+ (proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
813
+ "passport",
814
+ )
815
+ const disclosedDataIDCard = DisclosedData.fromDisclosedBytes(
816
+ (proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
817
+ "id_card",
818
+ )
819
+ if (queryResult.document_type) {
820
+ // Document type is always at the same index in the disclosed data
821
+ if (
822
+ queryResult.document_type.eq &&
823
+ queryResult.document_type.eq.result &&
824
+ queryResult.document_type.eq.expected !== disclosedDataPassport.documentType
825
+ ) {
826
+ console.warn("Document type does not match the expected document type")
827
+ isCorrect = false
828
+ queryResultErrors.document_type.eq = {
829
+ expected: `${queryResult.document_type.eq.expected}`,
830
+ received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
831
+ message: "Document type does not match the expected document type",
832
+ }
794
833
  }
795
- return getIndex(a) - getIndex(b)
796
- })
797
-
798
- for (const proof of sortedProofs!) {
799
- const proofData = getProofData(proof.proof as string, true)
800
- if (proof.name?.startsWith("sig_check_dsc")) {
801
- commitmentOut = getCommitmentFromDSCProof(proofData)
802
- const merkleRoot = getMerkleRootFromDSCProof(proofData)
803
- if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(merkleRoot)) {
804
- console.warn("The ID was signed by an unrecognized root certificate")
805
- isCorrect = false
806
- queryResultErrors.sig_check_dsc.certificate = {
807
- expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
808
- received: `Certificate registry root: ${merkleRoot.toString()}`,
809
- message: "The ID was signed by an unrecognized root certificate",
810
- }
834
+ if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
835
+ console.warn("Document type does not match the disclosed document type in query result")
836
+ isCorrect = false
837
+ queryResultErrors.document_type.disclose = {
838
+ expected: `${queryResult.document_type.disclose?.result}`,
839
+ received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
840
+ message: "Document type does not match the disclosed document type in query result",
811
841
  }
812
- } else if (proof.name?.startsWith("sig_check_id_data")) {
813
- commitmentIn = getCommitmentInFromIDDataProof(proofData)
814
- if (commitmentIn !== commitmentOut) {
815
- console.warn(
816
- "Failed to check the link between the certificate signature and ID signature",
817
- )
818
- isCorrect = false
819
- queryResultErrors.sig_check_id_data.commitment = {
820
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
821
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
822
- message: "Failed to check the link between the certificate signature and ID signature",
823
- }
842
+ }
843
+ }
844
+ if (queryResult.birthdate) {
845
+ const birthdatePassport = disclosedDataPassport.dateOfBirth
846
+ const birthdateIDCard = disclosedDataIDCard.dateOfBirth
847
+ if (
848
+ queryResult.birthdate.eq &&
849
+ queryResult.birthdate.eq.result &&
850
+ queryResult.birthdate.eq.expected.getTime() !== birthdatePassport.getTime() &&
851
+ queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()
852
+ ) {
853
+ console.warn("Birthdate does not match the expected birthdate")
854
+ isCorrect = false
855
+ queryResultErrors.birthdate.eq = {
856
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
857
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
858
+ message: "Birthdate does not match the expected birthdate",
824
859
  }
825
- commitmentOut = getCommitmentOutFromIDDataProof(proofData)
826
- } else if (proof.name?.startsWith("data_check_integrity")) {
827
- commitmentIn = getCommitmentInFromIntegrityProof(proofData)
828
- if (commitmentIn !== commitmentOut) {
829
- console.warn("Failed to check the link between the ID signature and the data signed")
830
- isCorrect = false
831
- queryResultErrors.data_check_integrity.commitment = {
832
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
833
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
834
- message: "Failed to check the link between the ID signature and the data signed",
835
- }
860
+ }
861
+ if (
862
+ queryResult.birthdate.disclose &&
863
+ queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
864
+ queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()
865
+ ) {
866
+ console.warn("Birthdate does not match the disclosed birthdate in query result")
867
+ isCorrect = false
868
+ queryResultErrors.birthdate.disclose = {
869
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
870
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
871
+ message: "Birthdate does not match the disclosed birthdate in query result",
836
872
  }
837
- commitmentOut = getCommitmentOutFromIntegrityProof(proofData)
838
- const currentDate = getCurrentDateFromIntegrityProof(proofData)
839
- const todayToCurrentDate = today.getTime() - currentDate.getTime()
840
- const differenceInDays = validity ?? 180
841
- const expectedDifference = differenceInDays * 86400000
842
- const actualDifference = today.getTime() - (today.getTime() - expectedDifference)
843
- // The ID should not expire within the next 6 months (or whatever the custom value is)
844
- if (todayToCurrentDate >= actualDifference) {
845
- console.warn(
846
- `The date used to check the validity of the ID is older than ${differenceInDays} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
847
- )
848
- isCorrect = false
849
- queryResultErrors.data_check_integrity.date = {
850
- expected: `Difference: ${differenceInDays} days`,
851
- received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
852
- message:
853
- "The date used to check the validity of the ID is older than the validity period",
854
- }
873
+ }
874
+ }
875
+ if (queryResult.expiry_date) {
876
+ const expiryDatePassport = disclosedDataPassport.dateOfExpiry
877
+ const expiryDateIDCard = disclosedDataIDCard.dateOfExpiry
878
+ if (
879
+ queryResult.expiry_date.eq &&
880
+ queryResult.expiry_date.eq.result &&
881
+ queryResult.expiry_date.eq.expected.getTime() !== expiryDatePassport.getTime() &&
882
+ queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()
883
+ ) {
884
+ console.warn("Expiry date does not match the expected expiry date")
885
+ isCorrect = false
886
+ queryResultErrors.expiry_date.eq = {
887
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
888
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
889
+ message: "Expiry date does not match the expected expiry date",
855
890
  }
856
- } else if (proof.name === "disclose_bytes") {
857
- commitmentIn = getCommitmentInFromDisclosureProof(proofData)
858
- if (commitmentIn !== commitmentOut) {
859
- console.warn(
860
- "Failed to check the link between the validity of the ID and the data to disclose",
861
- )
862
- isCorrect = false
863
- queryResultErrors.disclose.commitment = {
864
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
865
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
866
- message:
867
- "Failed to check the link between the validity of the ID and the data to disclose",
868
- }
891
+ }
892
+ if (
893
+ queryResult.expiry_date.disclose &&
894
+ queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
895
+ queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()
896
+ ) {
897
+ console.warn("Expiry date does not match the disclosed expiry date in query result")
898
+ isCorrect = false
899
+ queryResultErrors.expiry_date.disclose = {
900
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
901
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
902
+ message: "Expiry date does not match the disclosed expiry date in query result",
869
903
  }
870
- // We can't be certain that the disclosed data is for a passport or an ID card
871
- // so we need to check both (unless the document type is revealed)
872
- const disclosedDataPassport = DisclosedData.fromBytesProof(proofData, "passport")
873
- const disclosedDataIDCard = DisclosedData.fromBytesProof(proofData, "id_card")
874
- if (queryResult.document_type) {
875
- // Document type is always at the same index in the disclosed data
876
- if (
877
- queryResult.document_type.eq &&
878
- queryResult.document_type.eq.result &&
879
- queryResult.document_type.eq.expected !== disclosedDataPassport.documentType
880
- ) {
881
- console.warn("Document type does not match the expected document type")
882
- isCorrect = false
883
- queryResultErrors.document_type.eq = {
884
- expected: `${queryResult.document_type.eq.expected}`,
885
- received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
886
- message: "Document type does not match the expected document type",
887
- }
888
- }
889
- if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
890
- console.warn("Document type does not match the disclosed document type in query result")
891
- isCorrect = false
892
- queryResultErrors.document_type.disclose = {
893
- expected: `${queryResult.document_type.disclose?.result}`,
894
- received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
895
- message: "Document type does not match the disclosed document type in query result",
896
- }
897
- }
904
+ }
905
+ }
906
+ if (queryResult.nationality) {
907
+ const nationalityPassport = disclosedDataPassport.nationality
908
+ const nationalityIDCard = disclosedDataIDCard.nationality
909
+ if (
910
+ queryResult.nationality.eq &&
911
+ queryResult.nationality.eq.result &&
912
+ queryResult.nationality.eq.expected !== nationalityPassport &&
913
+ queryResult.nationality.eq.expected !== nationalityIDCard
914
+ ) {
915
+ console.warn("Nationality does not match the expected nationality")
916
+ isCorrect = false
917
+ queryResultErrors.nationality.eq = {
918
+ expected: `${queryResult.nationality.eq.expected}`,
919
+ received: `${nationalityPassport ?? nationalityIDCard}`,
920
+ message: "Nationality does not match the expected nationality",
898
921
  }
899
- if (queryResult.birthdate) {
900
- const birthdatePassport = disclosedDataPassport.dateOfBirth
901
- const birthdateIDCard = disclosedDataIDCard.dateOfBirth
902
- if (
903
- queryResult.birthdate.eq &&
904
- queryResult.birthdate.eq.result &&
905
- queryResult.birthdate.eq.expected.getTime() !== birthdatePassport.getTime() &&
906
- queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()
907
- ) {
908
- console.warn("Birthdate does not match the expected birthdate")
909
- isCorrect = false
910
- queryResultErrors.birthdate.eq = {
911
- expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
912
- received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
913
- message: "Birthdate does not match the expected birthdate",
914
- }
915
- }
916
- if (
917
- queryResult.birthdate.disclose &&
918
- queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
919
- queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()
920
- ) {
921
- console.warn("Birthdate does not match the disclosed birthdate in query result")
922
- isCorrect = false
923
- queryResultErrors.birthdate.disclose = {
924
- expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
925
- received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
926
- message: "Birthdate does not match the disclosed birthdate in query result",
927
- }
928
- }
922
+ }
923
+ if (
924
+ queryResult.nationality.disclose &&
925
+ queryResult.nationality.disclose.result !== nationalityPassport &&
926
+ queryResult.nationality.disclose.result !== nationalityIDCard
927
+ ) {
928
+ console.warn("Nationality does not match the disclosed nationality in query result")
929
+ isCorrect = false
930
+ queryResultErrors.nationality.disclose = {
931
+ expected: `${queryResult.nationality.disclose.result}`,
932
+ received: `${nationalityPassport ?? nationalityIDCard}`,
933
+ message: "Nationality does not match the disclosed nationality in query result",
929
934
  }
930
- if (queryResult.expiry_date) {
931
- const expiryDatePassport = disclosedDataPassport.dateOfExpiry
932
- const expiryDateIDCard = disclosedDataIDCard.dateOfExpiry
933
- if (
934
- queryResult.expiry_date.eq &&
935
- queryResult.expiry_date.eq.result &&
936
- queryResult.expiry_date.eq.expected.getTime() !== expiryDatePassport.getTime() &&
937
- queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()
938
- ) {
939
- console.warn("Expiry date does not match the expected expiry date")
940
- isCorrect = false
941
- queryResultErrors.expiry_date.eq = {
942
- expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
943
- received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
944
- message: "Expiry date does not match the expected expiry date",
945
- }
946
- }
947
- if (
948
- queryResult.expiry_date.disclose &&
949
- queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
950
- queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()
951
- ) {
952
- console.warn("Expiry date does not match the disclosed expiry date in query result")
953
- isCorrect = false
954
- queryResultErrors.expiry_date.disclose = {
955
- expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
956
- received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
957
- message: "Expiry date does not match the disclosed expiry date in query result",
958
- }
959
- }
935
+ }
936
+ }
937
+ if (queryResult.document_number) {
938
+ const documentNumberPassport = disclosedDataPassport.documentNumber
939
+ const documentNumberIDCard = disclosedDataIDCard.documentNumber
940
+ if (
941
+ queryResult.document_number.eq &&
942
+ queryResult.document_number.eq.result &&
943
+ queryResult.document_number.eq.expected !== documentNumberPassport &&
944
+ queryResult.document_number.eq.expected !== documentNumberIDCard
945
+ ) {
946
+ console.warn("Document number does not match the expected document number")
947
+ isCorrect = false
948
+ queryResultErrors.document_number.eq = {
949
+ expected: `${queryResult.document_number.eq.expected}`,
950
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
951
+ message: "Document number does not match the expected document number",
960
952
  }
961
- if (queryResult.nationality) {
962
- const nationalityPassport = disclosedDataPassport.nationality
963
- const nationalityIDCard = disclosedDataIDCard.nationality
964
- if (
965
- queryResult.nationality.eq &&
966
- queryResult.nationality.eq.result &&
967
- queryResult.nationality.eq.expected !== nationalityPassport &&
968
- queryResult.nationality.eq.expected !== nationalityIDCard
969
- ) {
970
- console.warn("Nationality does not match the expected nationality")
971
- isCorrect = false
972
- queryResultErrors.nationality.eq = {
973
- expected: `${queryResult.nationality.eq.expected}`,
974
- received: `${nationalityPassport ?? nationalityIDCard}`,
975
- message: "Nationality does not match the expected nationality",
976
- }
977
- }
978
- if (
979
- queryResult.nationality.disclose &&
980
- queryResult.nationality.disclose.result !== nationalityPassport &&
981
- queryResult.nationality.disclose.result !== nationalityIDCard
982
- ) {
983
- console.warn("Nationality does not match the disclosed nationality in query result")
984
- isCorrect = false
985
- queryResultErrors.nationality.disclose = {
986
- expected: `${queryResult.nationality.disclose.result}`,
987
- received: `${nationalityPassport ?? nationalityIDCard}`,
988
- message: "Nationality does not match the disclosed nationality in query result",
989
- }
990
- }
953
+ }
954
+ if (
955
+ queryResult.document_number.disclose &&
956
+ queryResult.document_number.disclose.result !== documentNumberPassport &&
957
+ queryResult.document_number.disclose.result !== documentNumberIDCard
958
+ ) {
959
+ console.warn("Document number does not match the disclosed document number in query result")
960
+ isCorrect = false
961
+ queryResultErrors.document_number.disclose = {
962
+ expected: `${queryResult.document_number.disclose.result}`,
963
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
964
+ message: "Document number does not match the disclosed document number in query result",
991
965
  }
992
- if (queryResult.document_number) {
993
- const documentNumberPassport = disclosedDataPassport.documentNumber
994
- const documentNumberIDCard = disclosedDataIDCard.documentNumber
995
- if (
996
- queryResult.document_number.eq &&
997
- queryResult.document_number.eq.result &&
998
- queryResult.document_number.eq.expected !== documentNumberPassport &&
999
- queryResult.document_number.eq.expected !== documentNumberIDCard
1000
- ) {
1001
- console.warn("Document number does not match the expected document number")
1002
- isCorrect = false
1003
- queryResultErrors.document_number.eq = {
1004
- expected: `${queryResult.document_number.eq.expected}`,
1005
- received: `${documentNumberPassport ?? documentNumberIDCard}`,
1006
- message: "Document number does not match the expected document number",
1007
- }
1008
- }
1009
- if (
1010
- queryResult.document_number.disclose &&
1011
- queryResult.document_number.disclose.result !== documentNumberPassport &&
1012
- queryResult.document_number.disclose.result !== documentNumberIDCard
1013
- ) {
1014
- console.warn(
1015
- "Document number does not match the disclosed document number in query result",
1016
- )
1017
- isCorrect = false
1018
- queryResultErrors.document_number.disclose = {
1019
- expected: `${queryResult.document_number.disclose.result}`,
1020
- received: `${documentNumberPassport ?? documentNumberIDCard}`,
1021
- message:
1022
- "Document number does not match the disclosed document number in query result",
1023
- }
1024
- }
966
+ }
967
+ }
968
+ if (queryResult.gender) {
969
+ const genderPassport = disclosedDataPassport.gender
970
+ const genderIDCard = disclosedDataIDCard.gender
971
+ if (
972
+ queryResult.gender.eq &&
973
+ queryResult.gender.eq.result &&
974
+ queryResult.gender.eq.expected !== genderPassport &&
975
+ queryResult.gender.eq.expected !== genderIDCard
976
+ ) {
977
+ console.warn("Gender does not match the expected gender")
978
+ isCorrect = false
979
+ queryResultErrors.gender.eq = {
980
+ expected: `${queryResult.gender.eq.expected}`,
981
+ received: `${genderPassport ?? genderIDCard}`,
982
+ message: "Gender does not match the expected gender",
1025
983
  }
1026
- if (queryResult.gender) {
1027
- const genderPassport = disclosedDataPassport.gender
1028
- const genderIDCard = disclosedDataIDCard.gender
1029
- if (
1030
- queryResult.gender.eq &&
1031
- queryResult.gender.eq.result &&
1032
- queryResult.gender.eq.expected !== genderPassport &&
1033
- queryResult.gender.eq.expected !== genderIDCard
1034
- ) {
1035
- console.warn("Gender does not match the expected gender")
1036
- isCorrect = false
1037
- queryResultErrors.gender.eq = {
1038
- expected: `${queryResult.gender.eq.expected}`,
1039
- received: `${genderPassport ?? genderIDCard}`,
1040
- message: "Gender does not match the expected gender",
1041
- }
1042
- }
1043
- if (
1044
- queryResult.gender.disclose &&
1045
- queryResult.gender.disclose.result !== genderPassport &&
1046
- queryResult.gender.disclose.result !== genderIDCard
1047
- ) {
1048
- console.warn("Gender does not match the disclosed gender in query result")
1049
- isCorrect = false
1050
- queryResultErrors.gender.disclose = {
1051
- expected: `${queryResult.gender.disclose.result}`,
1052
- received: `${genderPassport ?? genderIDCard}`,
1053
- message: "Gender does not match the disclosed gender in query result",
1054
- }
1055
- }
984
+ }
985
+ if (
986
+ queryResult.gender.disclose &&
987
+ queryResult.gender.disclose.result !== genderPassport &&
988
+ queryResult.gender.disclose.result !== genderIDCard
989
+ ) {
990
+ console.warn("Gender does not match the disclosed gender in query result")
991
+ isCorrect = false
992
+ queryResultErrors.gender.disclose = {
993
+ expected: `${queryResult.gender.disclose.result}`,
994
+ received: `${genderPassport ?? genderIDCard}`,
995
+ message: "Gender does not match the disclosed gender in query result",
1056
996
  }
1057
- if (queryResult.issuing_country) {
1058
- const issuingCountryPassport = disclosedDataPassport.issuingCountry
1059
- const issuingCountryIDCard = disclosedDataIDCard.issuingCountry
1060
- if (
1061
- queryResult.issuing_country.eq &&
1062
- queryResult.issuing_country.eq.result &&
1063
- queryResult.issuing_country.eq.expected !== issuingCountryPassport &&
1064
- queryResult.issuing_country.eq.expected !== issuingCountryIDCard
1065
- ) {
1066
- console.warn("Issuing country does not match the expected issuing country")
1067
- isCorrect = false
1068
- queryResultErrors.issuing_country.eq = {
1069
- expected: `${queryResult.issuing_country.eq.expected}`,
1070
- received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1071
- message: "Issuing country does not match the expected issuing country",
1072
- }
1073
- }
1074
- if (
1075
- queryResult.issuing_country.disclose &&
1076
- queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
1077
- queryResult.issuing_country.disclose.result !== issuingCountryIDCard
1078
- ) {
1079
- console.warn(
1080
- "Issuing country does not match the disclosed issuing country in query result",
1081
- )
1082
- isCorrect = false
1083
- queryResultErrors.issuing_country.disclose = {
1084
- expected: `${queryResult.issuing_country.disclose.result}`,
1085
- received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1086
- message:
1087
- "Issuing country does not match the disclosed issuing country in query result",
1088
- }
1089
- }
997
+ }
998
+ }
999
+ if (queryResult.issuing_country) {
1000
+ const issuingCountryPassport = disclosedDataPassport.issuingCountry
1001
+ const issuingCountryIDCard = disclosedDataIDCard.issuingCountry
1002
+ if (
1003
+ queryResult.issuing_country.eq &&
1004
+ queryResult.issuing_country.eq.result &&
1005
+ queryResult.issuing_country.eq.expected !== issuingCountryPassport &&
1006
+ queryResult.issuing_country.eq.expected !== issuingCountryIDCard
1007
+ ) {
1008
+ console.warn("Issuing country does not match the expected issuing country")
1009
+ isCorrect = false
1010
+ queryResultErrors.issuing_country.eq = {
1011
+ expected: `${queryResult.issuing_country.eq.expected}`,
1012
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1013
+ message: "Issuing country does not match the expected issuing country",
1090
1014
  }
1091
- if (queryResult.fullname) {
1092
- const fullnamePassport = disclosedDataPassport.name
1093
- const fullnameIDCard = disclosedDataIDCard.name
1094
- if (
1095
- queryResult.fullname.eq &&
1096
- queryResult.fullname.eq.result &&
1097
- formatName(queryResult.fullname.eq.expected).toLowerCase() !==
1098
- fullnamePassport.toLowerCase() &&
1099
- formatName(queryResult.fullname.eq.expected).toLowerCase() !==
1100
- fullnameIDCard.toLowerCase()
1101
- ) {
1102
- console.warn("Fullname does not match the expected fullname")
1103
- isCorrect = false
1104
- queryResultErrors.fullname.eq = {
1105
- expected: `${queryResult.fullname.eq.expected}`,
1106
- received: `${fullnamePassport ?? fullnameIDCard}`,
1107
- message: "Fullname does not match the expected fullname",
1108
- }
1109
- }
1110
- if (
1111
- queryResult.fullname.disclose &&
1112
- formatName(queryResult.fullname.disclose.result).toLowerCase() !==
1113
- fullnamePassport.toLowerCase() &&
1114
- formatName(queryResult.fullname.disclose.result).toLowerCase() !==
1115
- fullnameIDCard.toLowerCase()
1116
- ) {
1117
- console.warn("Fullname does not match the disclosed fullname in query result")
1118
- isCorrect = false
1119
- queryResultErrors.fullname.disclose = {
1120
- expected: `${queryResult.fullname.disclose.result}`,
1121
- received: `${fullnamePassport ?? fullnameIDCard}`,
1122
- message: "Fullname does not match the disclosed fullname in query result",
1123
- }
1124
- }
1015
+ }
1016
+ if (
1017
+ queryResult.issuing_country.disclose &&
1018
+ queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
1019
+ queryResult.issuing_country.disclose.result !== issuingCountryIDCard
1020
+ ) {
1021
+ console.warn("Issuing country does not match the disclosed issuing country in query result")
1022
+ isCorrect = false
1023
+ queryResultErrors.issuing_country.disclose = {
1024
+ expected: `${queryResult.issuing_country.disclose.result}`,
1025
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
1026
+ message: "Issuing country does not match the disclosed issuing country in query result",
1125
1027
  }
1126
- if (queryResult.firstname) {
1127
- // If fullname was not revealed, then the name could be either the first name or last name
1128
- const firstnamePassport =
1129
- disclosedDataPassport.firstName && disclosedDataPassport.firstName.length > 0
1130
- ? disclosedDataPassport.firstName
1131
- : disclosedDataPassport.name
1132
- const firstnameIDCard =
1133
- disclosedDataIDCard.firstName && disclosedDataIDCard.firstName.length > 0
1134
- ? disclosedDataIDCard.firstName
1135
- : disclosedDataIDCard.name
1136
- if (
1137
- queryResult.firstname.eq &&
1138
- queryResult.firstname.eq.result &&
1139
- formatName(queryResult.firstname.eq.expected).toLowerCase() !==
1140
- firstnamePassport.toLowerCase() &&
1141
- formatName(queryResult.firstname.eq.expected).toLowerCase() !==
1142
- firstnameIDCard.toLowerCase()
1143
- ) {
1144
- console.warn("Firstname does not match the expected firstname")
1145
- isCorrect = false
1146
- queryResultErrors.firstname.eq = {
1147
- expected: `${queryResult.firstname.eq.expected}`,
1148
- received: `${firstnamePassport ?? firstnameIDCard}`,
1149
- message: "Firstname does not match the expected firstname",
1150
- }
1151
- }
1152
- if (
1153
- queryResult.firstname.disclose &&
1154
- formatName(queryResult.firstname.disclose.result).toLowerCase() !==
1155
- firstnamePassport.toLowerCase() &&
1156
- formatName(queryResult.firstname.disclose.result).toLowerCase() !==
1157
- firstnameIDCard.toLowerCase()
1158
- ) {
1159
- console.warn("Firstname does not match the disclosed firstname in query result")
1160
- isCorrect = false
1161
- queryResultErrors.firstname.disclose = {
1162
- expected: `${queryResult.firstname.disclose.result}`,
1163
- received: `${firstnamePassport ?? firstnameIDCard}`,
1164
- message: "Firstname does not match the disclosed firstname in query result",
1165
- }
1166
- }
1028
+ }
1029
+ }
1030
+ if (queryResult.fullname) {
1031
+ const fullnamePassport = disclosedDataPassport.name
1032
+ const fullnameIDCard = disclosedDataIDCard.name
1033
+ if (
1034
+ queryResult.fullname.eq &&
1035
+ queryResult.fullname.eq.result &&
1036
+ formatName(queryResult.fullname.eq.expected).toLowerCase() !==
1037
+ fullnamePassport.toLowerCase() &&
1038
+ formatName(queryResult.fullname.eq.expected).toLowerCase() !== fullnameIDCard.toLowerCase()
1039
+ ) {
1040
+ console.warn("Fullname does not match the expected fullname")
1041
+ isCorrect = false
1042
+ queryResultErrors.fullname.eq = {
1043
+ expected: `${queryResult.fullname.eq.expected}`,
1044
+ received: `${fullnamePassport ?? fullnameIDCard}`,
1045
+ message: "Fullname does not match the expected fullname",
1167
1046
  }
1168
- if (queryResult.lastname) {
1169
- // If fullname was not revealed, then the name could be either the first name or last name
1170
- const lastnamePassport =
1171
- disclosedDataPassport.lastName && disclosedDataPassport.lastName.length > 0
1172
- ? disclosedDataPassport.lastName
1173
- : disclosedDataPassport.name
1174
- const lastnameIDCard =
1175
- disclosedDataIDCard.lastName && disclosedDataIDCard.lastName.length > 0
1176
- ? disclosedDataIDCard.lastName
1177
- : disclosedDataIDCard.name
1178
- if (
1179
- queryResult.lastname.eq &&
1180
- queryResult.lastname.eq.result &&
1181
- formatName(queryResult.lastname.eq.expected).toLowerCase() !==
1182
- lastnamePassport.toLowerCase() &&
1183
- formatName(queryResult.lastname.eq.expected).toLowerCase() !==
1184
- lastnameIDCard.toLowerCase()
1185
- ) {
1186
- console.warn("Lastname does not match the expected lastname")
1187
- isCorrect = false
1188
- queryResultErrors.lastname.eq = {
1189
- expected: `${queryResult.lastname.eq.expected}`,
1190
- received: `${lastnamePassport ?? lastnameIDCard}`,
1191
- message: "Lastname does not match the expected lastname",
1192
- }
1193
- }
1194
- if (
1195
- queryResult.lastname.disclose &&
1196
- formatName(queryResult.lastname.disclose.result).toLowerCase() !==
1197
- lastnamePassport.toLowerCase() &&
1198
- formatName(queryResult.lastname.disclose.result).toLowerCase() !==
1199
- lastnameIDCard.toLowerCase()
1200
- ) {
1201
- console.warn("Lastname does not match the disclosed lastname in query result")
1202
- isCorrect = false
1203
- queryResultErrors.lastname.disclose = {
1204
- expected: `${queryResult.lastname.disclose.result}`,
1205
- received: `${lastnamePassport ?? lastnameIDCard}`,
1206
- message: "Lastname does not match the disclosed lastname in query result",
1207
- }
1208
- }
1047
+ }
1048
+ if (
1049
+ queryResult.fullname.disclose &&
1050
+ formatName(queryResult.fullname.disclose.result).toLowerCase() !==
1051
+ fullnamePassport.toLowerCase() &&
1052
+ formatName(queryResult.fullname.disclose.result).toLowerCase() !==
1053
+ fullnameIDCard.toLowerCase()
1054
+ ) {
1055
+ console.warn("Fullname does not match the disclosed fullname in query result")
1056
+ isCorrect = false
1057
+ queryResultErrors.fullname.disclose = {
1058
+ expected: `${queryResult.fullname.disclose.result}`,
1059
+ received: `${fullnamePassport ?? fullnameIDCard}`,
1060
+ message: "Fullname does not match the disclosed fullname in query result",
1209
1061
  }
1210
- uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1211
- } else if (proof.name === "compare_age") {
1212
- commitmentIn = getCommitmentInFromDisclosureProof(proofData)
1213
- if (commitmentIn !== commitmentOut) {
1214
- console.warn(
1215
- "Failed to check the link between the validity of the ID and the age derived from it",
1216
- )
1217
- isCorrect = false
1218
- queryResultErrors.age.commitment = {
1219
- expected: `Commitment: ${commitmentOut}`,
1220
- received: `Commitment: ${commitmentIn}`,
1221
- message:
1222
- "Failed to check the link between the validity of the ID and the age derived from it",
1223
- }
1062
+ }
1063
+ }
1064
+ if (queryResult.firstname) {
1065
+ // If fullname was not revealed, then the name could be either the first name or last name
1066
+ const firstnamePassport =
1067
+ disclosedDataPassport.firstName && disclosedDataPassport.firstName.length > 0
1068
+ ? disclosedDataPassport.firstName
1069
+ : disclosedDataPassport.name
1070
+ const firstnameIDCard =
1071
+ disclosedDataIDCard.firstName && disclosedDataIDCard.firstName.length > 0
1072
+ ? disclosedDataIDCard.firstName
1073
+ : disclosedDataIDCard.name
1074
+ if (
1075
+ queryResult.firstname.eq &&
1076
+ queryResult.firstname.eq.result &&
1077
+ formatName(queryResult.firstname.eq.expected).toLowerCase() !==
1078
+ firstnamePassport.toLowerCase() &&
1079
+ formatName(queryResult.firstname.eq.expected).toLowerCase() !==
1080
+ firstnameIDCard.toLowerCase()
1081
+ ) {
1082
+ console.warn("Firstname does not match the expected firstname")
1083
+ isCorrect = false
1084
+ queryResultErrors.firstname.eq = {
1085
+ expected: `${queryResult.firstname.eq.expected}`,
1086
+ received: `${firstnamePassport ?? firstnameIDCard}`,
1087
+ message: "Firstname does not match the expected firstname",
1224
1088
  }
1225
- const minAge = getMinAgeFromProof(proofData)
1226
- const maxAge = getMaxAgeFromProof(proofData)
1227
- if (queryResult.age) {
1228
- if (
1229
- queryResult.age.gte &&
1230
- queryResult.age.gte.result &&
1231
- minAge < (queryResult.age.gte.expected as number)
1232
- ) {
1233
- console.warn("Age is not greater than or equal to the expected age")
1234
- isCorrect = false
1235
- queryResultErrors.age.gte = {
1236
- expected: queryResult.age.gte.expected,
1237
- received: minAge,
1238
- message: "Age is not greater than or equal to the expected age",
1239
- }
1240
- }
1241
- if (
1242
- queryResult.age.lt &&
1243
- queryResult.age.lt.result &&
1244
- maxAge >= (queryResult.age.lt.expected as number)
1245
- ) {
1246
- console.warn("Age is not less than the expected age")
1247
- isCorrect = false
1248
- queryResultErrors.age.lt = {
1249
- expected: queryResult.age.lt.expected,
1250
- received: maxAge,
1251
- message: "Age is not less than the expected age",
1089
+ }
1090
+ if (
1091
+ queryResult.firstname.disclose &&
1092
+ formatName(queryResult.firstname.disclose.result).toLowerCase() !==
1093
+ firstnamePassport.toLowerCase() &&
1094
+ formatName(queryResult.firstname.disclose.result).toLowerCase() !==
1095
+ firstnameIDCard.toLowerCase()
1096
+ ) {
1097
+ console.warn("Firstname does not match the disclosed firstname in query result")
1098
+ isCorrect = false
1099
+ queryResultErrors.firstname.disclose = {
1100
+ expected: `${queryResult.firstname.disclose.result}`,
1101
+ received: `${firstnamePassport ?? firstnameIDCard}`,
1102
+ message: "Firstname does not match the disclosed firstname in query result",
1103
+ }
1104
+ }
1105
+ }
1106
+ if (queryResult.lastname) {
1107
+ // If fullname was not revealed, then the name could be either the first name or last name
1108
+ const lastnamePassport =
1109
+ disclosedDataPassport.lastName && disclosedDataPassport.lastName.length > 0
1110
+ ? disclosedDataPassport.lastName
1111
+ : disclosedDataPassport.name
1112
+ const lastnameIDCard =
1113
+ disclosedDataIDCard.lastName && disclosedDataIDCard.lastName.length > 0
1114
+ ? disclosedDataIDCard.lastName
1115
+ : disclosedDataIDCard.name
1116
+ if (
1117
+ queryResult.lastname.eq &&
1118
+ queryResult.lastname.eq.result &&
1119
+ formatName(queryResult.lastname.eq.expected).toLowerCase() !==
1120
+ lastnamePassport.toLowerCase() &&
1121
+ formatName(queryResult.lastname.eq.expected).toLowerCase() !== lastnameIDCard.toLowerCase()
1122
+ ) {
1123
+ console.warn("Lastname does not match the expected lastname")
1124
+ isCorrect = false
1125
+ queryResultErrors.lastname.eq = {
1126
+ expected: `${queryResult.lastname.eq.expected}`,
1127
+ received: `${lastnamePassport ?? lastnameIDCard}`,
1128
+ message: "Lastname does not match the expected lastname",
1129
+ }
1130
+ }
1131
+ if (
1132
+ queryResult.lastname.disclose &&
1133
+ formatName(queryResult.lastname.disclose.result).toLowerCase() !==
1134
+ lastnamePassport.toLowerCase() &&
1135
+ formatName(queryResult.lastname.disclose.result).toLowerCase() !==
1136
+ lastnameIDCard.toLowerCase()
1137
+ ) {
1138
+ console.warn("Lastname does not match the disclosed lastname in query result")
1139
+ isCorrect = false
1140
+ queryResultErrors.lastname.disclose = {
1141
+ expected: `${queryResult.lastname.disclose.result}`,
1142
+ received: `${lastnamePassport ?? lastnameIDCard}`,
1143
+ message: "Lastname does not match the disclosed lastname in query result",
1144
+ }
1145
+ }
1146
+ }
1147
+ return { isCorrect, queryResultErrors }
1148
+ }
1149
+
1150
+ private checkAgePublicInputs(proof: ProofResult, queryResult: QueryResult) {
1151
+ const queryResultErrors: QueryResultErrors = {
1152
+ sig_check_dsc: {},
1153
+ sig_check_id_data: {},
1154
+ data_check_integrity: {},
1155
+ disclose: {},
1156
+ age: {},
1157
+ birthdate: {},
1158
+ expiry_date: {},
1159
+ document_type: {},
1160
+ issuing_country: {},
1161
+ gender: {},
1162
+ nationality: {},
1163
+ firstname: {},
1164
+ lastname: {},
1165
+ fullname: {},
1166
+ document_number: {},
1167
+ outer: {},
1168
+ }
1169
+ let isCorrect = true
1170
+ const currentTime = new Date()
1171
+ const today = new Date(
1172
+ currentTime.getFullYear(),
1173
+ currentTime.getMonth(),
1174
+ currentTime.getDate(),
1175
+ 0,
1176
+ 0,
1177
+ 0,
1178
+ 0,
1179
+ )
1180
+ const minAge = getMinAgeFromCommittedInputs(
1181
+ proof.committedInputs?.compare_age as AgeCommittedInputs,
1182
+ )
1183
+ const maxAge = getMaxAgeFromCommittedInputs(
1184
+ proof.committedInputs?.compare_age as AgeCommittedInputs,
1185
+ )
1186
+ if (queryResult.age) {
1187
+ if (
1188
+ queryResult.age.gte &&
1189
+ queryResult.age.gte.result &&
1190
+ minAge < (queryResult.age.gte.expected as number)
1191
+ ) {
1192
+ console.warn("Age is not greater than or equal to the expected age")
1193
+ isCorrect = false
1194
+ queryResultErrors.age.gte = {
1195
+ expected: queryResult.age.gte.expected,
1196
+ received: minAge,
1197
+ message: "Age is not greater than or equal to the expected age",
1198
+ }
1199
+ }
1200
+ if (
1201
+ queryResult.age.lt &&
1202
+ queryResult.age.lt.result &&
1203
+ maxAge >= (queryResult.age.lt.expected as number)
1204
+ ) {
1205
+ console.warn("Age is not less than the expected age")
1206
+ isCorrect = false
1207
+ queryResultErrors.age.lt = {
1208
+ expected: queryResult.age.lt.expected,
1209
+ received: maxAge,
1210
+ message: "Age is not less than the expected age",
1211
+ }
1212
+ }
1213
+ if (queryResult.age.range) {
1214
+ if (
1215
+ queryResult.age.range.result &&
1216
+ (minAge < (queryResult.age.range.expected[0] as number) ||
1217
+ maxAge >= (queryResult.age.range.expected[1] as number))
1218
+ ) {
1219
+ console.warn("Age is not in the expected range")
1220
+ isCorrect = false
1221
+ queryResultErrors.age.range = {
1222
+ expected: queryResult.age.range.expected,
1223
+ received: [minAge, maxAge],
1224
+ message: "Age is not in the expected range",
1225
+ }
1226
+ }
1227
+ }
1228
+ if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
1229
+ console.warn("Maximum age should be equal to 0")
1230
+ isCorrect = false
1231
+ queryResultErrors.age.disclose = {
1232
+ expected: 0,
1233
+ received: maxAge,
1234
+ message: "Maximum age should be equal to 0",
1235
+ }
1236
+ }
1237
+ if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
1238
+ console.warn("Minimum age should be equal to 0")
1239
+ isCorrect = false
1240
+ queryResultErrors.age.disclose = {
1241
+ expected: 0,
1242
+ received: minAge,
1243
+ message: "Minimum age should be equal to 0",
1244
+ }
1245
+ }
1246
+ if (
1247
+ queryResult.age.disclose &&
1248
+ (queryResult.age.disclose.result !== minAge || queryResult.age.disclose.result !== maxAge)
1249
+ ) {
1250
+ console.warn("Age does not match the disclosed age in query result")
1251
+ isCorrect = false
1252
+ queryResultErrors.age.disclose = {
1253
+ expected: `${minAge}`,
1254
+ received: `${queryResult.age.disclose.result}`,
1255
+ message: "Age does not match the disclosed age in query result",
1256
+ }
1257
+ }
1258
+ } else {
1259
+ console.warn("Age is not set in the query result")
1260
+ isCorrect = false
1261
+ queryResultErrors.age.disclose = {
1262
+ message: "Age is not set in the query result",
1263
+ }
1264
+ }
1265
+ const currentDate = getCurrentDateFromCommittedInputs(
1266
+ proof.committedInputs?.compare_age as AgeCommittedInputs,
1267
+ )
1268
+ if (
1269
+ currentDate.getTime() !== today.getTime() &&
1270
+ currentDate.getTime() !== today.getTime() - 86400000
1271
+ ) {
1272
+ console.warn("Current date in the proof is too old")
1273
+ isCorrect = false
1274
+ queryResultErrors.age.disclose = {
1275
+ expected: `${today.toISOString()}`,
1276
+ received: `${currentDate.toISOString()}`,
1277
+ message: "Current date in the proof is too old",
1278
+ }
1279
+ }
1280
+ return { isCorrect, queryResultErrors }
1281
+ }
1282
+
1283
+ private checkBirthdatePublicInputs(proof: ProofResult, queryResult: QueryResult) {
1284
+ const queryResultErrors: 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
+ }
1302
+ let isCorrect = true
1303
+ const currentTime = new Date()
1304
+ const today = new Date(
1305
+ currentTime.getFullYear(),
1306
+ currentTime.getMonth(),
1307
+ currentTime.getDate(),
1308
+ 0,
1309
+ 0,
1310
+ 0,
1311
+ )
1312
+ const minDate = getMinDateFromCommittedInputs(
1313
+ proof.committedInputs?.compare_birthdate as DateCommittedInputs,
1314
+ )
1315
+ const maxDate = getMaxDateFromCommittedInputs(
1316
+ proof.committedInputs?.compare_birthdate as DateCommittedInputs,
1317
+ )
1318
+ const currentDate = getCurrentDateFromCommittedInputs(
1319
+ proof.committedInputs?.compare_birthdate as DateCommittedInputs,
1320
+ )
1321
+ if (queryResult.birthdate) {
1322
+ if (
1323
+ queryResult.birthdate.gte &&
1324
+ queryResult.birthdate.gte.result &&
1325
+ minDate < queryResult.birthdate.gte.expected
1326
+ ) {
1327
+ console.warn("Birthdate is not greater than or equal to the expected birthdate")
1328
+ isCorrect = false
1329
+ queryResultErrors.birthdate.gte = {
1330
+ expected: queryResult.birthdate.gte.expected,
1331
+ received: minDate,
1332
+ message: "Birthdate is not greater than or equal to the expected birthdate",
1333
+ }
1334
+ }
1335
+ if (
1336
+ queryResult.birthdate.lte &&
1337
+ queryResult.birthdate.lte.result &&
1338
+ maxDate > queryResult.birthdate.lte.expected
1339
+ ) {
1340
+ console.warn("Birthdate is not less than the expected birthdate")
1341
+ isCorrect = false
1342
+ queryResultErrors.birthdate.lte = {
1343
+ expected: queryResult.birthdate.lte.expected,
1344
+ received: maxDate,
1345
+ message: "Birthdate is not less than the expected birthdate",
1346
+ }
1347
+ }
1348
+ if (queryResult.birthdate.range) {
1349
+ if (
1350
+ queryResult.birthdate.range.result &&
1351
+ (minDate < queryResult.birthdate.range.expected[0] ||
1352
+ maxDate > queryResult.birthdate.range.expected[1])
1353
+ ) {
1354
+ console.warn("Birthdate is not in the expected range")
1355
+ isCorrect = false
1356
+ queryResultErrors.birthdate.range = {
1357
+ expected: queryResult.birthdate.range.expected,
1358
+ received: [minDate, maxDate],
1359
+ message: "Birthdate is not in the expected range",
1360
+ }
1361
+ }
1362
+ }
1363
+ if (
1364
+ !queryResult.birthdate.lte &&
1365
+ !queryResult.birthdate.range &&
1366
+ maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()
1367
+ ) {
1368
+ console.warn("Maximum birthdate should be equal to default date value")
1369
+ isCorrect = false
1370
+ queryResultErrors.birthdate.disclose = {
1371
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1372
+ received: `${maxDate.toISOString()}`,
1373
+ message: "Maximum birthdate should be equal to default date value",
1374
+ }
1375
+ }
1376
+ if (
1377
+ !queryResult.birthdate.gte &&
1378
+ !queryResult.birthdate.range &&
1379
+ minDate.getTime() != DEFAULT_DATE_VALUE.getTime()
1380
+ ) {
1381
+ console.warn("Minimum birthdate should be equal to default date value")
1382
+ isCorrect = false
1383
+ queryResultErrors.birthdate.disclose = {
1384
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1385
+ received: `${minDate.toISOString()}`,
1386
+ message: "Minimum birthdate should be equal to default date value",
1387
+ }
1388
+ }
1389
+ } else {
1390
+ console.warn("Birthdate is not set in the query result")
1391
+ isCorrect = false
1392
+ queryResultErrors.birthdate.disclose = {
1393
+ message: "Birthdate is not set in the query result",
1394
+ }
1395
+ }
1396
+ if (
1397
+ currentDate.getTime() !== today.getTime() &&
1398
+ currentDate.getTime() !== today.getTime() - 86400000
1399
+ ) {
1400
+ console.warn("Current date in the proof is too old")
1401
+ isCorrect = false
1402
+ queryResultErrors.age.disclose = {
1403
+ expected: `${today.toISOString()}`,
1404
+ received: `${currentDate.toISOString()}`,
1405
+ message: "Current date in the proof is too old",
1406
+ }
1407
+ }
1408
+ return { isCorrect, queryResultErrors }
1409
+ }
1410
+
1411
+ private checkExpiryDatePublicInputs(proof: ProofResult, queryResult: QueryResult) {
1412
+ const queryResultErrors: QueryResultErrors = {
1413
+ sig_check_dsc: {},
1414
+ sig_check_id_data: {},
1415
+ data_check_integrity: {},
1416
+ disclose: {},
1417
+ age: {},
1418
+ birthdate: {},
1419
+ expiry_date: {},
1420
+ document_type: {},
1421
+ issuing_country: {},
1422
+ gender: {},
1423
+ nationality: {},
1424
+ firstname: {},
1425
+ lastname: {},
1426
+ fullname: {},
1427
+ document_number: {},
1428
+ outer: {},
1429
+ }
1430
+ let isCorrect = true
1431
+ const currentTime = new Date()
1432
+ const today = new Date(
1433
+ currentTime.getFullYear(),
1434
+ currentTime.getMonth(),
1435
+ currentTime.getDate(),
1436
+ 0,
1437
+ 0,
1438
+ 0,
1439
+ )
1440
+ const minDate = getMinDateFromCommittedInputs(
1441
+ proof.committedInputs?.compare_expiry as DateCommittedInputs,
1442
+ )
1443
+ const maxDate = getMaxDateFromCommittedInputs(
1444
+ proof.committedInputs?.compare_expiry as DateCommittedInputs,
1445
+ )
1446
+ const currentDate = getCurrentDateFromCommittedInputs(
1447
+ proof.committedInputs?.compare_expiry as DateCommittedInputs,
1448
+ )
1449
+ if (queryResult.expiry_date) {
1450
+ if (
1451
+ queryResult.expiry_date.gte &&
1452
+ queryResult.expiry_date.gte.result &&
1453
+ minDate < queryResult.expiry_date.gte.expected
1454
+ ) {
1455
+ console.warn("Expiry date is not greater than or equal to the expected expiry date")
1456
+ isCorrect = false
1457
+ queryResultErrors.expiry_date.gte = {
1458
+ expected: queryResult.expiry_date.gte.expected,
1459
+ received: minDate,
1460
+ message: "Expiry date is not greater than or equal to the expected expiry date",
1461
+ }
1462
+ }
1463
+ if (
1464
+ queryResult.expiry_date.lte &&
1465
+ queryResult.expiry_date.lte.result &&
1466
+ maxDate > queryResult.expiry_date.lte.expected
1467
+ ) {
1468
+ console.warn("Expiry date is not less than the expected expiry date")
1469
+ isCorrect = false
1470
+ queryResultErrors.expiry_date.lte = {
1471
+ expected: queryResult.expiry_date.lte.expected,
1472
+ received: maxDate,
1473
+ message: "Expiry date is not less than the expected expiry date",
1474
+ }
1475
+ }
1476
+ if (queryResult.expiry_date.range) {
1477
+ if (
1478
+ queryResult.expiry_date.range.result &&
1479
+ (minDate < queryResult.expiry_date.range.expected[0] ||
1480
+ maxDate > queryResult.expiry_date.range.expected[1])
1481
+ ) {
1482
+ console.warn("Expiry date is not in the expected range")
1483
+ isCorrect = false
1484
+ queryResultErrors.expiry_date.range = {
1485
+ expected: queryResult.expiry_date.range.expected,
1486
+ received: [minDate, maxDate],
1487
+ message: "Expiry date is not in the expected range",
1488
+ }
1489
+ }
1490
+ }
1491
+ if (
1492
+ !queryResult.expiry_date.lte &&
1493
+ !queryResult.expiry_date.range &&
1494
+ maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()
1495
+ ) {
1496
+ console.warn("Maximum expiry date should be equal to default date value")
1497
+ isCorrect = false
1498
+ queryResultErrors.expiry_date.disclose = {
1499
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1500
+ received: `${maxDate.toISOString()}`,
1501
+ message: "Maximum expiry date should be equal to default date value",
1502
+ }
1503
+ }
1504
+ if (
1505
+ !queryResult.expiry_date.gte &&
1506
+ !queryResult.expiry_date.range &&
1507
+ minDate.getTime() != DEFAULT_DATE_VALUE.getTime()
1508
+ ) {
1509
+ console.warn("Minimum expiry date should be equal to default date value")
1510
+ isCorrect = false
1511
+ queryResultErrors.expiry_date.disclose = {
1512
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1513
+ received: `${minDate.toISOString()}`,
1514
+ message: "Minimum expiry date should be equal to default date value",
1515
+ }
1516
+ }
1517
+ } else {
1518
+ console.warn("Expiry date is not set in the query result")
1519
+ isCorrect = false
1520
+ queryResultErrors.expiry_date.disclose = {
1521
+ message: "Expiry date is not set in the query result",
1522
+ }
1523
+ }
1524
+ if (
1525
+ currentDate.getTime() !== today.getTime() &&
1526
+ currentDate.getTime() !== today.getTime() - 86400000
1527
+ ) {
1528
+ console.warn("Current date in the proof is too old")
1529
+ isCorrect = false
1530
+ queryResultErrors.age.disclose = {
1531
+ expected: `${today.toISOString()}`,
1532
+ received: `${currentDate.toISOString()}`,
1533
+ message: "Current date in the proof is too old",
1534
+ }
1535
+ }
1536
+ return { isCorrect, queryResultErrors }
1537
+ }
1538
+
1539
+ private checkNationalityExclusionPublicInputs(queryResult: QueryResult, countryList: string[]) {
1540
+ const queryResultErrors: QueryResultErrors = {
1541
+ sig_check_dsc: {},
1542
+ sig_check_id_data: {},
1543
+ data_check_integrity: {},
1544
+ disclose: {},
1545
+ age: {},
1546
+ birthdate: {},
1547
+ expiry_date: {},
1548
+ document_type: {},
1549
+ issuing_country: {},
1550
+ gender: {},
1551
+ nationality: {},
1552
+ firstname: {},
1553
+ lastname: {},
1554
+ fullname: {},
1555
+ document_number: {},
1556
+ outer: {},
1557
+ }
1558
+ let isCorrect = true
1559
+ if (
1560
+ queryResult.nationality &&
1561
+ queryResult.nationality.out &&
1562
+ queryResult.nationality.out.result
1563
+ ) {
1564
+ if (
1565
+ !queryResult.nationality.out.expected?.every((country) => countryList.includes(country))
1566
+ ) {
1567
+ console.warn("Nationality exclusion list does not match the one from the query results")
1568
+ isCorrect = false
1569
+ queryResultErrors.nationality.out = {
1570
+ expected: queryResult.nationality.out.expected,
1571
+ received: countryList,
1572
+ message: "Nationality exclusion list does not match the one from the query results",
1573
+ }
1574
+ }
1575
+ } else if (!queryResult.nationality || !queryResult.nationality.out) {
1576
+ console.warn("Nationality exclusion is not set in the query result")
1577
+ isCorrect = false
1578
+ queryResultErrors.nationality.out = {
1579
+ message: "Nationality exclusion is not set in the query result",
1580
+ }
1581
+ }
1582
+ // Check the countryList is in ascending order
1583
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1584
+ // as it is requirement in the circuit for the exclusion check to work
1585
+ for (let i = 1; i < countryList.length; i++) {
1586
+ if (countryList[i] < countryList[i - 1]) {
1587
+ console.warn(
1588
+ "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1589
+ )
1590
+ isCorrect = false
1591
+ queryResultErrors.nationality.out = {
1592
+ message:
1593
+ "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1594
+ }
1595
+ }
1596
+ }
1597
+ return { isCorrect, queryResultErrors }
1598
+ }
1599
+
1600
+ private checkIssuingCountryExclusionPublicInputs(
1601
+ queryResult: QueryResult,
1602
+ countryList: string[],
1603
+ ) {
1604
+ const queryResultErrors: QueryResultErrors = {
1605
+ sig_check_dsc: {},
1606
+ sig_check_id_data: {},
1607
+ data_check_integrity: {},
1608
+ disclose: {},
1609
+ age: {},
1610
+ birthdate: {},
1611
+ expiry_date: {},
1612
+ document_type: {},
1613
+ issuing_country: {},
1614
+ gender: {},
1615
+ nationality: {},
1616
+ firstname: {},
1617
+ lastname: {},
1618
+ fullname: {},
1619
+ document_number: {},
1620
+ outer: {},
1621
+ }
1622
+ let isCorrect = true
1623
+
1624
+ if (
1625
+ queryResult.issuing_country &&
1626
+ queryResult.issuing_country.out &&
1627
+ queryResult.issuing_country.out.result
1628
+ ) {
1629
+ if (
1630
+ !queryResult.issuing_country.out.expected?.every((country) => countryList.includes(country))
1631
+ ) {
1632
+ console.warn("Issuing country exclusion list does not match the one from the query results")
1633
+ isCorrect = false
1634
+ queryResultErrors.issuing_country.out = {
1635
+ expected: queryResult.issuing_country.out.expected,
1636
+ received: countryList,
1637
+ message: "Issuing country exclusion list does not match the one from the query results",
1638
+ }
1639
+ }
1640
+ } else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1641
+ console.warn("Issuing country exclusion is not set in the query result")
1642
+ isCorrect = false
1643
+ queryResultErrors.issuing_country.out = {
1644
+ message: "Issuing country exclusion is not set in the query result",
1645
+ }
1646
+ }
1647
+ // Check the countryList is in ascending order
1648
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1649
+ // as it is requirement in the circuit for the exclusion check to work
1650
+ for (let i = 1; i < countryList.length; i++) {
1651
+ if (countryList[i] < countryList[i - 1]) {
1652
+ console.warn(
1653
+ "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1654
+ )
1655
+ isCorrect = false
1656
+ queryResultErrors.issuing_country.out = {
1657
+ message:
1658
+ "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1659
+ }
1660
+ }
1661
+ }
1662
+ return { isCorrect, queryResultErrors }
1663
+ }
1664
+
1665
+ private checkNationalityInclusionPublicInputs(queryResult: QueryResult, countryList: string[]) {
1666
+ const queryResultErrors: QueryResultErrors = {
1667
+ sig_check_dsc: {},
1668
+ sig_check_id_data: {},
1669
+ data_check_integrity: {},
1670
+ disclose: {},
1671
+ age: {},
1672
+ birthdate: {},
1673
+ expiry_date: {},
1674
+ document_type: {},
1675
+ issuing_country: {},
1676
+ gender: {},
1677
+ nationality: {},
1678
+ firstname: {},
1679
+ lastname: {},
1680
+ fullname: {},
1681
+ document_number: {},
1682
+ outer: {},
1683
+ }
1684
+ let isCorrect = true
1685
+ if (
1686
+ queryResult.nationality &&
1687
+ queryResult.nationality.in &&
1688
+ queryResult.nationality.in.result
1689
+ ) {
1690
+ if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
1691
+ console.warn("Nationality inclusion list does not match the one from the query results")
1692
+ isCorrect = false
1693
+ queryResultErrors.nationality.in = {
1694
+ expected: queryResult.nationality.in.expected,
1695
+ received: countryList,
1696
+ message: "Nationality inclusion list does not match the one from the query results",
1697
+ }
1698
+ }
1699
+ } else if (!queryResult.nationality || !queryResult.nationality.in) {
1700
+ console.warn("Nationality inclusion is not set in the query result")
1701
+ isCorrect = false
1702
+ queryResultErrors.nationality.in = {
1703
+ message: "Nationality inclusion is not set in the query result",
1704
+ }
1705
+ }
1706
+ return { isCorrect, queryResultErrors }
1707
+ }
1708
+
1709
+ private checkIssuingCountryInclusionPublicInputs(
1710
+ queryResult: QueryResult,
1711
+ countryList: string[],
1712
+ ) {
1713
+ const queryResultErrors: QueryResultErrors = {
1714
+ sig_check_dsc: {},
1715
+ sig_check_id_data: {},
1716
+ data_check_integrity: {},
1717
+ disclose: {},
1718
+ age: {},
1719
+ birthdate: {},
1720
+ expiry_date: {},
1721
+ document_type: {},
1722
+ issuing_country: {},
1723
+ gender: {},
1724
+ nationality: {},
1725
+ firstname: {},
1726
+ lastname: {},
1727
+ fullname: {},
1728
+ document_number: {},
1729
+ outer: {},
1730
+ }
1731
+ let isCorrect = true
1732
+
1733
+ if (
1734
+ queryResult.issuing_country &&
1735
+ queryResult.issuing_country.in &&
1736
+ queryResult.issuing_country.in.result
1737
+ ) {
1738
+ if (
1739
+ !queryResult.issuing_country.in.expected?.every((country) => countryList.includes(country))
1740
+ ) {
1741
+ console.warn("Issuing country inclusion list does not match the one from the query results")
1742
+ isCorrect = false
1743
+ queryResultErrors.issuing_country.in = {
1744
+ expected: queryResult.issuing_country.in.expected,
1745
+ received: countryList,
1746
+ message: "Issuing country inclusion list does not match the one from the query results",
1747
+ }
1748
+ }
1749
+ } else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1750
+ console.warn("Issuing country inclusion is not set in the query result")
1751
+ isCorrect = false
1752
+ queryResultErrors.issuing_country.in = {
1753
+ message: "Issuing country inclusion is not set in the query result",
1754
+ }
1755
+ }
1756
+ return { isCorrect, queryResultErrors }
1757
+ }
1758
+
1759
+ private async checkPublicInputs(
1760
+ proofs: Array<ProofResult>,
1761
+ queryResult: QueryResult,
1762
+ validity?: number,
1763
+ ) {
1764
+ let commitmentIn: bigint | undefined
1765
+ let commitmentOut: bigint | undefined
1766
+ let isCorrect = true
1767
+ let uniqueIdentifier: string | undefined
1768
+ const VALID_CERTIFICATE_REGISTRY_ROOT = [
1769
+ BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
1770
+ BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
1771
+ BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
1772
+ ]
1773
+ const currentTime = new Date()
1774
+ const today = new Date(
1775
+ currentTime.getFullYear(),
1776
+ currentTime.getMonth(),
1777
+ currentTime.getDate(),
1778
+ 0,
1779
+ 0,
1780
+ 0,
1781
+ 0,
1782
+ )
1783
+ let queryResultErrors: QueryResultErrors = {
1784
+ sig_check_dsc: {},
1785
+ sig_check_id_data: {},
1786
+ data_check_integrity: {},
1787
+ disclose: {},
1788
+ age: {},
1789
+ birthdate: {},
1790
+ expiry_date: {},
1791
+ document_type: {},
1792
+ issuing_country: {},
1793
+ gender: {},
1794
+ nationality: {},
1795
+ firstname: {},
1796
+ lastname: {},
1797
+ fullname: {},
1798
+ document_number: {},
1799
+ outer: {},
1800
+ }
1801
+
1802
+ // Since the order is important for the commitments, we need to sort the proofs
1803
+ // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
1804
+ const sortedProofs = proofs.sort((a, b) => {
1805
+ const proofOrder = [
1806
+ "sig_check_dsc",
1807
+ "sig_check_id_data",
1808
+ "data_check_integrity",
1809
+ "disclose_bytes",
1810
+ "compare_age",
1811
+ "compare_birthdate",
1812
+ "compare_expiry",
1813
+ "exclusion_check_nationality",
1814
+ "inclusion_check_nationality",
1815
+ "exclusion_check_issuing_country",
1816
+ "inclusion_check_issuing_country",
1817
+ ]
1818
+ const getIndex = (proof: ProofResult) => {
1819
+ const name = proof.name || ""
1820
+ return proofOrder.findIndex((p) => name.startsWith(p))
1821
+ }
1822
+ return getIndex(a) - getIndex(b)
1823
+ })
1824
+
1825
+ for (const proof of sortedProofs!) {
1826
+ const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
1827
+ if (proof.name?.startsWith("outer")) {
1828
+ const isForEVM = proof.name?.startsWith("outer_evm")
1829
+ const certificateRegistryRoot = getCertificateRegistryRootFromOuterProof(proofData)
1830
+ if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(certificateRegistryRoot)) {
1831
+ console.warn("The ID was signed by an unrecognized root certificate")
1832
+ isCorrect = false
1833
+ queryResultErrors.outer.certificate = {
1834
+ expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
1835
+ received: `Certificate registry root: ${certificateRegistryRoot.toString()}`,
1836
+ message: "The ID was signed by an unrecognized root certificate",
1837
+ }
1838
+ }
1839
+ const currentDate = getCurrentDateFromOuterProof(proofData)
1840
+ const todayToCurrentDate = today.getTime() - currentDate.getTime()
1841
+ const differenceInDays = validity ?? 180
1842
+ const expectedDifference = differenceInDays * 86400000
1843
+ const actualDifference = today.getTime() - (today.getTime() - expectedDifference)
1844
+ // The ID should not expire within the next 6 months (or whatever the custom value is)
1845
+ if (todayToCurrentDate >= actualDifference) {
1846
+ console.warn(
1847
+ `The date used to check the validity of the ID is older than ${differenceInDays} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
1848
+ )
1849
+ isCorrect = false
1850
+ queryResultErrors.outer.date = {
1851
+ expected: `Difference: ${differenceInDays} days`,
1852
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
1853
+ message:
1854
+ "The date used to check the validity of the ID is older than the validity period",
1855
+ }
1856
+ }
1857
+ const paramCommitments = getParamCommitmentsFromOuterProof(proofData)
1858
+ const committedInputs = proof.committedInputs
1859
+ const keysInCommittedInputs = Object.keys(committedInputs || {})
1860
+ if (keysInCommittedInputs.length !== paramCommitments.length) {
1861
+ console.warn("The proof does not verify all the requested conditions and information")
1862
+ isCorrect = false
1863
+ queryResultErrors.outer.commitment = {
1864
+ expected: `Number of parameter commitments: ${paramCommitments.length}`,
1865
+ received: `Number of disclosure proofs provided: ${keysInCommittedInputs.length}`,
1866
+ message: "The proof does not verify all the requested conditions and information",
1867
+ }
1868
+ }
1869
+ if (!!committedInputs?.compare_age) {
1870
+ const ageCommittedInputs = committedInputs?.compare_age as AgeCommittedInputs
1871
+ const ageParameterCommitment = isForEVM
1872
+ ? await getAgeEVMParameterCommitment(
1873
+ ageCommittedInputs.currentDate,
1874
+ ageCommittedInputs.minAge,
1875
+ ageCommittedInputs.maxAge,
1876
+ )
1877
+ : await getAgeParameterCommitment(
1878
+ ageCommittedInputs.currentDate,
1879
+ ageCommittedInputs.minAge,
1880
+ ageCommittedInputs.maxAge,
1881
+ )
1882
+ if (!paramCommitments.includes(ageParameterCommitment)) {
1883
+ console.warn("This proof does not verify the age")
1884
+ isCorrect = false
1885
+ queryResultErrors.age.commitment = {
1886
+ expected: `Age parameter commitment: ${ageParameterCommitment.toString()}`,
1887
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1888
+ message: "This proof does not verify the age",
1252
1889
  }
1253
1890
  }
1254
- if (queryResult.age.range) {
1255
- if (
1256
- queryResult.age.range.result &&
1257
- (minAge < (queryResult.age.range.expected[0] as number) ||
1258
- maxAge >= (queryResult.age.range.expected[1] as number))
1259
- ) {
1260
- console.warn("Age is not in the expected range")
1261
- isCorrect = false
1262
- queryResultErrors.age.range = {
1263
- expected: queryResult.age.range.expected,
1264
- received: [minAge, maxAge],
1265
- message: "Age is not in the expected range",
1266
- }
1891
+ const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
1892
+ this.checkAgePublicInputs(proof, queryResult)
1893
+ isCorrect = isCorrect && isCorrectAge
1894
+ queryResultErrors = {
1895
+ ...queryResultErrors,
1896
+ ...queryResultErrorsAge,
1897
+ }
1898
+ } else if (!!committedInputs?.compare_birthdate) {
1899
+ const birthdateCommittedInputs = committedInputs?.compare_birthdate as DateCommittedInputs
1900
+ const birthdateParameterCommitment = isForEVM
1901
+ ? await getDateEVMParameterCommitment(
1902
+ ProofType.BIRTHDATE,
1903
+ birthdateCommittedInputs.currentDate,
1904
+ birthdateCommittedInputs.minDate,
1905
+ birthdateCommittedInputs.maxDate,
1906
+ )
1907
+ : await getDateParameterCommitment(
1908
+ ProofType.BIRTHDATE,
1909
+ birthdateCommittedInputs.currentDate,
1910
+ birthdateCommittedInputs.minDate,
1911
+ birthdateCommittedInputs.maxDate,
1912
+ )
1913
+ if (!paramCommitments.includes(birthdateParameterCommitment)) {
1914
+ console.warn("This proof does not verify the birthdate")
1915
+ isCorrect = false
1916
+ queryResultErrors.birthdate.commitment = {
1917
+ expected: `Birthdate parameter commitment: ${birthdateParameterCommitment.toString()}`,
1918
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1919
+ message: "This proof does not verify the birthdate",
1267
1920
  }
1268
1921
  }
1269
- if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
1270
- console.warn("Maximum age should be equal to 0")
1922
+ const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
1923
+ this.checkBirthdatePublicInputs(proof, queryResult)
1924
+ isCorrect = isCorrect && isCorrectBirthdate
1925
+ queryResultErrors = {
1926
+ ...queryResultErrors,
1927
+ ...queryResultErrorsBirthdate,
1928
+ }
1929
+ } else if (!!committedInputs?.compare_expiry) {
1930
+ const expiryCommittedInputs = committedInputs?.compare_expiry as DateCommittedInputs
1931
+ const expiryParameterCommitment = isForEVM
1932
+ ? await getDateEVMParameterCommitment(
1933
+ ProofType.EXPIRY_DATE,
1934
+ expiryCommittedInputs.currentDate,
1935
+ expiryCommittedInputs.minDate,
1936
+ expiryCommittedInputs.maxDate,
1937
+ )
1938
+ : await getDateParameterCommitment(
1939
+ ProofType.EXPIRY_DATE,
1940
+ expiryCommittedInputs.currentDate,
1941
+ expiryCommittedInputs.minDate,
1942
+ expiryCommittedInputs.maxDate,
1943
+ )
1944
+ if (!paramCommitments.includes(expiryParameterCommitment)) {
1945
+ console.warn("This proof does not verify the expiry date")
1271
1946
  isCorrect = false
1272
- queryResultErrors.age.disclose = {
1273
- expected: 0,
1274
- received: maxAge,
1275
- message: "Maximum age should be equal to 0",
1947
+ queryResultErrors.expiry_date.commitment = {
1948
+ expected: `Expiry date parameter commitment: ${expiryParameterCommitment.toString()}`,
1949
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1950
+ message: "This proof does not verify the expiry date",
1276
1951
  }
1277
1952
  }
1278
- if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
1279
- console.warn("Minimum age should be equal to 0")
1953
+ const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
1954
+ this.checkExpiryDatePublicInputs(proof, queryResult)
1955
+ isCorrect = isCorrect && isCorrectExpiryDate
1956
+ queryResultErrors = {
1957
+ ...queryResultErrors,
1958
+ ...queryResultErrorsExpiryDate,
1959
+ }
1960
+ } else if (!!committedInputs?.disclose_bytes) {
1961
+ const discloseCommittedInputs = committedInputs?.disclose_bytes as DiscloseCommittedInputs
1962
+ const discloseParameterCommitment = isForEVM
1963
+ ? await getDiscloseEVMParameterCommitment(
1964
+ discloseCommittedInputs.discloseMask,
1965
+ discloseCommittedInputs.disclosedBytes,
1966
+ )
1967
+ : await getDiscloseParameterCommitment(
1968
+ discloseCommittedInputs.discloseMask,
1969
+ discloseCommittedInputs.disclosedBytes,
1970
+ )
1971
+ if (!paramCommitments.includes(discloseParameterCommitment)) {
1972
+ console.warn("This proof does not verify any of the data disclosed")
1280
1973
  isCorrect = false
1281
- queryResultErrors.age.disclose = {
1282
- expected: 0,
1283
- received: minAge,
1284
- message: "Minimum age should be equal to 0",
1974
+ queryResultErrors.disclose.commitment = {
1975
+ expected: `Disclosure parameter commitment: ${discloseParameterCommitment.toString()}`,
1976
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1977
+ message: "This proof does not verify any of the data disclosed",
1285
1978
  }
1286
1979
  }
1287
- if (
1288
- queryResult.age.disclose &&
1289
- (queryResult.age.disclose.result !== minAge ||
1290
- queryResult.age.disclose.result !== maxAge)
1291
- ) {
1292
- console.warn("Age does not match the disclosed age in query result")
1980
+ const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
1981
+ this.checkDiscloseBytesPublicInputs(proof, queryResult)
1982
+ isCorrect = isCorrect && isCorrectDisclose
1983
+ queryResultErrors = {
1984
+ ...queryResultErrors,
1985
+ ...queryResultErrorsDisclose,
1986
+ }
1987
+ } else if (!!committedInputs?.inclusion_check_nationality) {
1988
+ const inclusionCheckNationalityCommittedInputs =
1989
+ committedInputs?.inclusion_check_nationality as CountryCommittedInputs
1990
+ const inclusionCheckNationalityParameterCommitment = isForEVM
1991
+ ? await getCountryEVMParameterCommitment(
1992
+ ProofType.NATIONALITY_INCLUSION,
1993
+ inclusionCheckNationalityCommittedInputs.countries,
1994
+ )
1995
+ : await getCountryParameterCommitment(
1996
+ ProofType.NATIONALITY_INCLUSION,
1997
+ inclusionCheckNationalityCommittedInputs.countries,
1998
+ )
1999
+ if (!paramCommitments.includes(inclusionCheckNationalityParameterCommitment)) {
2000
+ console.warn("This proof does not verify the inclusion of the nationality")
1293
2001
  isCorrect = false
1294
- queryResultErrors.age.disclose = {
1295
- expected: `${minAge}`,
1296
- received: `${queryResult.age.disclose.result}`,
1297
- message: "Age does not match the disclosed age in query result",
2002
+ queryResultErrors.nationality.commitment = {
2003
+ expected: `Nationality parameter commitment: ${inclusionCheckNationalityParameterCommitment.toString()}`,
2004
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2005
+ message: "This proof does not verify the inclusion of the nationality",
1298
2006
  }
1299
2007
  }
1300
- } else {
1301
- console.warn("Age is not set in the query result")
2008
+ const countryList = inclusionCheckNationalityCommittedInputs.countries
2009
+ const {
2010
+ isCorrect: isCorrectNationalityInclusion,
2011
+ queryResultErrors: queryResultErrorsNationalityInclusion,
2012
+ } = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
2013
+ isCorrect = isCorrect && isCorrectNationalityInclusion
2014
+ queryResultErrors = {
2015
+ ...queryResultErrors,
2016
+ ...queryResultErrorsNationalityInclusion,
2017
+ }
2018
+ } else if (!!committedInputs?.inclusion_check_issuing_country) {
2019
+ const inclusionCheckIssuingCountryCommittedInputs =
2020
+ committedInputs?.inclusion_check_issuing_country as CountryCommittedInputs
2021
+ const inclusionCheckIssuingCountryParameterCommitment = isForEVM
2022
+ ? await getCountryEVMParameterCommitment(
2023
+ ProofType.ISSUING_COUNTRY_INCLUSION,
2024
+ inclusionCheckIssuingCountryCommittedInputs.countries,
2025
+ )
2026
+ : await getCountryParameterCommitment(
2027
+ ProofType.ISSUING_COUNTRY_INCLUSION,
2028
+ inclusionCheckIssuingCountryCommittedInputs.countries,
2029
+ )
2030
+ if (!paramCommitments.includes(inclusionCheckIssuingCountryParameterCommitment)) {
2031
+ console.warn("This proof does not verify the inclusion of the issuing country")
2032
+ isCorrect = false
2033
+ queryResultErrors.issuing_country.commitment = {
2034
+ expected: `Issuing country parameter commitment: ${inclusionCheckIssuingCountryParameterCommitment.toString()}`,
2035
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2036
+ message: "This proof does not verify the inclusion of the issuing country",
2037
+ }
2038
+ }
2039
+ const countryList = inclusionCheckIssuingCountryCommittedInputs.countries
2040
+ const {
2041
+ isCorrect: isCorrectIssuingCountryInclusion,
2042
+ queryResultErrors: queryResultErrorsIssuingCountryInclusion,
2043
+ } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
2044
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion
2045
+ queryResultErrors = {
2046
+ ...queryResultErrors,
2047
+ ...queryResultErrorsIssuingCountryInclusion,
2048
+ }
2049
+ } else if (!!committedInputs?.exclusion_check_nationality) {
2050
+ const exclusionCheckNationalityCommittedInputs =
2051
+ committedInputs?.exclusion_check_nationality as CountryCommittedInputs
2052
+ const exclusionCheckNationalityParameterCommitment = isForEVM
2053
+ ? await getCountryEVMParameterCommitment(
2054
+ ProofType.NATIONALITY_EXCLUSION,
2055
+ exclusionCheckNationalityCommittedInputs.countries,
2056
+ )
2057
+ : await getCountryParameterCommitment(
2058
+ ProofType.NATIONALITY_EXCLUSION,
2059
+ exclusionCheckNationalityCommittedInputs.countries,
2060
+ )
2061
+ if (!paramCommitments.includes(exclusionCheckNationalityParameterCommitment)) {
2062
+ console.warn("This proof does not verify the exclusion of the nationality")
2063
+ isCorrect = false
2064
+ queryResultErrors.nationality.commitment = {
2065
+ expected: `Nationality parameter commitment: ${exclusionCheckNationalityParameterCommitment.toString()}`,
2066
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2067
+ message: "This proof does not verify the exclusion of the nationality",
2068
+ }
2069
+ }
2070
+ const countryList = exclusionCheckNationalityCommittedInputs.countries
2071
+ const {
2072
+ isCorrect: isCorrectNationalityExclusion,
2073
+ queryResultErrors: queryResultErrorsNationalityExclusion,
2074
+ } = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
2075
+ isCorrect = isCorrect && isCorrectNationalityExclusion
2076
+ queryResultErrors = {
2077
+ ...queryResultErrors,
2078
+ ...queryResultErrorsNationalityExclusion,
2079
+ }
2080
+ } else if (!!committedInputs?.exclusion_check_issuing_country) {
2081
+ const exclusionCheckIssuingCountryCommittedInputs =
2082
+ committedInputs?.exclusion_check_issuing_country as CountryCommittedInputs
2083
+ const exclusionCheckIssuingCountryParameterCommitment = isForEVM
2084
+ ? await getCountryEVMParameterCommitment(
2085
+ ProofType.ISSUING_COUNTRY_EXCLUSION,
2086
+ exclusionCheckIssuingCountryCommittedInputs.countries,
2087
+ )
2088
+ : await getCountryParameterCommitment(
2089
+ ProofType.ISSUING_COUNTRY_EXCLUSION,
2090
+ exclusionCheckIssuingCountryCommittedInputs.countries,
2091
+ )
2092
+ if (!paramCommitments.includes(exclusionCheckIssuingCountryParameterCommitment)) {
2093
+ console.warn("This proof does not verify the exclusion of the issuing country")
2094
+ isCorrect = false
2095
+ queryResultErrors.issuing_country.commitment = {
2096
+ expected: `Issuing country parameter commitment: ${exclusionCheckIssuingCountryParameterCommitment.toString()}`,
2097
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
2098
+ message: "This proof does not verify the exclusion of the issuing country",
2099
+ }
2100
+ }
2101
+ const countryList = exclusionCheckIssuingCountryCommittedInputs.countries
2102
+ const {
2103
+ isCorrect: isCorrectIssuingCountryExclusion,
2104
+ queryResultErrors: queryResultErrorsIssuingCountryExclusion,
2105
+ } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
2106
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion
2107
+ queryResultErrors = {
2108
+ ...queryResultErrors,
2109
+ ...queryResultErrorsIssuingCountryExclusion,
2110
+ }
2111
+ }
2112
+ uniqueIdentifier = getNullifierFromOuterProof(proofData).toString(10)
2113
+ } else if (proof.name?.startsWith("sig_check_dsc")) {
2114
+ commitmentOut = getCommitmentFromDSCProof(proofData)
2115
+ const merkleRoot = getMerkleRootFromDSCProof(proofData)
2116
+ if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(merkleRoot)) {
2117
+ console.warn("The ID was signed by an unrecognized root certificate")
1302
2118
  isCorrect = false
1303
- queryResultErrors.age.disclose = {
1304
- message: "Age is not set in the query result",
2119
+ queryResultErrors.sig_check_dsc.certificate = {
2120
+ expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
2121
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
2122
+ message: "The ID was signed by an unrecognized root certificate",
1305
2123
  }
1306
2124
  }
1307
- const currentDate = getCurrentDateFromAgeProof(proofData)
1308
- if (
1309
- currentDate.getTime() !== today.getTime() &&
1310
- currentDate.getTime() !== today.getTime() - 86400000
1311
- ) {
1312
- console.warn("Current date in the proof is too old")
2125
+ } else if (proof.name?.startsWith("sig_check_id_data")) {
2126
+ commitmentIn = getCommitmentInFromIDDataProof(proofData)
2127
+ if (commitmentIn !== commitmentOut) {
2128
+ console.warn(
2129
+ "Failed to check the link between the certificate signature and ID signature",
2130
+ )
2131
+ isCorrect = false
2132
+ queryResultErrors.sig_check_id_data.commitment = {
2133
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
2134
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
2135
+ message: "Failed to check the link between the certificate signature and ID signature",
2136
+ }
2137
+ }
2138
+ commitmentOut = getCommitmentOutFromIDDataProof(proofData)
2139
+ } else if (proof.name?.startsWith("data_check_integrity")) {
2140
+ commitmentIn = getCommitmentInFromIntegrityProof(proofData)
2141
+ if (commitmentIn !== commitmentOut) {
2142
+ console.warn("Failed to check the link between the ID signature and the data signed")
2143
+ isCorrect = false
2144
+ queryResultErrors.data_check_integrity.commitment = {
2145
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
2146
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
2147
+ message: "Failed to check the link between the ID signature and the data signed",
2148
+ }
2149
+ }
2150
+ commitmentOut = getCommitmentOutFromIntegrityProof(proofData)
2151
+ const currentDate = getCurrentDateFromIntegrityProof(proofData)
2152
+ const todayToCurrentDate = today.getTime() - currentDate.getTime()
2153
+ const differenceInDays = validity ?? 180
2154
+ const expectedDifference = differenceInDays * 86400000
2155
+ const actualDifference = today.getTime() - (today.getTime() - expectedDifference)
2156
+ // The ID should not expire within the next 6 months (or whatever the custom value is)
2157
+ if (todayToCurrentDate >= actualDifference) {
2158
+ console.warn(
2159
+ `The date used to check the validity of the ID is older than ${differenceInDays} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
2160
+ )
2161
+ isCorrect = false
2162
+ queryResultErrors.data_check_integrity.date = {
2163
+ expected: `Difference: ${differenceInDays} days`,
2164
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
2165
+ message:
2166
+ "The date used to check the validity of the ID is older than the validity period",
2167
+ }
2168
+ }
2169
+ } else if (proof.name === "disclose_bytes") {
2170
+ commitmentIn = getCommitmentInFromDisclosureProof(proofData)
2171
+ if (commitmentIn !== commitmentOut) {
2172
+ console.warn(
2173
+ "Failed to check the link between the validity of the ID and the data to disclose",
2174
+ )
2175
+ isCorrect = false
2176
+ queryResultErrors.disclose.commitment = {
2177
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
2178
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
2179
+ message:
2180
+ "Failed to check the link between the validity of the ID and the data to disclose",
2181
+ }
2182
+ }
2183
+ const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
2184
+ const calculatedParamCommitment = await getDiscloseParameterCommitment(
2185
+ (proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).discloseMask!,
2186
+ (proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
2187
+ )
2188
+ if (paramCommitment !== calculatedParamCommitment) {
2189
+ console.warn("The disclosed data does not match the data committed by the proof")
2190
+ isCorrect = false
2191
+ queryResultErrors.disclose.commitment = {
2192
+ expected: `Commitment: ${calculatedParamCommitment}`,
2193
+ received: `Commitment: ${paramCommitment}`,
2194
+ message: "The disclosed data does not match the data committed by the proof",
2195
+ }
2196
+ }
2197
+ const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
2198
+ this.checkDiscloseBytesPublicInputs(proof, queryResult)
2199
+ isCorrect = isCorrect && isCorrectDisclose
2200
+ queryResultErrors = {
2201
+ ...queryResultErrors,
2202
+ ...queryResultErrorsDisclose,
2203
+ }
2204
+ uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
2205
+ } else if (proof.name === "compare_age") {
2206
+ commitmentIn = getCommitmentInFromDisclosureProof(proofData)
2207
+ if (commitmentIn !== commitmentOut) {
2208
+ console.warn(
2209
+ "Failed to check the link between the validity of the ID and the age derived from it",
2210
+ )
2211
+ isCorrect = false
2212
+ queryResultErrors.age.commitment = {
2213
+ expected: `Commitment: ${commitmentOut}`,
2214
+ received: `Commitment: ${commitmentIn}`,
2215
+ message:
2216
+ "Failed to check the link between the validity of the ID and the age derived from it",
2217
+ }
2218
+ }
2219
+ const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
2220
+ const committedInputs = proof.committedInputs?.compare_age as AgeCommittedInputs
2221
+ const calculatedParamCommitment = await getAgeParameterCommitment(
2222
+ committedInputs.currentDate,
2223
+ committedInputs.minAge,
2224
+ committedInputs.maxAge,
2225
+ )
2226
+ if (paramCommitment !== calculatedParamCommitment) {
2227
+ console.warn(
2228
+ "The conditions for the age check do not match the conditions checked by the proof",
2229
+ )
1313
2230
  isCorrect = false
1314
- queryResultErrors.age.disclose = {
1315
- expected: `${today.toISOString()}`,
1316
- received: `${currentDate.toISOString()}`,
1317
- message: "Current date in the proof is too old",
2231
+ queryResultErrors.age.commitment = {
2232
+ expected: `Commitment: ${calculatedParamCommitment}`,
2233
+ received: `Commitment: ${paramCommitment}`,
2234
+ message:
2235
+ "The conditions for the age check do not match the conditions checked by the proof",
1318
2236
  }
1319
2237
  }
2238
+ const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
2239
+ this.checkAgePublicInputs(proof, queryResult)
2240
+ isCorrect = isCorrect && isCorrectAge
2241
+ queryResultErrors = {
2242
+ ...queryResultErrors,
2243
+ ...queryResultErrorsAge,
2244
+ }
1320
2245
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1321
2246
  } else if (proof.name === "compare_birthdate") {
1322
2247
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -1332,83 +2257,33 @@ export class ZKPassport {
1332
2257
  "Failed to check the link between the validity of the ID and the birthdate derived from it",
1333
2258
  }
1334
2259
  }
1335
- const minDate = getMinDateFromProof(proofData)
1336
- const maxDate = getMaxDateFromProof(proofData)
1337
- if (queryResult.birthdate) {
1338
- if (
1339
- queryResult.birthdate.gte &&
1340
- queryResult.birthdate.gte.result &&
1341
- minDate < queryResult.birthdate.gte.expected
1342
- ) {
1343
- console.warn("Birthdate is not greater than or equal to the expected birthdate")
1344
- isCorrect = false
1345
- queryResultErrors.birthdate.gte = {
1346
- expected: queryResult.birthdate.gte.expected,
1347
- received: minDate,
1348
- message: "Birthdate is not greater than or equal to the expected birthdate",
1349
- }
1350
- }
1351
- if (
1352
- queryResult.birthdate.lte &&
1353
- queryResult.birthdate.lte.result &&
1354
- maxDate > queryResult.birthdate.lte.expected
1355
- ) {
1356
- console.warn("Birthdate is not less than the expected birthdate")
1357
- isCorrect = false
1358
- queryResultErrors.birthdate.lte = {
1359
- expected: queryResult.birthdate.lte.expected,
1360
- received: maxDate,
1361
- message: "Birthdate is not less than the expected birthdate",
1362
- }
1363
- }
1364
- if (queryResult.birthdate.range) {
1365
- if (
1366
- queryResult.birthdate.range.result &&
1367
- (minDate < queryResult.birthdate.range.expected[0] ||
1368
- maxDate > queryResult.birthdate.range.expected[1])
1369
- ) {
1370
- console.warn("Birthdate is not in the expected range")
1371
- isCorrect = false
1372
- queryResultErrors.birthdate.range = {
1373
- expected: queryResult.birthdate.range.expected,
1374
- received: [minDate, maxDate],
1375
- message: "Birthdate is not in the expected range",
1376
- }
1377
- }
1378
- }
1379
- if (
1380
- !queryResult.birthdate.lte &&
1381
- !queryResult.birthdate.range &&
1382
- maxDate.getTime() != defaultDateValue.getTime()
1383
- ) {
1384
- console.warn("Maximum birthdate should be equal to default date value")
1385
- isCorrect = false
1386
- queryResultErrors.birthdate.disclose = {
1387
- expected: `${defaultDateValue.toISOString()}`,
1388
- received: `${maxDate.toISOString()}`,
1389
- message: "Maximum birthdate should be equal to default date value",
1390
- }
1391
- }
1392
- if (
1393
- !queryResult.birthdate.gte &&
1394
- !queryResult.birthdate.range &&
1395
- minDate.getTime() != defaultDateValue.getTime()
1396
- ) {
1397
- console.warn("Minimum birthdate should be equal to default date value")
1398
- isCorrect = false
1399
- queryResultErrors.birthdate.disclose = {
1400
- expected: `${defaultDateValue.toISOString()}`,
1401
- received: `${minDate.toISOString()}`,
1402
- message: "Minimum birthdate should be equal to default date value",
1403
- }
1404
- }
1405
- } else {
1406
- console.warn("Birthdate is not set in the query result")
2260
+ const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
2261
+ const committedInputs = proof.committedInputs?.compare_birthdate as DateCommittedInputs
2262
+ const calculatedParamCommitment = await getDateParameterCommitment(
2263
+ ProofType.BIRTHDATE,
2264
+ committedInputs.currentDate,
2265
+ committedInputs.minDate,
2266
+ committedInputs.maxDate,
2267
+ )
2268
+ if (paramCommitment !== calculatedParamCommitment) {
2269
+ console.warn(
2270
+ "The conditions for the birthdate check do not match the conditions checked by the proof",
2271
+ )
1407
2272
  isCorrect = false
1408
- queryResultErrors.birthdate.disclose = {
1409
- message: "Birthdate is not set in the query result",
2273
+ queryResultErrors.birthdate.commitment = {
2274
+ expected: `Commitment: ${calculatedParamCommitment}`,
2275
+ received: `Commitment: ${paramCommitment}`,
2276
+ message:
2277
+ "The conditions for the birthdate check do not match the conditions checked by the proof",
1410
2278
  }
1411
2279
  }
2280
+ const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
2281
+ this.checkBirthdatePublicInputs(proof, queryResult)
2282
+ isCorrect = isCorrect && isCorrectBirthdate
2283
+ queryResultErrors = {
2284
+ ...queryResultErrors,
2285
+ ...queryResultErrorsBirthdate,
2286
+ }
1412
2287
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1413
2288
  } else if (proof.name === "compare_expiry") {
1414
2289
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -1423,83 +2298,33 @@ export class ZKPassport {
1423
2298
  message: "Failed to check the link between the validity of the ID and its expiry date",
1424
2299
  }
1425
2300
  }
1426
- const minDate = getMinDateFromProof(proofData)
1427
- const maxDate = getMaxDateFromProof(proofData)
1428
- if (queryResult.expiry_date) {
1429
- if (
1430
- queryResult.expiry_date.gte &&
1431
- queryResult.expiry_date.gte.result &&
1432
- minDate < queryResult.expiry_date.gte.expected
1433
- ) {
1434
- console.warn("Expiry date is not greater than or equal to the expected expiry date")
1435
- isCorrect = false
1436
- queryResultErrors.expiry_date.gte = {
1437
- expected: queryResult.expiry_date.gte.expected,
1438
- received: minDate,
1439
- message: "Expiry date is not greater than or equal to the expected expiry date",
1440
- }
1441
- }
1442
- if (
1443
- queryResult.expiry_date.lte &&
1444
- queryResult.expiry_date.lte.result &&
1445
- maxDate > queryResult.expiry_date.lte.expected
1446
- ) {
1447
- console.warn("Expiry date is not less than the expected expiry date")
1448
- isCorrect = false
1449
- queryResultErrors.expiry_date.lte = {
1450
- expected: queryResult.expiry_date.lte.expected,
1451
- received: maxDate,
1452
- message: "Expiry date is not less than the expected expiry date",
1453
- }
1454
- }
1455
- if (queryResult.expiry_date.range) {
1456
- if (
1457
- queryResult.expiry_date.range.result &&
1458
- (minDate < queryResult.expiry_date.range.expected[0] ||
1459
- maxDate > queryResult.expiry_date.range.expected[1])
1460
- ) {
1461
- console.warn("Expiry date is not in the expected range")
1462
- isCorrect = false
1463
- queryResultErrors.expiry_date.range = {
1464
- expected: queryResult.expiry_date.range.expected,
1465
- received: [minDate, maxDate],
1466
- message: "Expiry date is not in the expected range",
1467
- }
1468
- }
1469
- }
1470
- if (
1471
- !queryResult.expiry_date.lte &&
1472
- !queryResult.expiry_date.range &&
1473
- maxDate.getTime() != defaultDateValue.getTime()
1474
- ) {
1475
- console.warn("Maximum expiry date should be equal to default date value")
1476
- isCorrect = false
1477
- queryResultErrors.expiry_date.disclose = {
1478
- expected: `${defaultDateValue.toISOString()}`,
1479
- received: `${maxDate.toISOString()}`,
1480
- message: "Maximum expiry date should be equal to default date value",
1481
- }
1482
- }
1483
- if (
1484
- !queryResult.expiry_date.gte &&
1485
- !queryResult.expiry_date.range &&
1486
- minDate.getTime() != defaultDateValue.getTime()
1487
- ) {
1488
- console.warn("Minimum expiry date should be equal to default date value")
1489
- isCorrect = false
1490
- queryResultErrors.expiry_date.disclose = {
1491
- expected: `${defaultDateValue.toISOString()}`,
1492
- received: `${minDate.toISOString()}`,
1493
- message: "Minimum expiry date should be equal to default date value",
1494
- }
1495
- }
1496
- } else {
1497
- console.warn("Expiry date is not set in the query result")
2301
+ const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
2302
+ const committedInputs = proof.committedInputs?.compare_expiry as DateCommittedInputs
2303
+ const calculatedParamCommitment = await getDateParameterCommitment(
2304
+ ProofType.EXPIRY_DATE,
2305
+ committedInputs.currentDate,
2306
+ committedInputs.minDate,
2307
+ committedInputs.maxDate,
2308
+ )
2309
+ if (paramCommitment !== calculatedParamCommitment) {
2310
+ console.warn(
2311
+ "The conditions for the expiry date check do not match the conditions checked by the proof",
2312
+ )
1498
2313
  isCorrect = false
1499
- queryResultErrors.expiry_date.disclose = {
1500
- message: "Expiry date is not set in the query result",
2314
+ queryResultErrors.expiry_date.commitment = {
2315
+ expected: `Commitment: ${calculatedParamCommitment}`,
2316
+ received: `Commitment: ${paramCommitment}`,
2317
+ message:
2318
+ "The conditions for the expiry date check do not match the conditions checked by the proof",
1501
2319
  }
1502
2320
  }
2321
+ const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
2322
+ this.checkExpiryDatePublicInputs(proof, queryResult)
2323
+ isCorrect = isCorrect && isCorrectExpiryDate
2324
+ queryResultErrors = {
2325
+ ...queryResultErrors,
2326
+ ...queryResultErrorsExpiryDate,
2327
+ }
1503
2328
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1504
2329
  } else if (proof.name === "exclusion_check_nationality") {
1505
2330
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -1515,44 +2340,36 @@ export class ZKPassport {
1515
2340
  "Failed to check the link between the validity of the ID and the nationality exclusion check",
1516
2341
  }
1517
2342
  }
1518
- const countryList = getCountryListFromExclusionProof(proofData)
1519
- if (
1520
- queryResult.nationality &&
1521
- queryResult.nationality.out &&
1522
- queryResult.nationality.out.result
1523
- ) {
1524
- if (
1525
- !queryResult.nationality.out.expected?.every((country) => countryList.includes(country))
1526
- ) {
1527
- console.warn("Nationality exclusion list does not match the one from the query results")
1528
- isCorrect = false
1529
- queryResultErrors.nationality.out = {
1530
- expected: queryResult.nationality.out.expected,
1531
- received: countryList,
1532
- message: "Nationality exclusion list does not match the one from the query results",
1533
- }
1534
- }
1535
- } else if (!queryResult.nationality || !queryResult.nationality.out) {
1536
- console.warn("Nationality exclusion is not set in the query result")
2343
+ const countryList = (
2344
+ proof.committedInputs?.exclusion_check_nationality as CountryCommittedInputs
2345
+ ).countries
2346
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2347
+ const calculatedParamCommitment = await getCountryParameterCommitment(
2348
+ ProofType.NATIONALITY_EXCLUSION,
2349
+ countryList,
2350
+ true,
2351
+ )
2352
+ if (paramCommittment !== calculatedParamCommitment) {
2353
+ console.warn(
2354
+ "The committed country list for the exclusion check does not match the one from the proof",
2355
+ )
1537
2356
  isCorrect = false
1538
- queryResultErrors.nationality.out = {
1539
- message: "Nationality exclusion is not set in the query result",
2357
+ queryResultErrors.nationality.commitment = {
2358
+ expected: `Commitment: ${calculatedParamCommitment}`,
2359
+ received: `Commitment: ${paramCommittment}`,
2360
+ message:
2361
+ "The committed country list for the exclusion check does not match the one from the proof",
1540
2362
  }
1541
2363
  }
1542
- // Check the countryList is in ascending order
1543
- // If the prover doesn't use a sorted list then the proof cannot be trusted
1544
- // as it is requirement in the circuit for the exclusion check to work
1545
- for (let i = 1; i < countryList.length; i++) {
1546
- if (countryList[i] < countryList[i - 1]) {
1547
- console.warn(
1548
- "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1549
- )
1550
- isCorrect = false
1551
- queryResultErrors.nationality.out = {
1552
- message:
1553
- "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1554
- }
1555
- }
2364
+
2365
+ const {
2366
+ isCorrect: isCorrectNationalityExclusion,
2367
+ queryResultErrors: queryResultErrorsNationalityExclusion,
2368
+ } = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
2369
+ isCorrect = isCorrect && isCorrectNationalityExclusion
2370
+ queryResultErrors = {
2371
+ ...queryResultErrors,
2372
+ ...queryResultErrorsNationalityExclusion,
1556
2373
  }
1557
2374
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1558
2375
  } else if (proof.name === "exclusion_check_issuing_country") {
@@ -1569,49 +2386,35 @@ export class ZKPassport {
1569
2386
  "Failed to check the link between the validity of the ID and the issuing country exclusion check",
1570
2387
  }
1571
2388
  }
1572
- const countryList = getCountryListFromExclusionProof(proofData)
1573
- if (
1574
- queryResult.issuing_country &&
1575
- queryResult.issuing_country.out &&
1576
- queryResult.issuing_country.out.result
1577
- ) {
1578
- if (
1579
- !queryResult.issuing_country.out.expected?.every((country) =>
1580
- countryList.includes(country),
1581
- )
1582
- ) {
1583
- console.warn(
1584
- "Issuing country exclusion list does not match the one from the query results",
1585
- )
1586
- isCorrect = false
1587
- queryResultErrors.issuing_country.out = {
1588
- expected: queryResult.issuing_country.out.expected,
1589
- received: countryList,
1590
- message:
1591
- "Issuing country exclusion list does not match the one from the query results",
1592
- }
1593
- }
1594
- } else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1595
- console.warn("Issuing country exclusion is not set in the query result")
2389
+ const countryList = (
2390
+ proof.committedInputs?.exclusion_check_issuing_country as CountryCommittedInputs
2391
+ ).countries
2392
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2393
+ const calculatedParamCommitment = await getCountryParameterCommitment(
2394
+ ProofType.ISSUING_COUNTRY_EXCLUSION,
2395
+ countryList,
2396
+ true,
2397
+ )
2398
+ if (paramCommittment !== calculatedParamCommitment) {
2399
+ console.warn(
2400
+ "The committed country list for the issuing country exclusion check does not match the one from the proof",
2401
+ )
1596
2402
  isCorrect = false
1597
- queryResultErrors.issuing_country.out = {
1598
- message: "Issuing country exclusion is not set in the query result",
2403
+ queryResultErrors.issuing_country.commitment = {
2404
+ expected: `Commitment: ${calculatedParamCommitment}`,
2405
+ received: `Commitment: ${paramCommittment}`,
2406
+ message:
2407
+ "The committed country list for the issuing country exclusion check does not match the one from the proof",
1599
2408
  }
1600
2409
  }
1601
- // Check the countryList is in ascending order
1602
- // If the prover doesn't use a sorted list then the proof cannot be trusted
1603
- // as it is requirement in the circuit for the exclusion check to work
1604
- for (let i = 1; i < countryList.length; i++) {
1605
- if (countryList[i] < countryList[i - 1]) {
1606
- console.warn(
1607
- "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1608
- )
1609
- isCorrect = false
1610
- queryResultErrors.issuing_country.out = {
1611
- message:
1612
- "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1613
- }
1614
- }
2410
+ const {
2411
+ isCorrect: isCorrectIssuingCountryExclusion,
2412
+ queryResultErrors: queryResultErrorsIssuingCountryExclusion,
2413
+ } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
2414
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion
2415
+ queryResultErrors = {
2416
+ ...queryResultErrors,
2417
+ ...queryResultErrorsIssuingCountryExclusion,
1615
2418
  }
1616
2419
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1617
2420
  } else if (proof.name === "inclusion_check_nationality") {
@@ -1628,30 +2431,36 @@ export class ZKPassport {
1628
2431
  "Failed to check the link between the validity of the ID and the nationality inclusion check",
1629
2432
  }
1630
2433
  }
1631
- const countryList = getCountryListFromInclusionProof(proofData)
1632
- if (
1633
- queryResult.nationality &&
1634
- queryResult.nationality.in &&
1635
- queryResult.nationality.in.result
1636
- ) {
1637
- if (
1638
- !queryResult.nationality.in.expected?.every((country) => countryList.includes(country))
1639
- ) {
1640
- console.warn("Nationality inclusion list does not match the one from the query results")
1641
- isCorrect = false
1642
- queryResultErrors.nationality.in = {
1643
- expected: queryResult.nationality.in.expected,
1644
- received: countryList,
1645
- message: "Nationality inclusion list does not match the one from the query results",
1646
- }
1647
- }
1648
- } else if (!queryResult.nationality || !queryResult.nationality.in) {
1649
- console.warn("Nationality inclusion is not set in the query result")
2434
+ const countryList = (
2435
+ proof.committedInputs?.inclusion_check_nationality as CountryCommittedInputs
2436
+ ).countries
2437
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2438
+ const calculatedParamCommitment = await getCountryParameterCommitment(
2439
+ ProofType.NATIONALITY_INCLUSION,
2440
+ countryList,
2441
+ false,
2442
+ )
2443
+ if (paramCommittment !== calculatedParamCommitment) {
2444
+ console.warn(
2445
+ "The committed country list for the nationality inclusion check does not match the one from the proof",
2446
+ )
1650
2447
  isCorrect = false
1651
- queryResultErrors.nationality.in = {
1652
- message: "Nationality inclusion is not set in the query result",
2448
+ queryResultErrors.nationality.commitment = {
2449
+ expected: `Commitment: ${calculatedParamCommitment}`,
2450
+ received: `Commitment: ${paramCommittment}`,
2451
+ message:
2452
+ "The committed country list for the nationality inclusion check does not match the one from the proof",
1653
2453
  }
1654
2454
  }
2455
+ const {
2456
+ isCorrect: isCorrectNationalityInclusion,
2457
+ queryResultErrors: queryResultErrorsNationalityInclusion,
2458
+ } = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
2459
+ isCorrect = isCorrect && isCorrectNationalityInclusion
2460
+ queryResultErrors = {
2461
+ ...queryResultErrors,
2462
+ ...queryResultErrorsNationalityInclusion,
2463
+ }
1655
2464
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1656
2465
  } else if (proof.name === "inclusion_check_issuing_country") {
1657
2466
  commitmentIn = getCommitmentInFromDisclosureProof(proofData)
@@ -1667,35 +2476,36 @@ export class ZKPassport {
1667
2476
  "Failed to check the link between the validity of the ID and the issuing country inclusion check",
1668
2477
  }
1669
2478
  }
1670
- const countryList = getCountryListFromInclusionProof(proofData)
1671
- if (
1672
- queryResult.issuing_country &&
1673
- queryResult.issuing_country.in &&
1674
- queryResult.issuing_country.in.result
1675
- ) {
1676
- if (
1677
- !queryResult.issuing_country.in.expected?.every((country) =>
1678
- countryList.includes(country),
1679
- )
1680
- ) {
1681
- console.warn(
1682
- "Issuing country inclusion list does not match the one from the query results",
1683
- )
1684
- isCorrect = false
1685
- queryResultErrors.issuing_country.in = {
1686
- expected: queryResult.issuing_country.in.expected,
1687
- received: countryList,
1688
- message:
1689
- "Issuing country inclusion list does not match the one from the query results",
1690
- }
1691
- }
1692
- } else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1693
- console.warn("Issuing country inclusion is not set in the query result")
2479
+ const countryList = (
2480
+ proof.committedInputs?.inclusion_check_issuing_country as CountryCommittedInputs
2481
+ ).countries
2482
+ const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
2483
+ const calculatedParamCommitment = await getCountryParameterCommitment(
2484
+ ProofType.ISSUING_COUNTRY_INCLUSION,
2485
+ countryList,
2486
+ false,
2487
+ )
2488
+ if (paramCommittment !== calculatedParamCommitment) {
2489
+ console.warn(
2490
+ "The committed country list for the issuing country inclusion check does not match the one from the proof",
2491
+ )
1694
2492
  isCorrect = false
1695
- queryResultErrors.issuing_country.in = {
1696
- message: "Issuing country inclusion is not set in the query result",
2493
+ queryResultErrors.issuing_country.commitment = {
2494
+ expected: `Commitment: ${calculatedParamCommitment}`,
2495
+ received: `Commitment: ${paramCommittment}`,
2496
+ message:
2497
+ "The committed country list for the issuing country inclusion check does not match the one from the proof",
1697
2498
  }
1698
2499
  }
2500
+ const {
2501
+ isCorrect: isCorrectIssuingCountryInclusion,
2502
+ queryResultErrors: queryResultErrorsIssuingCountryInclusion,
2503
+ } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
2504
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion
2505
+ queryResultErrors = {
2506
+ ...queryResultErrors,
2507
+ ...queryResultErrorsIssuingCountryInclusion,
2508
+ }
1699
2509
  uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
1700
2510
  }
1701
2511
  }
@@ -1752,17 +2562,54 @@ export class ZKPassport {
1752
2562
  // Only proceed with the proof verification if the public inputs are correct
1753
2563
  if (verified) {
1754
2564
  for (const proof of proofs) {
1755
- const proofData = getProofData(proof.proof as string, true)
2565
+ const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
1756
2566
  const hostedPackagedCircuit = await getHostedPackagedCircuitByName(
1757
2567
  proof.version as any,
1758
2568
  proof.name!,
1759
2569
  )
1760
- const vkeyBytes = Buffer.from(hostedPackagedCircuit.vkey, "base64")
1761
- try {
1762
- verified = await verifier.verifyUltraHonkProof(proofData, new Uint8Array(vkeyBytes))
1763
- } catch (e) {
1764
- console.warn("Error verifying proof", e)
1765
- verified = false
2570
+ if (proof.name?.startsWith("outer_evm")) {
2571
+ try {
2572
+ const { createPublicClient, http } = await import("viem")
2573
+ const { sepolia } = await import("viem/chains")
2574
+ const verifierDetails = this.getSolidityVerifierDetails("ethereum_sepolia")
2575
+ const client = createPublicClient({
2576
+ chain: sepolia,
2577
+ transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
2578
+ })
2579
+ const params = this.getSolidityVerifierParameters(proof)
2580
+ const result = await client.readContract({
2581
+ address: verifierDetails.address as `0x${string}`,
2582
+ abi: verifierDetails.abi,
2583
+ functionName: "verifyProof",
2584
+ args: [
2585
+ params.vkeyHash,
2586
+ params.proof,
2587
+ params.publicInputs,
2588
+ params.committedInputs,
2589
+ params.committedInputCounts,
2590
+ params.validityPeriodInDays,
2591
+ ],
2592
+ })
2593
+ const isVerified = Array.isArray(result) ? Boolean(result[0]) : false
2594
+ verified = isVerified
2595
+ } catch (error) {
2596
+ console.warn("Error verifying proof", error)
2597
+ verified = false
2598
+ }
2599
+ } else {
2600
+ const vkeyBytes = Buffer.from(hostedPackagedCircuit.vkey, "base64")
2601
+ try {
2602
+ verified = await verifier.verifyUltraHonkProof(
2603
+ {
2604
+ proof: Buffer.from(proofData.proof.join(""), "hex"),
2605
+ publicInputs: proofData.publicInputs,
2606
+ },
2607
+ new Uint8Array(vkeyBytes),
2608
+ )
2609
+ } catch (e) {
2610
+ console.warn("Error verifying proof", e)
2611
+ verified = false
2612
+ }
1766
2613
  }
1767
2614
  if (!verified) {
1768
2615
  // Break the loop if the proof is not valid
@@ -1776,6 +2623,166 @@ export class ZKPassport {
1776
2623
  return { uniqueIdentifier, verified, queryResultErrors }
1777
2624
  }
1778
2625
 
2626
+ public getSolidityVerifierDetails(network: EVMChain): {
2627
+ address: string
2628
+ abi: {
2629
+ type: "function" | "event" | "constructor"
2630
+ name: string
2631
+ inputs: { name: string; type: string; internalType: string }[]
2632
+ outputs: { name: string; type: string; internalType: string }[]
2633
+ }[]
2634
+ } {
2635
+ if (network === "ethereum_sepolia") {
2636
+ return {
2637
+ address: "0xca644D3424c2ee577FaaF2b56C0f9D1937E8e87C",
2638
+ abi: ZKPassportVerifierAbi.abi as any,
2639
+ }
2640
+ } else if (network === "local_anvil") {
2641
+ return {
2642
+ address: "0x0",
2643
+ abi: ZKPassportVerifierAbi.abi as any,
2644
+ }
2645
+ }
2646
+ throw new Error(`Unsupported network: ${network}`)
2647
+ }
2648
+
2649
+ public getSolidityVerifierParameters(proof: ProofResult, validityPeriodInDays: number = 7) {
2650
+ if (!proof.name?.startsWith("outer_evm")) {
2651
+ throw new Error(
2652
+ "This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.",
2653
+ )
2654
+ }
2655
+ const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
2656
+ // For EVM optimised proofs, the first 16 bytes of the proof are the aggregation object
2657
+ // and should be moved at the end of the public inputs
2658
+ const actualProof = proofData.proof.slice(16)
2659
+ const actualPublicInputs = proofData.publicInputs.concat(
2660
+ proofData.proof.slice(0, 16).map((x) => `0x${x}`),
2661
+ )
2662
+ let committedInputCounts: { circuitName: DisclosureCircuitName; count: number }[] = []
2663
+ let committedInputs: { circuitName: DisclosureCircuitName; inputs: string }[] = []
2664
+ for (const key in proof.committedInputs) {
2665
+ const committedInputCount = getCommittedInputCount(key as DisclosureCircuitName)
2666
+ const circuitName = key as DisclosureCircuitName
2667
+ committedInputCounts.push({ circuitName, count: committedInputCount })
2668
+ let compressedCommittedInputs = ""
2669
+ if (
2670
+ circuitName === "inclusion_check_issuing_country_evm" ||
2671
+ circuitName === "inclusion_check_nationality_evm" ||
2672
+ circuitName === "exclusion_check_issuing_country_evm" ||
2673
+ circuitName === "exclusion_check_nationality_evm"
2674
+ ) {
2675
+ const value = proof.committedInputs[circuitName] as CountryCommittedInputs
2676
+ const formattedCountries = value.countries
2677
+ if (
2678
+ circuitName === "exclusion_check_issuing_country_evm" ||
2679
+ circuitName === "exclusion_check_nationality_evm"
2680
+ ) {
2681
+ formattedCountries.sort((a, b) => a.localeCompare(b))
2682
+ }
2683
+ const proofType = (() => {
2684
+ switch (circuitName) {
2685
+ case "exclusion_check_issuing_country_evm":
2686
+ return ProofType.ISSUING_COUNTRY_EXCLUSION
2687
+ case "exclusion_check_nationality_evm":
2688
+ return ProofType.NATIONALITY_EXCLUSION
2689
+ case "inclusion_check_issuing_country_evm":
2690
+ return ProofType.ISSUING_COUNTRY_INCLUSION
2691
+ case "inclusion_check_nationality_evm":
2692
+ return ProofType.NATIONALITY_INCLUSION
2693
+ }
2694
+ })()
2695
+ compressedCommittedInputs =
2696
+ proofType.toString(16).padStart(2, "0") +
2697
+ rightPadArrayWithZeros(
2698
+ formattedCountries.map((c) => Array.from(new TextEncoder().encode(c))).flat(),
2699
+ 600,
2700
+ )
2701
+ .map((x) => x.toString(16).padStart(2, "0"))
2702
+ .join("")
2703
+ } else if (circuitName === "compare_age_evm") {
2704
+ const value = proof.committedInputs[circuitName] as AgeCommittedInputs
2705
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
2706
+ compressedCommittedInputs =
2707
+ ProofType.AGE.toString(16).padStart(2, "0") +
2708
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
2709
+ value.minAge.toString(16).padStart(2, "0") +
2710
+ value.maxAge.toString(16).padStart(2, "0")
2711
+ } else if (circuitName === "compare_birthdate_evm") {
2712
+ const value = proof.committedInputs[circuitName] as DateCommittedInputs
2713
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
2714
+ const minDateBytes = Array.from(new TextEncoder().encode(value.minDate))
2715
+ const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate))
2716
+ compressedCommittedInputs =
2717
+ ProofType.BIRTHDATE.toString(16).padStart(2, "0") +
2718
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
2719
+ minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
2720
+ maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
2721
+ } else if (circuitName === "compare_expiry_evm") {
2722
+ const value = proof.committedInputs[circuitName] as DateCommittedInputs
2723
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
2724
+ const minDateBytes = Array.from(new TextEncoder().encode(value.minDate))
2725
+ const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate))
2726
+ compressedCommittedInputs =
2727
+ ProofType.EXPIRY_DATE.toString(16).padStart(2, "0") +
2728
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
2729
+ minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
2730
+ maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
2731
+ } else if (circuitName === "disclose_bytes_evm") {
2732
+ const value = proof.committedInputs[circuitName] as DiscloseCommittedInputs
2733
+ compressedCommittedInputs =
2734
+ ProofType.DISCLOSE.toString(16).padStart(2, "0") +
2735
+ value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2736
+ value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
2737
+ } else {
2738
+ throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`)
2739
+ }
2740
+ committedInputs.push({ circuitName, inputs: compressedCommittedInputs })
2741
+ }
2742
+ const parameterCommitments = proofData.publicInputs.slice(11, proofData.publicInputs.length - 1)
2743
+ let compressedCommittedInputs = ""
2744
+ let committedInputCountsArray = []
2745
+ for (const commitment of parameterCommitments) {
2746
+ const committedInput = committedInputs.find((x) => {
2747
+ const rawHashedInputs = sha256(hexToBytes(x.inputs))
2748
+ // Shift the hash 8 bits to the right (1 byte)
2749
+ // as one byte is dropped in the circuit to fit in the 254-bit field size
2750
+ const hashedInputs = new Uint8Array(rawHashedInputs.length)
2751
+ // Move each byte 1 position to the right (shifting 8 bits)
2752
+ for (let i = 0; i < rawHashedInputs.length - 1; i++) {
2753
+ hashedInputs[i + 1] = rawHashedInputs[i]
2754
+ }
2755
+ // First byte becomes 0 (since we're shifting right)
2756
+ hashedInputs[0] = 0
2757
+
2758
+ return bytesToHex(hashedInputs) === commitment.replace("0x", "")
2759
+ })
2760
+ if (committedInput) {
2761
+ const count = committedInputCounts.find(
2762
+ (x) => x.circuitName === committedInput.circuitName,
2763
+ )?.count
2764
+ if (count) {
2765
+ committedInputCountsArray.push(count)
2766
+ compressedCommittedInputs += committedInput.inputs
2767
+ } else {
2768
+ throw new Error(`Unknown circuit name: ${committedInput.circuitName}`)
2769
+ }
2770
+ } else {
2771
+ throw new Error(`Invalid commitment: ${commitment}`)
2772
+ }
2773
+ }
2774
+ const params: SolidityVerifierParameters = {
2775
+ // Make sure the vkeyHash is 32 bytes
2776
+ vkeyHash: `0x${proof.vkeyHash!.replace("0x", "").padStart(64, "0")}`,
2777
+ proof: `0x${actualProof.join("")}`,
2778
+ publicInputs: actualPublicInputs,
2779
+ committedInputs: `0x${compressedCommittedInputs}`,
2780
+ committedInputCounts: committedInputCountsArray,
2781
+ validityPeriodInDays,
2782
+ }
2783
+ return params
2784
+ }
2785
+
1779
2786
  /**
1780
2787
  * @notice Returns the URL of the request.
1781
2788
  * @param requestId The request ID.
@@ -1789,7 +2796,7 @@ export class ZKPassport {
1789
2796
  const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString(
1790
2797
  "base64",
1791
2798
  )
1792
- return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}`
2799
+ return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`
1793
2800
  }
1794
2801
 
1795
2802
  /**