@zkpassport/sdk 0.2.15 → 0.3.1

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