@zkpassport/sdk 0.2.14 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -13,6 +13,10 @@ const logger_1 = require("./logger");
13
13
  const pako_1 = require("pako");
14
14
  const en_json_1 = tslib_1.__importDefault(require("i18n-iso-countries/langs/en.json"));
15
15
  const buffer_1 = require("buffer/");
16
+ const sha256_1 = require("@noble/hashes/sha256");
17
+ const utils_3 = require("@noble/hashes/utils");
18
+ const ZKPassportVerifier_json_1 = tslib_1.__importDefault(require("./assets/abi/ZKPassportVerifier.json"));
19
+ const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
16
20
  // If Buffer is not defined, then we use the Buffer from the buffer package
17
21
  if (typeof globalThis.Buffer === "undefined") {
18
22
  globalThis.Buffer = buffer_1.Buffer;
@@ -59,13 +63,13 @@ function generalCompare(fnName, key, value, requestId, requestIdToConfig) {
59
63
  [fnName]: value,
60
64
  };
61
65
  }
62
- var utils_3 = require("@zkpassport/utils");
63
- Object.defineProperty(exports, "SANCTIONED_COUNTRIES", { enumerable: true, get: function () { return utils_3.SANCTIONED_COUNTRIES; } });
64
- Object.defineProperty(exports, "EU_COUNTRIES", { enumerable: true, get: function () { return utils_3.EU_COUNTRIES; } });
65
- Object.defineProperty(exports, "EEA_COUNTRIES", { enumerable: true, get: function () { return utils_3.EEA_COUNTRIES; } });
66
- Object.defineProperty(exports, "SCHENGEN_COUNTRIES", { enumerable: true, get: function () { return utils_3.SCHENGEN_COUNTRIES; } });
67
- Object.defineProperty(exports, "ASEAN_COUNTRIES", { enumerable: true, get: function () { return utils_3.ASEAN_COUNTRIES; } });
68
- Object.defineProperty(exports, "MERCOSUR_COUNTRIES", { enumerable: true, get: function () { return utils_3.MERCOSUR_COUNTRIES; } });
66
+ var utils_4 = require("@zkpassport/utils");
67
+ Object.defineProperty(exports, "SANCTIONED_COUNTRIES", { enumerable: true, get: function () { return utils_4.SANCTIONED_COUNTRIES; } });
68
+ Object.defineProperty(exports, "EU_COUNTRIES", { enumerable: true, get: function () { return utils_4.EU_COUNTRIES; } });
69
+ Object.defineProperty(exports, "EEA_COUNTRIES", { enumerable: true, get: function () { return utils_4.EEA_COUNTRIES; } });
70
+ Object.defineProperty(exports, "SCHENGEN_COUNTRIES", { enumerable: true, get: function () { return utils_4.SCHENGEN_COUNTRIES; } });
71
+ Object.defineProperty(exports, "ASEAN_COUNTRIES", { enumerable: true, get: function () { return utils_4.ASEAN_COUNTRIES; } });
72
+ Object.defineProperty(exports, "MERCOSUR_COUNTRIES", { enumerable: true, get: function () { return utils_4.MERCOSUR_COUNTRIES; } });
69
73
  class ZKPassport {
70
74
  //private wasmVerifierInit: boolean = false
71
75
  constructor(_domain) {
@@ -117,6 +121,11 @@ class ZKPassport {
117
121
  delete this.topicToFailedProofCount[topic];
118
122
  }
119
123
  setExpectedProofCount(topic) {
124
+ // If the mode is not fast, we'll receive only 1 compressed proof
125
+ if (this.topicToLocalConfig[topic].mode !== "fast") {
126
+ this.topicToExpectedProofCount[topic] = 1;
127
+ return;
128
+ }
120
129
  const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
121
130
  const neededCircuits = [];
122
131
  // Determine which circuits are needed based on the requested fields
@@ -196,7 +205,13 @@ class ZKPassport {
196
205
  logger_1.noLogger.debug(`User generated proof`);
197
206
  // Uncompress the proof and convert it to a hex string
198
207
  const bytesProof = buffer_1.Buffer.from(request.params.proof, "base64");
208
+ const bytesCommittedInputs = request.params.committedInputs
209
+ ? buffer_1.Buffer.from(request.params.committedInputs, "base64")
210
+ : null;
199
211
  const uncompressedProof = (0, pako_1.inflate)(bytesProof);
212
+ const uncompressedCommittedInputs = bytesCommittedInputs
213
+ ? (0, pako_1.inflate)(bytesCommittedInputs)
214
+ : null;
200
215
  // The gzip lib in the app compress the proof as ASCII
201
216
  // and since the app passes the proof as a hex string, we can
202
217
  // just decode the bytes as hex characters using the TextDecoder
@@ -206,6 +221,9 @@ class ZKPassport {
206
221
  vkeyHash: request.params.vkeyHash,
207
222
  name: request.params.name,
208
223
  version: request.params.version,
224
+ committedInputs: uncompressedCommittedInputs
225
+ ? JSON.parse(new TextDecoder().decode(uncompressedCommittedInputs))
226
+ : undefined,
209
227
  };
210
228
  this.topicToProofs[topic].push(processedProof);
211
229
  await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
@@ -311,7 +329,7 @@ class ZKPassport {
311
329
  const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[topic].publicKey);
312
330
  this.setExpectedProofCount(topic);
313
331
  return {
314
- url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
332
+ url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
315
333
  requestId: topic,
316
334
  onRequestReceived: (callback) => this.onRequestReceivedCallbacks[topic].push(callback),
317
335
  onGeneratingProof: (callback) => this.onGeneratingProofCallbacks[topic].push(callback),
@@ -335,7 +353,7 @@ class ZKPassport {
335
353
  * @param validity How many days ago should have the ID been last scanned by the user?
336
354
  * @returns The query builder object.
337
355
  */
338
- async request({ name, logo, purpose, scope, validity, topicOverride, keyPairOverride, }) {
356
+ async request({ name, logo, purpose, scope, mode, validity, topicOverride, keyPairOverride, }) {
339
357
  const topic = topicOverride || (0, crypto_1.randomBytes)(16).toString("hex");
340
358
  const keyPair = keyPairOverride || (await (0, encryption_1.generateECDHKeyPair)());
341
359
  this.topicToKeyPair[topic] = {
@@ -349,6 +367,7 @@ class ZKPassport {
349
367
  this.topicToLocalConfig[topic] = {
350
368
  // Default to 6 months
351
369
  validity: validity || 6 * 30,
370
+ mode: mode || "fast",
352
371
  };
353
372
  this.onRequestReceivedCallbacks[topic] = [];
354
373
  this.onGeneratingProofCallbacks[topic] = [];
@@ -406,19 +425,319 @@ class ZKPassport {
406
425
  };
407
426
  return this.getZkPassportRequest(topic);
408
427
  }
409
- async checkPublicInputs(proofs, queryResult, validity) {
410
- let commitmentIn;
411
- let commitmentOut;
428
+ checkDiscloseBytesPublicInputs(proof, queryResult) {
429
+ const queryResultErrors = {
430
+ sig_check_dsc: {},
431
+ sig_check_id_data: {},
432
+ data_check_integrity: {},
433
+ disclose: {},
434
+ age: {},
435
+ birthdate: {},
436
+ expiry_date: {},
437
+ document_type: {},
438
+ issuing_country: {},
439
+ gender: {},
440
+ nationality: {},
441
+ firstname: {},
442
+ lastname: {},
443
+ fullname: {},
444
+ document_number: {},
445
+ outer: {},
446
+ };
412
447
  let isCorrect = true;
413
- let uniqueIdentifier;
414
- const VALID_CERTIFICATE_REGISTRY_ROOT = [
415
- BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
416
- BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
417
- BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
418
- ];
419
- const defaultDateValue = new Date(1111, 10, 11);
420
- const currentTime = new Date();
421
- const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
448
+ // We can't be certain that the disclosed data is for a passport or an ID card
449
+ // so we need to check both (unless the document type is revealed)
450
+ const disclosedDataPassport = utils_1.DisclosedData.fromDisclosedBytes((proof.committedInputs?.disclose_bytes).disclosedBytes, "passport");
451
+ const disclosedDataIDCard = utils_1.DisclosedData.fromDisclosedBytes((proof.committedInputs?.disclose_bytes).disclosedBytes, "id_card");
452
+ if (queryResult.document_type) {
453
+ // Document type is always at the same index in the disclosed data
454
+ if (queryResult.document_type.eq &&
455
+ queryResult.document_type.eq.result &&
456
+ queryResult.document_type.eq.expected !== disclosedDataPassport.documentType) {
457
+ console.warn("Document type does not match the expected document type");
458
+ isCorrect = false;
459
+ queryResultErrors.document_type.eq = {
460
+ expected: `${queryResult.document_type.eq.expected}`,
461
+ received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
462
+ message: "Document type does not match the expected document type",
463
+ };
464
+ }
465
+ if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
466
+ console.warn("Document type does not match the disclosed document type in query result");
467
+ isCorrect = false;
468
+ queryResultErrors.document_type.disclose = {
469
+ expected: `${queryResult.document_type.disclose?.result}`,
470
+ received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
471
+ message: "Document type does not match the disclosed document type in query result",
472
+ };
473
+ }
474
+ }
475
+ if (queryResult.birthdate) {
476
+ const birthdatePassport = disclosedDataPassport.dateOfBirth;
477
+ const birthdateIDCard = disclosedDataIDCard.dateOfBirth;
478
+ if (queryResult.birthdate.eq &&
479
+ queryResult.birthdate.eq.result &&
480
+ queryResult.birthdate.eq.expected.getTime() !== birthdatePassport.getTime() &&
481
+ queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()) {
482
+ console.warn("Birthdate does not match the expected birthdate");
483
+ isCorrect = false;
484
+ queryResultErrors.birthdate.eq = {
485
+ expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
486
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
487
+ message: "Birthdate does not match the expected birthdate",
488
+ };
489
+ }
490
+ if (queryResult.birthdate.disclose &&
491
+ queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
492
+ queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()) {
493
+ console.warn("Birthdate does not match the disclosed birthdate in query result");
494
+ isCorrect = false;
495
+ queryResultErrors.birthdate.disclose = {
496
+ expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
497
+ received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
498
+ message: "Birthdate does not match the disclosed birthdate in query result",
499
+ };
500
+ }
501
+ }
502
+ if (queryResult.expiry_date) {
503
+ const expiryDatePassport = disclosedDataPassport.dateOfExpiry;
504
+ const expiryDateIDCard = disclosedDataIDCard.dateOfExpiry;
505
+ if (queryResult.expiry_date.eq &&
506
+ queryResult.expiry_date.eq.result &&
507
+ queryResult.expiry_date.eq.expected.getTime() !== expiryDatePassport.getTime() &&
508
+ queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()) {
509
+ console.warn("Expiry date does not match the expected expiry date");
510
+ isCorrect = false;
511
+ queryResultErrors.expiry_date.eq = {
512
+ expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
513
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
514
+ message: "Expiry date does not match the expected expiry date",
515
+ };
516
+ }
517
+ if (queryResult.expiry_date.disclose &&
518
+ queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
519
+ queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()) {
520
+ console.warn("Expiry date does not match the disclosed expiry date in query result");
521
+ isCorrect = false;
522
+ queryResultErrors.expiry_date.disclose = {
523
+ expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
524
+ received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
525
+ message: "Expiry date does not match the disclosed expiry date in query result",
526
+ };
527
+ }
528
+ }
529
+ if (queryResult.nationality) {
530
+ const nationalityPassport = disclosedDataPassport.nationality;
531
+ const nationalityIDCard = disclosedDataIDCard.nationality;
532
+ if (queryResult.nationality.eq &&
533
+ queryResult.nationality.eq.result &&
534
+ queryResult.nationality.eq.expected !== nationalityPassport &&
535
+ queryResult.nationality.eq.expected !== nationalityIDCard) {
536
+ console.warn("Nationality does not match the expected nationality");
537
+ isCorrect = false;
538
+ queryResultErrors.nationality.eq = {
539
+ expected: `${queryResult.nationality.eq.expected}`,
540
+ received: `${nationalityPassport ?? nationalityIDCard}`,
541
+ message: "Nationality does not match the expected nationality",
542
+ };
543
+ }
544
+ if (queryResult.nationality.disclose &&
545
+ queryResult.nationality.disclose.result !== nationalityPassport &&
546
+ queryResult.nationality.disclose.result !== nationalityIDCard) {
547
+ console.warn("Nationality does not match the disclosed nationality in query result");
548
+ isCorrect = false;
549
+ queryResultErrors.nationality.disclose = {
550
+ expected: `${queryResult.nationality.disclose.result}`,
551
+ received: `${nationalityPassport ?? nationalityIDCard}`,
552
+ message: "Nationality does not match the disclosed nationality in query result",
553
+ };
554
+ }
555
+ }
556
+ if (queryResult.document_number) {
557
+ const documentNumberPassport = disclosedDataPassport.documentNumber;
558
+ const documentNumberIDCard = disclosedDataIDCard.documentNumber;
559
+ if (queryResult.document_number.eq &&
560
+ queryResult.document_number.eq.result &&
561
+ queryResult.document_number.eq.expected !== documentNumberPassport &&
562
+ queryResult.document_number.eq.expected !== documentNumberIDCard) {
563
+ console.warn("Document number does not match the expected document number");
564
+ isCorrect = false;
565
+ queryResultErrors.document_number.eq = {
566
+ expected: `${queryResult.document_number.eq.expected}`,
567
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
568
+ message: "Document number does not match the expected document number",
569
+ };
570
+ }
571
+ if (queryResult.document_number.disclose &&
572
+ queryResult.document_number.disclose.result !== documentNumberPassport &&
573
+ queryResult.document_number.disclose.result !== documentNumberIDCard) {
574
+ console.warn("Document number does not match the disclosed document number in query result");
575
+ isCorrect = false;
576
+ queryResultErrors.document_number.disclose = {
577
+ expected: `${queryResult.document_number.disclose.result}`,
578
+ received: `${documentNumberPassport ?? documentNumberIDCard}`,
579
+ message: "Document number does not match the disclosed document number in query result",
580
+ };
581
+ }
582
+ }
583
+ if (queryResult.gender) {
584
+ const genderPassport = disclosedDataPassport.gender;
585
+ const genderIDCard = disclosedDataIDCard.gender;
586
+ if (queryResult.gender.eq &&
587
+ queryResult.gender.eq.result &&
588
+ queryResult.gender.eq.expected !== genderPassport &&
589
+ queryResult.gender.eq.expected !== genderIDCard) {
590
+ console.warn("Gender does not match the expected gender");
591
+ isCorrect = false;
592
+ queryResultErrors.gender.eq = {
593
+ expected: `${queryResult.gender.eq.expected}`,
594
+ received: `${genderPassport ?? genderIDCard}`,
595
+ message: "Gender does not match the expected gender",
596
+ };
597
+ }
598
+ if (queryResult.gender.disclose &&
599
+ queryResult.gender.disclose.result !== genderPassport &&
600
+ queryResult.gender.disclose.result !== genderIDCard) {
601
+ console.warn("Gender does not match the disclosed gender in query result");
602
+ isCorrect = false;
603
+ queryResultErrors.gender.disclose = {
604
+ expected: `${queryResult.gender.disclose.result}`,
605
+ received: `${genderPassport ?? genderIDCard}`,
606
+ message: "Gender does not match the disclosed gender in query result",
607
+ };
608
+ }
609
+ }
610
+ if (queryResult.issuing_country) {
611
+ const issuingCountryPassport = disclosedDataPassport.issuingCountry;
612
+ const issuingCountryIDCard = disclosedDataIDCard.issuingCountry;
613
+ if (queryResult.issuing_country.eq &&
614
+ queryResult.issuing_country.eq.result &&
615
+ queryResult.issuing_country.eq.expected !== issuingCountryPassport &&
616
+ queryResult.issuing_country.eq.expected !== issuingCountryIDCard) {
617
+ console.warn("Issuing country does not match the expected issuing country");
618
+ isCorrect = false;
619
+ queryResultErrors.issuing_country.eq = {
620
+ expected: `${queryResult.issuing_country.eq.expected}`,
621
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
622
+ message: "Issuing country does not match the expected issuing country",
623
+ };
624
+ }
625
+ if (queryResult.issuing_country.disclose &&
626
+ queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
627
+ queryResult.issuing_country.disclose.result !== issuingCountryIDCard) {
628
+ console.warn("Issuing country does not match the disclosed issuing country in query result");
629
+ isCorrect = false;
630
+ queryResultErrors.issuing_country.disclose = {
631
+ expected: `${queryResult.issuing_country.disclose.result}`,
632
+ received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
633
+ message: "Issuing country does not match the disclosed issuing country in query result",
634
+ };
635
+ }
636
+ }
637
+ if (queryResult.fullname) {
638
+ const fullnamePassport = disclosedDataPassport.name;
639
+ const fullnameIDCard = disclosedDataIDCard.name;
640
+ if (queryResult.fullname.eq &&
641
+ queryResult.fullname.eq.result &&
642
+ (0, utils_1.formatName)(queryResult.fullname.eq.expected).toLowerCase() !==
643
+ fullnamePassport.toLowerCase() &&
644
+ (0, utils_1.formatName)(queryResult.fullname.eq.expected).toLowerCase() !== fullnameIDCard.toLowerCase()) {
645
+ console.warn("Fullname does not match the expected fullname");
646
+ isCorrect = false;
647
+ queryResultErrors.fullname.eq = {
648
+ expected: `${queryResult.fullname.eq.expected}`,
649
+ received: `${fullnamePassport ?? fullnameIDCard}`,
650
+ message: "Fullname does not match the expected fullname",
651
+ };
652
+ }
653
+ if (queryResult.fullname.disclose &&
654
+ (0, utils_1.formatName)(queryResult.fullname.disclose.result).toLowerCase() !==
655
+ fullnamePassport.toLowerCase() &&
656
+ (0, utils_1.formatName)(queryResult.fullname.disclose.result).toLowerCase() !==
657
+ fullnameIDCard.toLowerCase()) {
658
+ console.warn("Fullname does not match the disclosed fullname in query result");
659
+ isCorrect = false;
660
+ queryResultErrors.fullname.disclose = {
661
+ expected: `${queryResult.fullname.disclose.result}`,
662
+ received: `${fullnamePassport ?? fullnameIDCard}`,
663
+ message: "Fullname does not match the disclosed fullname in query result",
664
+ };
665
+ }
666
+ }
667
+ if (queryResult.firstname) {
668
+ // If fullname was not revealed, then the name could be either the first name or last name
669
+ const firstnamePassport = disclosedDataPassport.firstName && disclosedDataPassport.firstName.length > 0
670
+ ? disclosedDataPassport.firstName
671
+ : disclosedDataPassport.name;
672
+ const firstnameIDCard = disclosedDataIDCard.firstName && disclosedDataIDCard.firstName.length > 0
673
+ ? disclosedDataIDCard.firstName
674
+ : disclosedDataIDCard.name;
675
+ if (queryResult.firstname.eq &&
676
+ queryResult.firstname.eq.result &&
677
+ (0, utils_1.formatName)(queryResult.firstname.eq.expected).toLowerCase() !==
678
+ firstnamePassport.toLowerCase() &&
679
+ (0, utils_1.formatName)(queryResult.firstname.eq.expected).toLowerCase() !==
680
+ firstnameIDCard.toLowerCase()) {
681
+ console.warn("Firstname does not match the expected firstname");
682
+ isCorrect = false;
683
+ queryResultErrors.firstname.eq = {
684
+ expected: `${queryResult.firstname.eq.expected}`,
685
+ received: `${firstnamePassport ?? firstnameIDCard}`,
686
+ message: "Firstname does not match the expected firstname",
687
+ };
688
+ }
689
+ if (queryResult.firstname.disclose &&
690
+ (0, utils_1.formatName)(queryResult.firstname.disclose.result).toLowerCase() !==
691
+ firstnamePassport.toLowerCase() &&
692
+ (0, utils_1.formatName)(queryResult.firstname.disclose.result).toLowerCase() !==
693
+ firstnameIDCard.toLowerCase()) {
694
+ console.warn("Firstname does not match the disclosed firstname in query result");
695
+ isCorrect = false;
696
+ queryResultErrors.firstname.disclose = {
697
+ expected: `${queryResult.firstname.disclose.result}`,
698
+ received: `${firstnamePassport ?? firstnameIDCard}`,
699
+ message: "Firstname does not match the disclosed firstname in query result",
700
+ };
701
+ }
702
+ }
703
+ if (queryResult.lastname) {
704
+ // If fullname was not revealed, then the name could be either the first name or last name
705
+ const lastnamePassport = disclosedDataPassport.lastName && disclosedDataPassport.lastName.length > 0
706
+ ? disclosedDataPassport.lastName
707
+ : disclosedDataPassport.name;
708
+ const lastnameIDCard = disclosedDataIDCard.lastName && disclosedDataIDCard.lastName.length > 0
709
+ ? disclosedDataIDCard.lastName
710
+ : disclosedDataIDCard.name;
711
+ if (queryResult.lastname.eq &&
712
+ queryResult.lastname.eq.result &&
713
+ (0, utils_1.formatName)(queryResult.lastname.eq.expected).toLowerCase() !==
714
+ lastnamePassport.toLowerCase() &&
715
+ (0, utils_1.formatName)(queryResult.lastname.eq.expected).toLowerCase() !== lastnameIDCard.toLowerCase()) {
716
+ console.warn("Lastname does not match the expected lastname");
717
+ isCorrect = false;
718
+ queryResultErrors.lastname.eq = {
719
+ expected: `${queryResult.lastname.eq.expected}`,
720
+ received: `${lastnamePassport ?? lastnameIDCard}`,
721
+ message: "Lastname does not match the expected lastname",
722
+ };
723
+ }
724
+ if (queryResult.lastname.disclose &&
725
+ (0, utils_1.formatName)(queryResult.lastname.disclose.result).toLowerCase() !==
726
+ lastnamePassport.toLowerCase() &&
727
+ (0, utils_1.formatName)(queryResult.lastname.disclose.result).toLowerCase() !==
728
+ lastnameIDCard.toLowerCase()) {
729
+ console.warn("Lastname does not match the disclosed lastname in query result");
730
+ isCorrect = false;
731
+ queryResultErrors.lastname.disclose = {
732
+ expected: `${queryResult.lastname.disclose.result}`,
733
+ received: `${lastnamePassport ?? lastnameIDCard}`,
734
+ message: "Lastname does not match the disclosed lastname in query result",
735
+ };
736
+ }
737
+ }
738
+ return { isCorrect, queryResultErrors };
739
+ }
740
+ checkAgePublicInputs(proof, queryResult) {
422
741
  const queryResultErrors = {
423
742
  sig_check_dsc: {},
424
743
  sig_check_id_data: {},
@@ -435,388 +754,845 @@ class ZKPassport {
435
754
  lastname: {},
436
755
  fullname: {},
437
756
  document_number: {},
757
+ outer: {},
438
758
  };
439
- // Since the order is important for the commitments, we need to sort the proofs
440
- // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
441
- const sortedProofs = proofs.sort((a, b) => {
442
- const proofOrder = [
443
- "sig_check_dsc",
444
- "sig_check_id_data",
445
- "data_check_integrity",
446
- "disclose_bytes",
447
- "compare_age",
448
- "compare_birthdate",
449
- "compare_expiry",
450
- "exclusion_check_nationality",
451
- "inclusion_check_nationality",
452
- "exclusion_check_issuing_country",
453
- "inclusion_check_issuing_country",
454
- ];
455
- const getIndex = (proof) => {
456
- const name = proof.name || "";
457
- return proofOrder.findIndex((p) => name.startsWith(p));
458
- };
459
- return getIndex(a) - getIndex(b);
460
- });
461
- for (const proof of sortedProofs) {
462
- const proofData = (0, utils_1.getProofData)(proof.proof, true);
463
- if (proof.name?.startsWith("sig_check_dsc")) {
464
- commitmentOut = (0, utils_1.getCommitmentFromDSCProof)(proofData);
465
- const merkleRoot = (0, utils_1.getMerkleRootFromDSCProof)(proofData);
466
- if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(merkleRoot)) {
467
- console.warn("The ID was signed by an unrecognized root certificate");
468
- isCorrect = false;
469
- queryResultErrors.sig_check_dsc.certificate = {
470
- expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
471
- received: `Certificate registry root: ${merkleRoot.toString()}`,
472
- message: "The ID was signed by an unrecognized root certificate",
473
- };
474
- }
759
+ let isCorrect = true;
760
+ const currentTime = new Date();
761
+ const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
762
+ const minAge = (0, utils_1.getMinAgeFromCommittedInputs)(proof.committedInputs?.compare_age);
763
+ const maxAge = (0, utils_1.getMaxAgeFromCommittedInputs)(proof.committedInputs?.compare_age);
764
+ if (queryResult.age) {
765
+ if (queryResult.age.gte &&
766
+ queryResult.age.gte.result &&
767
+ minAge < queryResult.age.gte.expected) {
768
+ console.warn("Age is not greater than or equal to the expected age");
769
+ isCorrect = false;
770
+ queryResultErrors.age.gte = {
771
+ expected: queryResult.age.gte.expected,
772
+ received: minAge,
773
+ message: "Age is not greater than or equal to the expected age",
774
+ };
475
775
  }
476
- else if (proof.name?.startsWith("sig_check_id_data")) {
477
- commitmentIn = (0, utils_1.getCommitmentInFromIDDataProof)(proofData);
478
- if (commitmentIn !== commitmentOut) {
479
- console.warn("Failed to check the link between the certificate signature and ID signature");
480
- isCorrect = false;
481
- queryResultErrors.sig_check_id_data.commitment = {
482
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
483
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
484
- message: "Failed to check the link between the certificate signature and ID signature",
485
- };
486
- }
487
- commitmentOut = (0, utils_1.getCommitmentOutFromIDDataProof)(proofData);
776
+ if (queryResult.age.lt &&
777
+ queryResult.age.lt.result &&
778
+ maxAge >= queryResult.age.lt.expected) {
779
+ console.warn("Age is not less than the expected age");
780
+ isCorrect = false;
781
+ queryResultErrors.age.lt = {
782
+ expected: queryResult.age.lt.expected,
783
+ received: maxAge,
784
+ message: "Age is not less than the expected age",
785
+ };
488
786
  }
489
- else if (proof.name?.startsWith("data_check_integrity")) {
490
- commitmentIn = (0, utils_1.getCommitmentInFromIntegrityProof)(proofData);
491
- if (commitmentIn !== commitmentOut) {
492
- console.warn("Failed to check the link between the ID signature and the data signed");
787
+ if (queryResult.age.range) {
788
+ if (queryResult.age.range.result &&
789
+ (minAge < queryResult.age.range.expected[0] ||
790
+ maxAge >= queryResult.age.range.expected[1])) {
791
+ console.warn("Age is not in the expected range");
493
792
  isCorrect = false;
494
- queryResultErrors.data_check_integrity.commitment = {
495
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
496
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
497
- message: "Failed to check the link between the ID signature and the data signed",
793
+ queryResultErrors.age.range = {
794
+ expected: queryResult.age.range.expected,
795
+ received: [minAge, maxAge],
796
+ message: "Age is not in the expected range",
498
797
  };
499
798
  }
500
- commitmentOut = (0, utils_1.getCommitmentOutFromIntegrityProof)(proofData);
501
- const currentDate = (0, utils_1.getCurrentDateFromIntegrityProof)(proofData);
502
- const todayToCurrentDate = today.getTime() - currentDate.getTime();
503
- const differenceInDays = validity ?? 180;
504
- const expectedDifference = differenceInDays * 86400000;
505
- const actualDifference = today.getTime() - (today.getTime() - expectedDifference);
506
- // The ID should not expire within the next 6 months (or whatever the custom value is)
507
- if (todayToCurrentDate >= actualDifference) {
508
- console.warn(`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`);
799
+ }
800
+ if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
801
+ console.warn("Maximum age should be equal to 0");
802
+ isCorrect = false;
803
+ queryResultErrors.age.disclose = {
804
+ expected: 0,
805
+ received: maxAge,
806
+ message: "Maximum age should be equal to 0",
807
+ };
808
+ }
809
+ if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
810
+ console.warn("Minimum age should be equal to 0");
811
+ isCorrect = false;
812
+ queryResultErrors.age.disclose = {
813
+ expected: 0,
814
+ received: minAge,
815
+ message: "Minimum age should be equal to 0",
816
+ };
817
+ }
818
+ if (queryResult.age.disclose &&
819
+ (queryResult.age.disclose.result !== minAge || queryResult.age.disclose.result !== maxAge)) {
820
+ console.warn("Age does not match the disclosed age in query result");
821
+ isCorrect = false;
822
+ queryResultErrors.age.disclose = {
823
+ expected: `${minAge}`,
824
+ received: `${queryResult.age.disclose.result}`,
825
+ message: "Age does not match the disclosed age in query result",
826
+ };
827
+ }
828
+ }
829
+ else {
830
+ console.warn("Age is not set in the query result");
831
+ isCorrect = false;
832
+ queryResultErrors.age.disclose = {
833
+ message: "Age is not set in the query result",
834
+ };
835
+ }
836
+ const currentDate = (0, utils_1.getCurrentDateFromCommittedInputs)(proof.committedInputs?.compare_age);
837
+ if (currentDate.getTime() !== today.getTime() &&
838
+ currentDate.getTime() !== today.getTime() - 86400000) {
839
+ console.warn("Current date in the proof is too old");
840
+ isCorrect = false;
841
+ queryResultErrors.age.disclose = {
842
+ expected: `${today.toISOString()}`,
843
+ received: `${currentDate.toISOString()}`,
844
+ message: "Current date in the proof is too old",
845
+ };
846
+ }
847
+ return { isCorrect, queryResultErrors };
848
+ }
849
+ checkBirthdatePublicInputs(proof, queryResult) {
850
+ const queryResultErrors = {
851
+ sig_check_dsc: {},
852
+ sig_check_id_data: {},
853
+ data_check_integrity: {},
854
+ disclose: {},
855
+ age: {},
856
+ birthdate: {},
857
+ expiry_date: {},
858
+ document_type: {},
859
+ issuing_country: {},
860
+ gender: {},
861
+ nationality: {},
862
+ firstname: {},
863
+ lastname: {},
864
+ fullname: {},
865
+ document_number: {},
866
+ outer: {},
867
+ };
868
+ let isCorrect = true;
869
+ const currentTime = new Date();
870
+ const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0);
871
+ const minDate = (0, utils_1.getMinDateFromCommittedInputs)(proof.committedInputs?.compare_birthdate);
872
+ const maxDate = (0, utils_1.getMaxDateFromCommittedInputs)(proof.committedInputs?.compare_birthdate);
873
+ const currentDate = (0, utils_1.getCurrentDateFromCommittedInputs)(proof.committedInputs?.compare_birthdate);
874
+ if (queryResult.birthdate) {
875
+ if (queryResult.birthdate.gte &&
876
+ queryResult.birthdate.gte.result &&
877
+ minDate < queryResult.birthdate.gte.expected) {
878
+ console.warn("Birthdate is not greater than or equal to the expected birthdate");
879
+ isCorrect = false;
880
+ queryResultErrors.birthdate.gte = {
881
+ expected: queryResult.birthdate.gte.expected,
882
+ received: minDate,
883
+ message: "Birthdate is not greater than or equal to the expected birthdate",
884
+ };
885
+ }
886
+ if (queryResult.birthdate.lte &&
887
+ queryResult.birthdate.lte.result &&
888
+ maxDate > queryResult.birthdate.lte.expected) {
889
+ console.warn("Birthdate is not less than the expected birthdate");
890
+ isCorrect = false;
891
+ queryResultErrors.birthdate.lte = {
892
+ expected: queryResult.birthdate.lte.expected,
893
+ received: maxDate,
894
+ message: "Birthdate is not less than the expected birthdate",
895
+ };
896
+ }
897
+ if (queryResult.birthdate.range) {
898
+ if (queryResult.birthdate.range.result &&
899
+ (minDate < queryResult.birthdate.range.expected[0] ||
900
+ maxDate > queryResult.birthdate.range.expected[1])) {
901
+ console.warn("Birthdate is not in the expected range");
509
902
  isCorrect = false;
510
- queryResultErrors.data_check_integrity.date = {
511
- expected: `Difference: ${differenceInDays} days`,
512
- received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
513
- message: "The date used to check the validity of the ID is older than the validity period",
903
+ queryResultErrors.birthdate.range = {
904
+ expected: queryResult.birthdate.range.expected,
905
+ received: [minDate, maxDate],
906
+ message: "Birthdate is not in the expected range",
514
907
  };
515
908
  }
516
909
  }
517
- else if (proof.name === "disclose_bytes") {
518
- commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
519
- if (commitmentIn !== commitmentOut) {
520
- console.warn("Failed to check the link between the validity of the ID and the data to disclose");
910
+ if (!queryResult.birthdate.lte &&
911
+ !queryResult.birthdate.range &&
912
+ maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()) {
913
+ console.warn("Maximum birthdate should be equal to default date value");
914
+ isCorrect = false;
915
+ queryResultErrors.birthdate.disclose = {
916
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
917
+ received: `${maxDate.toISOString()}`,
918
+ message: "Maximum birthdate should be equal to default date value",
919
+ };
920
+ }
921
+ if (!queryResult.birthdate.gte &&
922
+ !queryResult.birthdate.range &&
923
+ minDate.getTime() != DEFAULT_DATE_VALUE.getTime()) {
924
+ console.warn("Minimum birthdate should be equal to default date value");
925
+ isCorrect = false;
926
+ queryResultErrors.birthdate.disclose = {
927
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
928
+ received: `${minDate.toISOString()}`,
929
+ message: "Minimum birthdate should be equal to default date value",
930
+ };
931
+ }
932
+ }
933
+ else {
934
+ console.warn("Birthdate is not set in the query result");
935
+ isCorrect = false;
936
+ queryResultErrors.birthdate.disclose = {
937
+ message: "Birthdate is not set in the query result",
938
+ };
939
+ }
940
+ if (currentDate.getTime() !== today.getTime() &&
941
+ currentDate.getTime() !== today.getTime() - 86400000) {
942
+ console.warn("Current date in the proof is too old");
943
+ isCorrect = false;
944
+ queryResultErrors.age.disclose = {
945
+ expected: `${today.toISOString()}`,
946
+ received: `${currentDate.toISOString()}`,
947
+ message: "Current date in the proof is too old",
948
+ };
949
+ }
950
+ return { isCorrect, queryResultErrors };
951
+ }
952
+ checkExpiryDatePublicInputs(proof, queryResult) {
953
+ const queryResultErrors = {
954
+ sig_check_dsc: {},
955
+ sig_check_id_data: {},
956
+ data_check_integrity: {},
957
+ disclose: {},
958
+ age: {},
959
+ birthdate: {},
960
+ expiry_date: {},
961
+ document_type: {},
962
+ issuing_country: {},
963
+ gender: {},
964
+ nationality: {},
965
+ firstname: {},
966
+ lastname: {},
967
+ fullname: {},
968
+ document_number: {},
969
+ outer: {},
970
+ };
971
+ let isCorrect = true;
972
+ const currentTime = new Date();
973
+ const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0);
974
+ const minDate = (0, utils_1.getMinDateFromCommittedInputs)(proof.committedInputs?.compare_expiry);
975
+ const maxDate = (0, utils_1.getMaxDateFromCommittedInputs)(proof.committedInputs?.compare_expiry);
976
+ const currentDate = (0, utils_1.getCurrentDateFromCommittedInputs)(proof.committedInputs?.compare_expiry);
977
+ if (queryResult.expiry_date) {
978
+ if (queryResult.expiry_date.gte &&
979
+ queryResult.expiry_date.gte.result &&
980
+ minDate < queryResult.expiry_date.gte.expected) {
981
+ console.warn("Expiry date is not greater than or equal to the expected expiry date");
982
+ isCorrect = false;
983
+ queryResultErrors.expiry_date.gte = {
984
+ expected: queryResult.expiry_date.gte.expected,
985
+ received: minDate,
986
+ message: "Expiry date is not greater than or equal to the expected expiry date",
987
+ };
988
+ }
989
+ if (queryResult.expiry_date.lte &&
990
+ queryResult.expiry_date.lte.result &&
991
+ maxDate > queryResult.expiry_date.lte.expected) {
992
+ console.warn("Expiry date is not less than the expected expiry date");
993
+ isCorrect = false;
994
+ queryResultErrors.expiry_date.lte = {
995
+ expected: queryResult.expiry_date.lte.expected,
996
+ received: maxDate,
997
+ message: "Expiry date is not less than the expected expiry date",
998
+ };
999
+ }
1000
+ if (queryResult.expiry_date.range) {
1001
+ if (queryResult.expiry_date.range.result &&
1002
+ (minDate < queryResult.expiry_date.range.expected[0] ||
1003
+ maxDate > queryResult.expiry_date.range.expected[1])) {
1004
+ console.warn("Expiry date is not in the expected range");
521
1005
  isCorrect = false;
522
- queryResultErrors.disclose.commitment = {
523
- expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
524
- received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
525
- message: "Failed to check the link between the validity of the ID and the data to disclose",
1006
+ queryResultErrors.expiry_date.range = {
1007
+ expected: queryResult.expiry_date.range.expected,
1008
+ received: [minDate, maxDate],
1009
+ message: "Expiry date is not in the expected range",
526
1010
  };
527
1011
  }
528
- // We can't be certain that the disclosed data is for a passport or an ID card
529
- // so we need to check both (unless the document type is revealed)
530
- const disclosedDataPassport = utils_1.DisclosedData.fromBytesProof(proofData, "passport");
531
- const disclosedDataIDCard = utils_1.DisclosedData.fromBytesProof(proofData, "id_card");
532
- if (queryResult.document_type) {
533
- // Document type is always at the same index in the disclosed data
534
- if (queryResult.document_type.eq &&
535
- queryResult.document_type.eq.result &&
536
- queryResult.document_type.eq.expected !== disclosedDataPassport.documentType) {
537
- console.warn("Document type does not match the expected document type");
538
- isCorrect = false;
539
- queryResultErrors.document_type.eq = {
540
- expected: `${queryResult.document_type.eq.expected}`,
541
- received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
542
- message: "Document type does not match the expected document type",
543
- };
544
- }
545
- if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
546
- console.warn("Document type does not match the disclosed document type in query result");
547
- isCorrect = false;
548
- queryResultErrors.document_type.disclose = {
549
- expected: `${queryResult.document_type.disclose?.result}`,
550
- received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
551
- message: "Document type does not match the disclosed document type in query result",
552
- };
553
- }
554
- }
555
- if (queryResult.birthdate) {
556
- const birthdatePassport = disclosedDataPassport.dateOfBirth;
557
- const birthdateIDCard = disclosedDataIDCard.dateOfBirth;
558
- if (queryResult.birthdate.eq &&
559
- queryResult.birthdate.eq.result &&
560
- queryResult.birthdate.eq.expected.getTime() !== birthdatePassport.getTime() &&
561
- queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()) {
562
- console.warn("Birthdate does not match the expected birthdate");
563
- isCorrect = false;
564
- queryResultErrors.birthdate.eq = {
565
- expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
566
- received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
567
- message: "Birthdate does not match the expected birthdate",
568
- };
569
- }
570
- if (queryResult.birthdate.disclose &&
571
- queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
572
- queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()) {
573
- console.warn("Birthdate does not match the disclosed birthdate in query result");
574
- isCorrect = false;
575
- queryResultErrors.birthdate.disclose = {
576
- expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
577
- received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
578
- message: "Birthdate does not match the disclosed birthdate in query result",
579
- };
580
- }
581
- }
582
- if (queryResult.expiry_date) {
583
- const expiryDatePassport = disclosedDataPassport.dateOfExpiry;
584
- const expiryDateIDCard = disclosedDataIDCard.dateOfExpiry;
585
- if (queryResult.expiry_date.eq &&
586
- queryResult.expiry_date.eq.result &&
587
- queryResult.expiry_date.eq.expected.getTime() !== expiryDatePassport.getTime() &&
588
- queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()) {
589
- console.warn("Expiry date does not match the expected expiry date");
590
- isCorrect = false;
591
- queryResultErrors.expiry_date.eq = {
592
- expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
593
- received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
594
- message: "Expiry date does not match the expected expiry date",
595
- };
596
- }
597
- if (queryResult.expiry_date.disclose &&
598
- queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
599
- queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()) {
600
- console.warn("Expiry date does not match the disclosed expiry date in query result");
601
- isCorrect = false;
602
- queryResultErrors.expiry_date.disclose = {
603
- expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
604
- received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
605
- message: "Expiry date does not match the disclosed expiry date in query result",
606
- };
607
- }
608
- }
609
- if (queryResult.nationality) {
610
- const nationalityPassport = disclosedDataPassport.nationality;
611
- const nationalityIDCard = disclosedDataIDCard.nationality;
612
- if (queryResult.nationality.eq &&
613
- queryResult.nationality.eq.result &&
614
- queryResult.nationality.eq.expected !== nationalityPassport &&
615
- queryResult.nationality.eq.expected !== nationalityIDCard) {
616
- console.warn("Nationality does not match the expected nationality");
617
- isCorrect = false;
618
- queryResultErrors.nationality.eq = {
619
- expected: `${queryResult.nationality.eq.expected}`,
620
- received: `${nationalityPassport ?? nationalityIDCard}`,
621
- message: "Nationality does not match the expected nationality",
622
- };
623
- }
624
- if (queryResult.nationality.disclose &&
625
- queryResult.nationality.disclose.result !== nationalityPassport &&
626
- queryResult.nationality.disclose.result !== nationalityIDCard) {
627
- console.warn("Nationality does not match the disclosed nationality in query result");
628
- isCorrect = false;
629
- queryResultErrors.nationality.disclose = {
630
- expected: `${queryResult.nationality.disclose.result}`,
631
- received: `${nationalityPassport ?? nationalityIDCard}`,
632
- message: "Nationality does not match the disclosed nationality in query result",
633
- };
634
- }
1012
+ }
1013
+ if (!queryResult.expiry_date.lte &&
1014
+ !queryResult.expiry_date.range &&
1015
+ maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()) {
1016
+ console.warn("Maximum expiry date should be equal to default date value");
1017
+ isCorrect = false;
1018
+ queryResultErrors.expiry_date.disclose = {
1019
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1020
+ received: `${maxDate.toISOString()}`,
1021
+ message: "Maximum expiry date should be equal to default date value",
1022
+ };
1023
+ }
1024
+ if (!queryResult.expiry_date.gte &&
1025
+ !queryResult.expiry_date.range &&
1026
+ minDate.getTime() != DEFAULT_DATE_VALUE.getTime()) {
1027
+ console.warn("Minimum expiry date should be equal to default date value");
1028
+ isCorrect = false;
1029
+ queryResultErrors.expiry_date.disclose = {
1030
+ expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
1031
+ received: `${minDate.toISOString()}`,
1032
+ message: "Minimum expiry date should be equal to default date value",
1033
+ };
1034
+ }
1035
+ }
1036
+ else {
1037
+ console.warn("Expiry date is not set in the query result");
1038
+ isCorrect = false;
1039
+ queryResultErrors.expiry_date.disclose = {
1040
+ message: "Expiry date is not set in the query result",
1041
+ };
1042
+ }
1043
+ if (currentDate.getTime() !== today.getTime() &&
1044
+ currentDate.getTime() !== today.getTime() - 86400000) {
1045
+ console.warn("Current date in the proof is too old");
1046
+ isCorrect = false;
1047
+ queryResultErrors.age.disclose = {
1048
+ expected: `${today.toISOString()}`,
1049
+ received: `${currentDate.toISOString()}`,
1050
+ message: "Current date in the proof is too old",
1051
+ };
1052
+ }
1053
+ return { isCorrect, queryResultErrors };
1054
+ }
1055
+ checkNationalityExclusionPublicInputs(queryResult, countryList) {
1056
+ const queryResultErrors = {
1057
+ sig_check_dsc: {},
1058
+ sig_check_id_data: {},
1059
+ data_check_integrity: {},
1060
+ disclose: {},
1061
+ age: {},
1062
+ birthdate: {},
1063
+ expiry_date: {},
1064
+ document_type: {},
1065
+ issuing_country: {},
1066
+ gender: {},
1067
+ nationality: {},
1068
+ firstname: {},
1069
+ lastname: {},
1070
+ fullname: {},
1071
+ document_number: {},
1072
+ outer: {},
1073
+ };
1074
+ let isCorrect = true;
1075
+ if (queryResult.nationality &&
1076
+ queryResult.nationality.out &&
1077
+ queryResult.nationality.out.result) {
1078
+ if (!queryResult.nationality.out.expected?.every((country) => countryList.includes(country))) {
1079
+ console.warn("Nationality exclusion list does not match the one from the query results");
1080
+ isCorrect = false;
1081
+ queryResultErrors.nationality.out = {
1082
+ expected: queryResult.nationality.out.expected,
1083
+ received: countryList,
1084
+ message: "Nationality exclusion list does not match the one from the query results",
1085
+ };
1086
+ }
1087
+ }
1088
+ else if (!queryResult.nationality || !queryResult.nationality.out) {
1089
+ console.warn("Nationality exclusion is not set in the query result");
1090
+ isCorrect = false;
1091
+ queryResultErrors.nationality.out = {
1092
+ message: "Nationality exclusion is not set in the query result",
1093
+ };
1094
+ }
1095
+ // Check the countryList is in ascending order
1096
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1097
+ // as it is requirement in the circuit for the exclusion check to work
1098
+ for (let i = 1; i < countryList.length; i++) {
1099
+ if (countryList[i] < countryList[i - 1]) {
1100
+ console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
1101
+ isCorrect = false;
1102
+ queryResultErrors.nationality.out = {
1103
+ message: "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1104
+ };
1105
+ }
1106
+ }
1107
+ return { isCorrect, queryResultErrors };
1108
+ }
1109
+ checkIssuingCountryExclusionPublicInputs(queryResult, countryList) {
1110
+ const queryResultErrors = {
1111
+ sig_check_dsc: {},
1112
+ sig_check_id_data: {},
1113
+ data_check_integrity: {},
1114
+ disclose: {},
1115
+ age: {},
1116
+ birthdate: {},
1117
+ expiry_date: {},
1118
+ document_type: {},
1119
+ issuing_country: {},
1120
+ gender: {},
1121
+ nationality: {},
1122
+ firstname: {},
1123
+ lastname: {},
1124
+ fullname: {},
1125
+ document_number: {},
1126
+ outer: {},
1127
+ };
1128
+ let isCorrect = true;
1129
+ if (queryResult.issuing_country &&
1130
+ queryResult.issuing_country.out &&
1131
+ queryResult.issuing_country.out.result) {
1132
+ if (!queryResult.issuing_country.out.expected?.every((country) => countryList.includes(country))) {
1133
+ console.warn("Issuing country exclusion list does not match the one from the query results");
1134
+ isCorrect = false;
1135
+ queryResultErrors.issuing_country.out = {
1136
+ expected: queryResult.issuing_country.out.expected,
1137
+ received: countryList,
1138
+ message: "Issuing country exclusion list does not match the one from the query results",
1139
+ };
1140
+ }
1141
+ }
1142
+ else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1143
+ console.warn("Issuing country exclusion is not set in the query result");
1144
+ isCorrect = false;
1145
+ queryResultErrors.issuing_country.out = {
1146
+ message: "Issuing country exclusion is not set in the query result",
1147
+ };
1148
+ }
1149
+ // Check the countryList is in ascending order
1150
+ // If the prover doesn't use a sorted list then the proof cannot be trusted
1151
+ // as it is requirement in the circuit for the exclusion check to work
1152
+ for (let i = 1; i < countryList.length; i++) {
1153
+ if (countryList[i] < countryList[i - 1]) {
1154
+ console.warn("The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted");
1155
+ isCorrect = false;
1156
+ queryResultErrors.issuing_country.out = {
1157
+ message: "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1158
+ };
1159
+ }
1160
+ }
1161
+ return { isCorrect, queryResultErrors };
1162
+ }
1163
+ checkNationalityInclusionPublicInputs(queryResult, countryList) {
1164
+ const queryResultErrors = {
1165
+ sig_check_dsc: {},
1166
+ sig_check_id_data: {},
1167
+ data_check_integrity: {},
1168
+ disclose: {},
1169
+ age: {},
1170
+ birthdate: {},
1171
+ expiry_date: {},
1172
+ document_type: {},
1173
+ issuing_country: {},
1174
+ gender: {},
1175
+ nationality: {},
1176
+ firstname: {},
1177
+ lastname: {},
1178
+ fullname: {},
1179
+ document_number: {},
1180
+ outer: {},
1181
+ };
1182
+ let isCorrect = true;
1183
+ if (queryResult.nationality &&
1184
+ queryResult.nationality.in &&
1185
+ queryResult.nationality.in.result) {
1186
+ if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
1187
+ console.warn("Nationality inclusion list does not match the one from the query results");
1188
+ isCorrect = false;
1189
+ queryResultErrors.nationality.in = {
1190
+ expected: queryResult.nationality.in.expected,
1191
+ received: countryList,
1192
+ message: "Nationality inclusion list does not match the one from the query results",
1193
+ };
1194
+ }
1195
+ }
1196
+ else if (!queryResult.nationality || !queryResult.nationality.in) {
1197
+ console.warn("Nationality inclusion is not set in the query result");
1198
+ isCorrect = false;
1199
+ queryResultErrors.nationality.in = {
1200
+ message: "Nationality inclusion is not set in the query result",
1201
+ };
1202
+ }
1203
+ return { isCorrect, queryResultErrors };
1204
+ }
1205
+ checkIssuingCountryInclusionPublicInputs(queryResult, countryList) {
1206
+ const queryResultErrors = {
1207
+ sig_check_dsc: {},
1208
+ sig_check_id_data: {},
1209
+ data_check_integrity: {},
1210
+ disclose: {},
1211
+ age: {},
1212
+ birthdate: {},
1213
+ expiry_date: {},
1214
+ document_type: {},
1215
+ issuing_country: {},
1216
+ gender: {},
1217
+ nationality: {},
1218
+ firstname: {},
1219
+ lastname: {},
1220
+ fullname: {},
1221
+ document_number: {},
1222
+ outer: {},
1223
+ };
1224
+ let isCorrect = true;
1225
+ if (queryResult.issuing_country &&
1226
+ queryResult.issuing_country.in &&
1227
+ queryResult.issuing_country.in.result) {
1228
+ if (!queryResult.issuing_country.in.expected?.every((country) => countryList.includes(country))) {
1229
+ console.warn("Issuing country inclusion list does not match the one from the query results");
1230
+ isCorrect = false;
1231
+ queryResultErrors.issuing_country.in = {
1232
+ expected: queryResult.issuing_country.in.expected,
1233
+ received: countryList,
1234
+ message: "Issuing country inclusion list does not match the one from the query results",
1235
+ };
1236
+ }
1237
+ }
1238
+ else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1239
+ console.warn("Issuing country inclusion is not set in the query result");
1240
+ isCorrect = false;
1241
+ queryResultErrors.issuing_country.in = {
1242
+ message: "Issuing country inclusion is not set in the query result",
1243
+ };
1244
+ }
1245
+ return { isCorrect, queryResultErrors };
1246
+ }
1247
+ async checkPublicInputs(proofs, queryResult, validity) {
1248
+ let commitmentIn;
1249
+ let commitmentOut;
1250
+ let isCorrect = true;
1251
+ let uniqueIdentifier;
1252
+ const VALID_CERTIFICATE_REGISTRY_ROOT = [
1253
+ BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
1254
+ BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
1255
+ BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
1256
+ ];
1257
+ const currentTime = new Date();
1258
+ const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
1259
+ let queryResultErrors = {
1260
+ sig_check_dsc: {},
1261
+ sig_check_id_data: {},
1262
+ data_check_integrity: {},
1263
+ disclose: {},
1264
+ age: {},
1265
+ birthdate: {},
1266
+ expiry_date: {},
1267
+ document_type: {},
1268
+ issuing_country: {},
1269
+ gender: {},
1270
+ nationality: {},
1271
+ firstname: {},
1272
+ lastname: {},
1273
+ fullname: {},
1274
+ document_number: {},
1275
+ outer: {},
1276
+ };
1277
+ // Since the order is important for the commitments, we need to sort the proofs
1278
+ // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
1279
+ const sortedProofs = proofs.sort((a, b) => {
1280
+ const proofOrder = [
1281
+ "sig_check_dsc",
1282
+ "sig_check_id_data",
1283
+ "data_check_integrity",
1284
+ "disclose_bytes",
1285
+ "compare_age",
1286
+ "compare_birthdate",
1287
+ "compare_expiry",
1288
+ "exclusion_check_nationality",
1289
+ "inclusion_check_nationality",
1290
+ "exclusion_check_issuing_country",
1291
+ "inclusion_check_issuing_country",
1292
+ ];
1293
+ const getIndex = (proof) => {
1294
+ const name = proof.name || "";
1295
+ return proofOrder.findIndex((p) => name.startsWith(p));
1296
+ };
1297
+ return getIndex(a) - getIndex(b);
1298
+ });
1299
+ for (const proof of sortedProofs) {
1300
+ const proofData = (0, utils_1.getProofData)(proof.proof, (0, utils_1.getNumberOfPublicInputs)(proof.name));
1301
+ if (proof.name?.startsWith("outer")) {
1302
+ const isForEVM = proof.name?.startsWith("outer_evm");
1303
+ const certificateRegistryRoot = (0, utils_1.getCertificateRegistryRootFromOuterProof)(proofData);
1304
+ if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(certificateRegistryRoot)) {
1305
+ console.warn("The ID was signed by an unrecognized root certificate");
1306
+ isCorrect = false;
1307
+ queryResultErrors.outer.certificate = {
1308
+ expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
1309
+ received: `Certificate registry root: ${certificateRegistryRoot.toString()}`,
1310
+ message: "The ID was signed by an unrecognized root certificate",
1311
+ };
635
1312
  }
636
- if (queryResult.document_number) {
637
- const documentNumberPassport = disclosedDataPassport.documentNumber;
638
- const documentNumberIDCard = disclosedDataIDCard.documentNumber;
639
- if (queryResult.document_number.eq &&
640
- queryResult.document_number.eq.result &&
641
- queryResult.document_number.eq.expected !== documentNumberPassport &&
642
- queryResult.document_number.eq.expected !== documentNumberIDCard) {
643
- console.warn("Document number does not match the expected document number");
644
- isCorrect = false;
645
- queryResultErrors.document_number.eq = {
646
- expected: `${queryResult.document_number.eq.expected}`,
647
- received: `${documentNumberPassport ?? documentNumberIDCard}`,
648
- message: "Document number does not match the expected document number",
649
- };
650
- }
651
- if (queryResult.document_number.disclose &&
652
- queryResult.document_number.disclose.result !== documentNumberPassport &&
653
- queryResult.document_number.disclose.result !== documentNumberIDCard) {
654
- console.warn("Document number does not match the disclosed document number in query result");
655
- isCorrect = false;
656
- queryResultErrors.document_number.disclose = {
657
- expected: `${queryResult.document_number.disclose.result}`,
658
- received: `${documentNumberPassport ?? documentNumberIDCard}`,
659
- message: "Document number does not match the disclosed document number in query result",
660
- };
661
- }
1313
+ const currentDate = (0, utils_1.getCurrentDateFromOuterProof)(proofData);
1314
+ const todayToCurrentDate = today.getTime() - currentDate.getTime();
1315
+ const differenceInDays = validity ?? 180;
1316
+ const expectedDifference = differenceInDays * 86400000;
1317
+ const actualDifference = today.getTime() - (today.getTime() - expectedDifference);
1318
+ // The ID should not expire within the next 6 months (or whatever the custom value is)
1319
+ if (todayToCurrentDate >= actualDifference) {
1320
+ console.warn(`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`);
1321
+ isCorrect = false;
1322
+ queryResultErrors.outer.date = {
1323
+ expected: `Difference: ${differenceInDays} days`,
1324
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
1325
+ message: "The date used to check the validity of the ID is older than the validity period",
1326
+ };
662
1327
  }
663
- if (queryResult.gender) {
664
- const genderPassport = disclosedDataPassport.gender;
665
- const genderIDCard = disclosedDataIDCard.gender;
666
- if (queryResult.gender.eq &&
667
- queryResult.gender.eq.result &&
668
- queryResult.gender.eq.expected !== genderPassport &&
669
- queryResult.gender.eq.expected !== genderIDCard) {
670
- console.warn("Gender does not match the expected gender");
671
- isCorrect = false;
672
- queryResultErrors.gender.eq = {
673
- expected: `${queryResult.gender.eq.expected}`,
674
- received: `${genderPassport ?? genderIDCard}`,
675
- message: "Gender does not match the expected gender",
676
- };
677
- }
678
- if (queryResult.gender.disclose &&
679
- queryResult.gender.disclose.result !== genderPassport &&
680
- queryResult.gender.disclose.result !== genderIDCard) {
681
- console.warn("Gender does not match the disclosed gender in query result");
682
- isCorrect = false;
683
- queryResultErrors.gender.disclose = {
684
- expected: `${queryResult.gender.disclose.result}`,
685
- received: `${genderPassport ?? genderIDCard}`,
686
- message: "Gender does not match the disclosed gender in query result",
687
- };
688
- }
1328
+ const paramCommitments = (0, utils_1.getParamCommitmentsFromOuterProof)(proofData);
1329
+ const committedInputs = proof.committedInputs;
1330
+ const keysInCommittedInputs = Object.keys(committedInputs || {});
1331
+ if (keysInCommittedInputs.length !== paramCommitments.length) {
1332
+ console.warn("The proof does not verify all the requested conditions and information");
1333
+ isCorrect = false;
1334
+ queryResultErrors.outer.commitment = {
1335
+ expected: `Number of parameter commitments: ${paramCommitments.length}`,
1336
+ received: `Number of disclosure proofs provided: ${keysInCommittedInputs.length}`,
1337
+ message: "The proof does not verify all the requested conditions and information",
1338
+ };
689
1339
  }
690
- if (queryResult.issuing_country) {
691
- const issuingCountryPassport = disclosedDataPassport.issuingCountry;
692
- const issuingCountryIDCard = disclosedDataIDCard.issuingCountry;
693
- if (queryResult.issuing_country.eq &&
694
- queryResult.issuing_country.eq.result &&
695
- queryResult.issuing_country.eq.expected !== issuingCountryPassport &&
696
- queryResult.issuing_country.eq.expected !== issuingCountryIDCard) {
697
- console.warn("Issuing country does not match the expected issuing country");
1340
+ if (!!committedInputs?.compare_age) {
1341
+ const ageCommittedInputs = committedInputs?.compare_age;
1342
+ const ageParameterCommitment = isForEVM
1343
+ ? await (0, utils_1.getAgeEVMParameterCommitment)(ageCommittedInputs.currentDate, ageCommittedInputs.minAge, ageCommittedInputs.maxAge)
1344
+ : await (0, utils_1.getAgeParameterCommitment)(ageCommittedInputs.currentDate, ageCommittedInputs.minAge, ageCommittedInputs.maxAge);
1345
+ if (!paramCommitments.includes(ageParameterCommitment)) {
1346
+ console.warn("This proof does not verify the age");
698
1347
  isCorrect = false;
699
- queryResultErrors.issuing_country.eq = {
700
- expected: `${queryResult.issuing_country.eq.expected}`,
701
- received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
702
- message: "Issuing country does not match the expected issuing country",
1348
+ queryResultErrors.age.commitment = {
1349
+ expected: `Age parameter commitment: ${ageParameterCommitment.toString()}`,
1350
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1351
+ message: "This proof does not verify the age",
703
1352
  };
704
1353
  }
705
- if (queryResult.issuing_country.disclose &&
706
- queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
707
- queryResult.issuing_country.disclose.result !== issuingCountryIDCard) {
708
- console.warn("Issuing country does not match the disclosed issuing country in query result");
1354
+ const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } = this.checkAgePublicInputs(proof, queryResult);
1355
+ isCorrect = isCorrect && isCorrectAge;
1356
+ queryResultErrors = {
1357
+ ...queryResultErrors,
1358
+ ...queryResultErrorsAge,
1359
+ };
1360
+ }
1361
+ else if (!!committedInputs?.compare_birthdate) {
1362
+ const birthdateCommittedInputs = committedInputs?.compare_birthdate;
1363
+ const birthdateParameterCommitment = isForEVM
1364
+ ? await (0, utils_1.getDateEVMParameterCommitment)(utils_1.ProofType.BIRTHDATE, birthdateCommittedInputs.currentDate, birthdateCommittedInputs.minDate, birthdateCommittedInputs.maxDate)
1365
+ : await (0, utils_1.getDateParameterCommitment)(utils_1.ProofType.BIRTHDATE, birthdateCommittedInputs.currentDate, birthdateCommittedInputs.minDate, birthdateCommittedInputs.maxDate);
1366
+ if (!paramCommitments.includes(birthdateParameterCommitment)) {
1367
+ console.warn("This proof does not verify the birthdate");
709
1368
  isCorrect = false;
710
- queryResultErrors.issuing_country.disclose = {
711
- expected: `${queryResult.issuing_country.disclose.result}`,
712
- received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
713
- message: "Issuing country does not match the disclosed issuing country in query result",
1369
+ queryResultErrors.birthdate.commitment = {
1370
+ expected: `Birthdate parameter commitment: ${birthdateParameterCommitment.toString()}`,
1371
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1372
+ message: "This proof does not verify the birthdate",
714
1373
  };
715
1374
  }
1375
+ const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } = this.checkBirthdatePublicInputs(proof, queryResult);
1376
+ isCorrect = isCorrect && isCorrectBirthdate;
1377
+ queryResultErrors = {
1378
+ ...queryResultErrors,
1379
+ ...queryResultErrorsBirthdate,
1380
+ };
716
1381
  }
717
- if (queryResult.fullname) {
718
- const fullnamePassport = disclosedDataPassport.name;
719
- const fullnameIDCard = disclosedDataIDCard.name;
720
- if (queryResult.fullname.eq &&
721
- queryResult.fullname.eq.result &&
722
- (0, utils_1.formatName)(queryResult.fullname.eq.expected).toLowerCase() !==
723
- fullnamePassport.toLowerCase() &&
724
- (0, utils_1.formatName)(queryResult.fullname.eq.expected).toLowerCase() !==
725
- fullnameIDCard.toLowerCase()) {
726
- console.warn("Fullname does not match the expected fullname");
1382
+ else if (!!committedInputs?.compare_expiry) {
1383
+ const expiryCommittedInputs = committedInputs?.compare_expiry;
1384
+ const expiryParameterCommitment = isForEVM
1385
+ ? await (0, utils_1.getDateEVMParameterCommitment)(utils_1.ProofType.EXPIRY_DATE, expiryCommittedInputs.currentDate, expiryCommittedInputs.minDate, expiryCommittedInputs.maxDate)
1386
+ : await (0, utils_1.getDateParameterCommitment)(utils_1.ProofType.EXPIRY_DATE, expiryCommittedInputs.currentDate, expiryCommittedInputs.minDate, expiryCommittedInputs.maxDate);
1387
+ if (!paramCommitments.includes(expiryParameterCommitment)) {
1388
+ console.warn("This proof does not verify the expiry date");
727
1389
  isCorrect = false;
728
- queryResultErrors.fullname.eq = {
729
- expected: `${queryResult.fullname.eq.expected}`,
730
- received: `${fullnamePassport ?? fullnameIDCard}`,
731
- message: "Fullname does not match the expected fullname",
1390
+ queryResultErrors.expiry_date.commitment = {
1391
+ expected: `Expiry date parameter commitment: ${expiryParameterCommitment.toString()}`,
1392
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1393
+ message: "This proof does not verify the expiry date",
732
1394
  };
733
1395
  }
734
- if (queryResult.fullname.disclose &&
735
- (0, utils_1.formatName)(queryResult.fullname.disclose.result).toLowerCase() !==
736
- fullnamePassport.toLowerCase() &&
737
- (0, utils_1.formatName)(queryResult.fullname.disclose.result).toLowerCase() !==
738
- fullnameIDCard.toLowerCase()) {
739
- console.warn("Fullname does not match the disclosed fullname in query result");
1396
+ const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } = this.checkExpiryDatePublicInputs(proof, queryResult);
1397
+ isCorrect = isCorrect && isCorrectExpiryDate;
1398
+ queryResultErrors = {
1399
+ ...queryResultErrors,
1400
+ ...queryResultErrorsExpiryDate,
1401
+ };
1402
+ }
1403
+ else if (!!committedInputs?.disclose_bytes) {
1404
+ const discloseCommittedInputs = committedInputs?.disclose_bytes;
1405
+ const discloseParameterCommitment = isForEVM
1406
+ ? await (0, utils_1.getDiscloseEVMParameterCommitment)(discloseCommittedInputs.discloseMask, discloseCommittedInputs.disclosedBytes)
1407
+ : await (0, utils_1.getDiscloseParameterCommitment)(discloseCommittedInputs.discloseMask, discloseCommittedInputs.disclosedBytes);
1408
+ if (!paramCommitments.includes(discloseParameterCommitment)) {
1409
+ console.warn("This proof does not verify any of the data disclosed");
740
1410
  isCorrect = false;
741
- queryResultErrors.fullname.disclose = {
742
- expected: `${queryResult.fullname.disclose.result}`,
743
- received: `${fullnamePassport ?? fullnameIDCard}`,
744
- message: "Fullname does not match the disclosed fullname in query result",
1411
+ queryResultErrors.disclose.commitment = {
1412
+ expected: `Disclosure parameter commitment: ${discloseParameterCommitment.toString()}`,
1413
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1414
+ message: "This proof does not verify any of the data disclosed",
745
1415
  };
746
1416
  }
1417
+ const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } = this.checkDiscloseBytesPublicInputs(proof, queryResult);
1418
+ isCorrect = isCorrect && isCorrectDisclose;
1419
+ queryResultErrors = {
1420
+ ...queryResultErrors,
1421
+ ...queryResultErrorsDisclose,
1422
+ };
747
1423
  }
748
- if (queryResult.firstname) {
749
- // If fullname was not revealed, then the name could be either the first name or last name
750
- const firstnamePassport = disclosedDataPassport.firstName && disclosedDataPassport.firstName.length > 0
751
- ? disclosedDataPassport.firstName
752
- : disclosedDataPassport.name;
753
- const firstnameIDCard = disclosedDataIDCard.firstName && disclosedDataIDCard.firstName.length > 0
754
- ? disclosedDataIDCard.firstName
755
- : disclosedDataIDCard.name;
756
- if (queryResult.firstname.eq &&
757
- queryResult.firstname.eq.result &&
758
- (0, utils_1.formatName)(queryResult.firstname.eq.expected).toLowerCase() !==
759
- firstnamePassport.toLowerCase() &&
760
- (0, utils_1.formatName)(queryResult.firstname.eq.expected).toLowerCase() !==
761
- firstnameIDCard.toLowerCase()) {
762
- console.warn("Firstname does not match the expected firstname");
1424
+ else if (!!committedInputs?.inclusion_check_nationality) {
1425
+ const inclusionCheckNationalityCommittedInputs = committedInputs?.inclusion_check_nationality;
1426
+ const inclusionCheckNationalityParameterCommitment = isForEVM
1427
+ ? await (0, utils_1.getCountryEVMParameterCommitment)(utils_1.ProofType.NATIONALITY_INCLUSION, inclusionCheckNationalityCommittedInputs.countries)
1428
+ : await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.NATIONALITY_INCLUSION, inclusionCheckNationalityCommittedInputs.countries);
1429
+ if (!paramCommitments.includes(inclusionCheckNationalityParameterCommitment)) {
1430
+ console.warn("This proof does not verify the inclusion of the nationality");
763
1431
  isCorrect = false;
764
- queryResultErrors.firstname.eq = {
765
- expected: `${queryResult.firstname.eq.expected}`,
766
- received: `${firstnamePassport ?? firstnameIDCard}`,
767
- message: "Firstname does not match the expected firstname",
1432
+ queryResultErrors.nationality.commitment = {
1433
+ expected: `Nationality parameter commitment: ${inclusionCheckNationalityParameterCommitment.toString()}`,
1434
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1435
+ message: "This proof does not verify the inclusion of the nationality",
768
1436
  };
769
1437
  }
770
- if (queryResult.firstname.disclose &&
771
- (0, utils_1.formatName)(queryResult.firstname.disclose.result).toLowerCase() !==
772
- firstnamePassport.toLowerCase() &&
773
- (0, utils_1.formatName)(queryResult.firstname.disclose.result).toLowerCase() !==
774
- firstnameIDCard.toLowerCase()) {
775
- console.warn("Firstname does not match the disclosed firstname in query result");
1438
+ const countryList = inclusionCheckNationalityCommittedInputs.countries;
1439
+ const { isCorrect: isCorrectNationalityInclusion, queryResultErrors: queryResultErrorsNationalityInclusion, } = this.checkNationalityInclusionPublicInputs(queryResult, countryList);
1440
+ isCorrect = isCorrect && isCorrectNationalityInclusion;
1441
+ queryResultErrors = {
1442
+ ...queryResultErrors,
1443
+ ...queryResultErrorsNationalityInclusion,
1444
+ };
1445
+ }
1446
+ else if (!!committedInputs?.inclusion_check_issuing_country) {
1447
+ const inclusionCheckIssuingCountryCommittedInputs = committedInputs?.inclusion_check_issuing_country;
1448
+ const inclusionCheckIssuingCountryParameterCommitment = isForEVM
1449
+ ? await (0, utils_1.getCountryEVMParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_INCLUSION, inclusionCheckIssuingCountryCommittedInputs.countries)
1450
+ : await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_INCLUSION, inclusionCheckIssuingCountryCommittedInputs.countries);
1451
+ if (!paramCommitments.includes(inclusionCheckIssuingCountryParameterCommitment)) {
1452
+ console.warn("This proof does not verify the inclusion of the issuing country");
776
1453
  isCorrect = false;
777
- queryResultErrors.firstname.disclose = {
778
- expected: `${queryResult.firstname.disclose.result}`,
779
- received: `${firstnamePassport ?? firstnameIDCard}`,
780
- message: "Firstname does not match the disclosed firstname in query result",
1454
+ queryResultErrors.issuing_country.commitment = {
1455
+ expected: `Issuing country parameter commitment: ${inclusionCheckIssuingCountryParameterCommitment.toString()}`,
1456
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1457
+ message: "This proof does not verify the inclusion of the issuing country",
781
1458
  };
782
1459
  }
1460
+ const countryList = inclusionCheckIssuingCountryCommittedInputs.countries;
1461
+ const { isCorrect: isCorrectIssuingCountryInclusion, queryResultErrors: queryResultErrorsIssuingCountryInclusion, } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList);
1462
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion;
1463
+ queryResultErrors = {
1464
+ ...queryResultErrors,
1465
+ ...queryResultErrorsIssuingCountryInclusion,
1466
+ };
783
1467
  }
784
- if (queryResult.lastname) {
785
- // If fullname was not revealed, then the name could be either the first name or last name
786
- const lastnamePassport = disclosedDataPassport.lastName && disclosedDataPassport.lastName.length > 0
787
- ? disclosedDataPassport.lastName
788
- : disclosedDataPassport.name;
789
- const lastnameIDCard = disclosedDataIDCard.lastName && disclosedDataIDCard.lastName.length > 0
790
- ? disclosedDataIDCard.lastName
791
- : disclosedDataIDCard.name;
792
- if (queryResult.lastname.eq &&
793
- queryResult.lastname.eq.result &&
794
- (0, utils_1.formatName)(queryResult.lastname.eq.expected).toLowerCase() !==
795
- lastnamePassport.toLowerCase() &&
796
- (0, utils_1.formatName)(queryResult.lastname.eq.expected).toLowerCase() !==
797
- lastnameIDCard.toLowerCase()) {
798
- console.warn("Lastname does not match the expected lastname");
1468
+ else if (!!committedInputs?.exclusion_check_nationality) {
1469
+ const exclusionCheckNationalityCommittedInputs = committedInputs?.exclusion_check_nationality;
1470
+ const exclusionCheckNationalityParameterCommitment = isForEVM
1471
+ ? await (0, utils_1.getCountryEVMParameterCommitment)(utils_1.ProofType.NATIONALITY_EXCLUSION, exclusionCheckNationalityCommittedInputs.countries)
1472
+ : await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.NATIONALITY_EXCLUSION, exclusionCheckNationalityCommittedInputs.countries);
1473
+ if (!paramCommitments.includes(exclusionCheckNationalityParameterCommitment)) {
1474
+ console.warn("This proof does not verify the exclusion of the nationality");
799
1475
  isCorrect = false;
800
- queryResultErrors.lastname.eq = {
801
- expected: `${queryResult.lastname.eq.expected}`,
802
- received: `${lastnamePassport ?? lastnameIDCard}`,
803
- message: "Lastname does not match the expected lastname",
1476
+ queryResultErrors.nationality.commitment = {
1477
+ expected: `Nationality parameter commitment: ${exclusionCheckNationalityParameterCommitment.toString()}`,
1478
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1479
+ message: "This proof does not verify the exclusion of the nationality",
804
1480
  };
805
1481
  }
806
- if (queryResult.lastname.disclose &&
807
- (0, utils_1.formatName)(queryResult.lastname.disclose.result).toLowerCase() !==
808
- lastnamePassport.toLowerCase() &&
809
- (0, utils_1.formatName)(queryResult.lastname.disclose.result).toLowerCase() !==
810
- lastnameIDCard.toLowerCase()) {
811
- console.warn("Lastname does not match the disclosed lastname in query result");
1482
+ const countryList = exclusionCheckNationalityCommittedInputs.countries;
1483
+ const { isCorrect: isCorrectNationalityExclusion, queryResultErrors: queryResultErrorsNationalityExclusion, } = this.checkNationalityExclusionPublicInputs(queryResult, countryList);
1484
+ isCorrect = isCorrect && isCorrectNationalityExclusion;
1485
+ queryResultErrors = {
1486
+ ...queryResultErrors,
1487
+ ...queryResultErrorsNationalityExclusion,
1488
+ };
1489
+ }
1490
+ else if (!!committedInputs?.exclusion_check_issuing_country) {
1491
+ const exclusionCheckIssuingCountryCommittedInputs = committedInputs?.exclusion_check_issuing_country;
1492
+ const exclusionCheckIssuingCountryParameterCommitment = isForEVM
1493
+ ? await (0, utils_1.getCountryEVMParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_EXCLUSION, exclusionCheckIssuingCountryCommittedInputs.countries)
1494
+ : await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_EXCLUSION, exclusionCheckIssuingCountryCommittedInputs.countries);
1495
+ if (!paramCommitments.includes(exclusionCheckIssuingCountryParameterCommitment)) {
1496
+ console.warn("This proof does not verify the exclusion of the issuing country");
812
1497
  isCorrect = false;
813
- queryResultErrors.lastname.disclose = {
814
- expected: `${queryResult.lastname.disclose.result}`,
815
- received: `${lastnamePassport ?? lastnameIDCard}`,
816
- message: "Lastname does not match the disclosed lastname in query result",
1498
+ queryResultErrors.issuing_country.commitment = {
1499
+ expected: `Issuing country parameter commitment: ${exclusionCheckIssuingCountryParameterCommitment.toString()}`,
1500
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1501
+ message: "This proof does not verify the exclusion of the issuing country",
817
1502
  };
818
1503
  }
1504
+ const countryList = exclusionCheckIssuingCountryCommittedInputs.countries;
1505
+ const { isCorrect: isCorrectIssuingCountryExclusion, queryResultErrors: queryResultErrorsIssuingCountryExclusion, } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList);
1506
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion;
1507
+ queryResultErrors = {
1508
+ ...queryResultErrors,
1509
+ ...queryResultErrorsIssuingCountryExclusion,
1510
+ };
1511
+ }
1512
+ uniqueIdentifier = (0, utils_1.getNullifierFromOuterProof)(proofData).toString(10);
1513
+ }
1514
+ else if (proof.name?.startsWith("sig_check_dsc")) {
1515
+ commitmentOut = (0, utils_1.getCommitmentFromDSCProof)(proofData);
1516
+ const merkleRoot = (0, utils_1.getMerkleRootFromDSCProof)(proofData);
1517
+ if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(merkleRoot)) {
1518
+ console.warn("The ID was signed by an unrecognized root certificate");
1519
+ isCorrect = false;
1520
+ queryResultErrors.sig_check_dsc.certificate = {
1521
+ expected: `Certificate registry root: ${VALID_CERTIFICATE_REGISTRY_ROOT.join(", ")}`,
1522
+ received: `Certificate registry root: ${merkleRoot.toString()}`,
1523
+ message: "The ID was signed by an unrecognized root certificate",
1524
+ };
1525
+ }
1526
+ }
1527
+ else if (proof.name?.startsWith("sig_check_id_data")) {
1528
+ commitmentIn = (0, utils_1.getCommitmentInFromIDDataProof)(proofData);
1529
+ if (commitmentIn !== commitmentOut) {
1530
+ console.warn("Failed to check the link between the certificate signature and ID signature");
1531
+ isCorrect = false;
1532
+ queryResultErrors.sig_check_id_data.commitment = {
1533
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
1534
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
1535
+ message: "Failed to check the link between the certificate signature and ID signature",
1536
+ };
1537
+ }
1538
+ commitmentOut = (0, utils_1.getCommitmentOutFromIDDataProof)(proofData);
1539
+ }
1540
+ else if (proof.name?.startsWith("data_check_integrity")) {
1541
+ commitmentIn = (0, utils_1.getCommitmentInFromIntegrityProof)(proofData);
1542
+ if (commitmentIn !== commitmentOut) {
1543
+ console.warn("Failed to check the link between the ID signature and the data signed");
1544
+ isCorrect = false;
1545
+ queryResultErrors.data_check_integrity.commitment = {
1546
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
1547
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
1548
+ message: "Failed to check the link between the ID signature and the data signed",
1549
+ };
1550
+ }
1551
+ commitmentOut = (0, utils_1.getCommitmentOutFromIntegrityProof)(proofData);
1552
+ const currentDate = (0, utils_1.getCurrentDateFromIntegrityProof)(proofData);
1553
+ const todayToCurrentDate = today.getTime() - currentDate.getTime();
1554
+ const differenceInDays = validity ?? 180;
1555
+ const expectedDifference = differenceInDays * 86400000;
1556
+ const actualDifference = today.getTime() - (today.getTime() - expectedDifference);
1557
+ // The ID should not expire within the next 6 months (or whatever the custom value is)
1558
+ if (todayToCurrentDate >= actualDifference) {
1559
+ console.warn(`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`);
1560
+ isCorrect = false;
1561
+ queryResultErrors.data_check_integrity.date = {
1562
+ expected: `Difference: ${differenceInDays} days`,
1563
+ received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
1564
+ message: "The date used to check the validity of the ID is older than the validity period",
1565
+ };
1566
+ }
1567
+ }
1568
+ else if (proof.name === "disclose_bytes") {
1569
+ commitmentIn = (0, utils_1.getCommitmentInFromDisclosureProof)(proofData);
1570
+ if (commitmentIn !== commitmentOut) {
1571
+ console.warn("Failed to check the link between the validity of the ID and the data to disclose");
1572
+ isCorrect = false;
1573
+ queryResultErrors.disclose.commitment = {
1574
+ expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
1575
+ received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
1576
+ message: "Failed to check the link between the validity of the ID and the data to disclose",
1577
+ };
1578
+ }
1579
+ const paramCommitment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1580
+ const calculatedParamCommitment = await (0, utils_1.getDiscloseParameterCommitment)((proof.committedInputs?.disclose_bytes).discloseMask, (proof.committedInputs?.disclose_bytes).disclosedBytes);
1581
+ if (paramCommitment !== calculatedParamCommitment) {
1582
+ console.warn("The disclosed data does not match the data committed by the proof");
1583
+ isCorrect = false;
1584
+ queryResultErrors.disclose.commitment = {
1585
+ expected: `Commitment: ${calculatedParamCommitment}`,
1586
+ received: `Commitment: ${paramCommitment}`,
1587
+ message: "The disclosed data does not match the data committed by the proof",
1588
+ };
819
1589
  }
1590
+ const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } = this.checkDiscloseBytesPublicInputs(proof, queryResult);
1591
+ isCorrect = isCorrect && isCorrectDisclose;
1592
+ queryResultErrors = {
1593
+ ...queryResultErrors,
1594
+ ...queryResultErrorsDisclose,
1595
+ };
820
1596
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
821
1597
  }
822
1598
  else if (proof.name === "compare_age") {
@@ -830,92 +1606,24 @@ class ZKPassport {
830
1606
  message: "Failed to check the link between the validity of the ID and the age derived from it",
831
1607
  };
832
1608
  }
833
- const minAge = (0, utils_1.getMinAgeFromProof)(proofData);
834
- const maxAge = (0, utils_1.getMaxAgeFromProof)(proofData);
835
- if (queryResult.age) {
836
- if (queryResult.age.gte &&
837
- queryResult.age.gte.result &&
838
- minAge < queryResult.age.gte.expected) {
839
- console.warn("Age is not greater than or equal to the expected age");
840
- isCorrect = false;
841
- queryResultErrors.age.gte = {
842
- expected: queryResult.age.gte.expected,
843
- received: minAge,
844
- message: "Age is not greater than or equal to the expected age",
845
- };
846
- }
847
- if (queryResult.age.lt &&
848
- queryResult.age.lt.result &&
849
- maxAge >= queryResult.age.lt.expected) {
850
- console.warn("Age is not less than the expected age");
851
- isCorrect = false;
852
- queryResultErrors.age.lt = {
853
- expected: queryResult.age.lt.expected,
854
- received: maxAge,
855
- message: "Age is not less than the expected age",
856
- };
857
- }
858
- if (queryResult.age.range) {
859
- if (queryResult.age.range.result &&
860
- (minAge < queryResult.age.range.expected[0] ||
861
- maxAge >= queryResult.age.range.expected[1])) {
862
- console.warn("Age is not in the expected range");
863
- isCorrect = false;
864
- queryResultErrors.age.range = {
865
- expected: queryResult.age.range.expected,
866
- received: [minAge, maxAge],
867
- message: "Age is not in the expected range",
868
- };
869
- }
870
- }
871
- if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
872
- console.warn("Maximum age should be equal to 0");
873
- isCorrect = false;
874
- queryResultErrors.age.disclose = {
875
- expected: 0,
876
- received: maxAge,
877
- message: "Maximum age should be equal to 0",
878
- };
879
- }
880
- if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
881
- console.warn("Minimum age should be equal to 0");
882
- isCorrect = false;
883
- queryResultErrors.age.disclose = {
884
- expected: 0,
885
- received: minAge,
886
- message: "Minimum age should be equal to 0",
887
- };
888
- }
889
- if (queryResult.age.disclose &&
890
- (queryResult.age.disclose.result !== minAge ||
891
- queryResult.age.disclose.result !== maxAge)) {
892
- console.warn("Age does not match the disclosed age in query result");
893
- isCorrect = false;
894
- queryResultErrors.age.disclose = {
895
- expected: `${minAge}`,
896
- received: `${queryResult.age.disclose.result}`,
897
- message: "Age does not match the disclosed age in query result",
898
- };
899
- }
900
- }
901
- else {
902
- console.warn("Age is not set in the query result");
903
- isCorrect = false;
904
- queryResultErrors.age.disclose = {
905
- message: "Age is not set in the query result",
906
- };
907
- }
908
- const currentDate = (0, utils_1.getCurrentDateFromAgeProof)(proofData);
909
- if (currentDate.getTime() !== today.getTime() &&
910
- currentDate.getTime() !== today.getTime() - 86400000) {
911
- console.warn("Current date in the proof is too old");
1609
+ const paramCommitment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1610
+ const committedInputs = proof.committedInputs?.compare_age;
1611
+ const calculatedParamCommitment = await (0, utils_1.getAgeParameterCommitment)(committedInputs.currentDate, committedInputs.minAge, committedInputs.maxAge);
1612
+ if (paramCommitment !== calculatedParamCommitment) {
1613
+ console.warn("The conditions for the age check do not match the conditions checked by the proof");
912
1614
  isCorrect = false;
913
- queryResultErrors.age.disclose = {
914
- expected: `${today.toISOString()}`,
915
- received: `${currentDate.toISOString()}`,
916
- message: "Current date in the proof is too old",
1615
+ queryResultErrors.age.commitment = {
1616
+ expected: `Commitment: ${calculatedParamCommitment}`,
1617
+ received: `Commitment: ${paramCommitment}`,
1618
+ message: "The conditions for the age check do not match the conditions checked by the proof",
917
1619
  };
918
1620
  }
1621
+ const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } = this.checkAgePublicInputs(proof, queryResult);
1622
+ isCorrect = isCorrect && isCorrectAge;
1623
+ queryResultErrors = {
1624
+ ...queryResultErrors,
1625
+ ...queryResultErrorsAge,
1626
+ };
919
1627
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
920
1628
  }
921
1629
  else if (proof.name === "compare_birthdate") {
@@ -929,74 +1637,24 @@ class ZKPassport {
929
1637
  message: "Failed to check the link between the validity of the ID and the birthdate derived from it",
930
1638
  };
931
1639
  }
932
- const minDate = (0, utils_1.getMinDateFromProof)(proofData);
933
- const maxDate = (0, utils_1.getMaxDateFromProof)(proofData);
934
- if (queryResult.birthdate) {
935
- if (queryResult.birthdate.gte &&
936
- queryResult.birthdate.gte.result &&
937
- minDate < queryResult.birthdate.gte.expected) {
938
- console.warn("Birthdate is not greater than or equal to the expected birthdate");
939
- isCorrect = false;
940
- queryResultErrors.birthdate.gte = {
941
- expected: queryResult.birthdate.gte.expected,
942
- received: minDate,
943
- message: "Birthdate is not greater than or equal to the expected birthdate",
944
- };
945
- }
946
- if (queryResult.birthdate.lte &&
947
- queryResult.birthdate.lte.result &&
948
- maxDate > queryResult.birthdate.lte.expected) {
949
- console.warn("Birthdate is not less than the expected birthdate");
950
- isCorrect = false;
951
- queryResultErrors.birthdate.lte = {
952
- expected: queryResult.birthdate.lte.expected,
953
- received: maxDate,
954
- message: "Birthdate is not less than the expected birthdate",
955
- };
956
- }
957
- if (queryResult.birthdate.range) {
958
- if (queryResult.birthdate.range.result &&
959
- (minDate < queryResult.birthdate.range.expected[0] ||
960
- maxDate > queryResult.birthdate.range.expected[1])) {
961
- console.warn("Birthdate is not in the expected range");
962
- isCorrect = false;
963
- queryResultErrors.birthdate.range = {
964
- expected: queryResult.birthdate.range.expected,
965
- received: [minDate, maxDate],
966
- message: "Birthdate is not in the expected range",
967
- };
968
- }
969
- }
970
- if (!queryResult.birthdate.lte &&
971
- !queryResult.birthdate.range &&
972
- maxDate.getTime() != defaultDateValue.getTime()) {
973
- console.warn("Maximum birthdate should be equal to default date value");
974
- isCorrect = false;
975
- queryResultErrors.birthdate.disclose = {
976
- expected: `${defaultDateValue.toISOString()}`,
977
- received: `${maxDate.toISOString()}`,
978
- message: "Maximum birthdate should be equal to default date value",
979
- };
980
- }
981
- if (!queryResult.birthdate.gte &&
982
- !queryResult.birthdate.range &&
983
- minDate.getTime() != defaultDateValue.getTime()) {
984
- console.warn("Minimum birthdate should be equal to default date value");
985
- isCorrect = false;
986
- queryResultErrors.birthdate.disclose = {
987
- expected: `${defaultDateValue.toISOString()}`,
988
- received: `${minDate.toISOString()}`,
989
- message: "Minimum birthdate should be equal to default date value",
990
- };
991
- }
992
- }
993
- else {
994
- console.warn("Birthdate is not set in the query result");
1640
+ const paramCommitment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1641
+ const committedInputs = proof.committedInputs?.compare_birthdate;
1642
+ const calculatedParamCommitment = await (0, utils_1.getDateParameterCommitment)(utils_1.ProofType.BIRTHDATE, committedInputs.currentDate, committedInputs.minDate, committedInputs.maxDate);
1643
+ if (paramCommitment !== calculatedParamCommitment) {
1644
+ console.warn("The conditions for the birthdate check do not match the conditions checked by the proof");
995
1645
  isCorrect = false;
996
- queryResultErrors.birthdate.disclose = {
997
- message: "Birthdate is not set in the query result",
1646
+ queryResultErrors.birthdate.commitment = {
1647
+ expected: `Commitment: ${calculatedParamCommitment}`,
1648
+ received: `Commitment: ${paramCommitment}`,
1649
+ message: "The conditions for the birthdate check do not match the conditions checked by the proof",
998
1650
  };
999
1651
  }
1652
+ const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } = this.checkBirthdatePublicInputs(proof, queryResult);
1653
+ isCorrect = isCorrect && isCorrectBirthdate;
1654
+ queryResultErrors = {
1655
+ ...queryResultErrors,
1656
+ ...queryResultErrorsBirthdate,
1657
+ };
1000
1658
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1001
1659
  }
1002
1660
  else if (proof.name === "compare_expiry") {
@@ -1010,74 +1668,24 @@ class ZKPassport {
1010
1668
  message: "Failed to check the link between the validity of the ID and its expiry date",
1011
1669
  };
1012
1670
  }
1013
- const minDate = (0, utils_1.getMinDateFromProof)(proofData);
1014
- const maxDate = (0, utils_1.getMaxDateFromProof)(proofData);
1015
- if (queryResult.expiry_date) {
1016
- if (queryResult.expiry_date.gte &&
1017
- queryResult.expiry_date.gte.result &&
1018
- minDate < queryResult.expiry_date.gte.expected) {
1019
- console.warn("Expiry date is not greater than or equal to the expected expiry date");
1020
- isCorrect = false;
1021
- queryResultErrors.expiry_date.gte = {
1022
- expected: queryResult.expiry_date.gte.expected,
1023
- received: minDate,
1024
- message: "Expiry date is not greater than or equal to the expected expiry date",
1025
- };
1026
- }
1027
- if (queryResult.expiry_date.lte &&
1028
- queryResult.expiry_date.lte.result &&
1029
- maxDate > queryResult.expiry_date.lte.expected) {
1030
- console.warn("Expiry date is not less than the expected expiry date");
1031
- isCorrect = false;
1032
- queryResultErrors.expiry_date.lte = {
1033
- expected: queryResult.expiry_date.lte.expected,
1034
- received: maxDate,
1035
- message: "Expiry date is not less than the expected expiry date",
1036
- };
1037
- }
1038
- if (queryResult.expiry_date.range) {
1039
- if (queryResult.expiry_date.range.result &&
1040
- (minDate < queryResult.expiry_date.range.expected[0] ||
1041
- maxDate > queryResult.expiry_date.range.expected[1])) {
1042
- console.warn("Expiry date is not in the expected range");
1043
- isCorrect = false;
1044
- queryResultErrors.expiry_date.range = {
1045
- expected: queryResult.expiry_date.range.expected,
1046
- received: [minDate, maxDate],
1047
- message: "Expiry date is not in the expected range",
1048
- };
1049
- }
1050
- }
1051
- if (!queryResult.expiry_date.lte &&
1052
- !queryResult.expiry_date.range &&
1053
- maxDate.getTime() != defaultDateValue.getTime()) {
1054
- console.warn("Maximum expiry date should be equal to default date value");
1055
- isCorrect = false;
1056
- queryResultErrors.expiry_date.disclose = {
1057
- expected: `${defaultDateValue.toISOString()}`,
1058
- received: `${maxDate.toISOString()}`,
1059
- message: "Maximum expiry date should be equal to default date value",
1060
- };
1061
- }
1062
- if (!queryResult.expiry_date.gte &&
1063
- !queryResult.expiry_date.range &&
1064
- minDate.getTime() != defaultDateValue.getTime()) {
1065
- console.warn("Minimum expiry date should be equal to default date value");
1066
- isCorrect = false;
1067
- queryResultErrors.expiry_date.disclose = {
1068
- expected: `${defaultDateValue.toISOString()}`,
1069
- received: `${minDate.toISOString()}`,
1070
- message: "Minimum expiry date should be equal to default date value",
1071
- };
1072
- }
1073
- }
1074
- else {
1075
- console.warn("Expiry date is not set in the query result");
1671
+ const paramCommitment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1672
+ const committedInputs = proof.committedInputs?.compare_expiry;
1673
+ const calculatedParamCommitment = await (0, utils_1.getDateParameterCommitment)(utils_1.ProofType.EXPIRY_DATE, committedInputs.currentDate, committedInputs.minDate, committedInputs.maxDate);
1674
+ if (paramCommitment !== calculatedParamCommitment) {
1675
+ console.warn("The conditions for the expiry date check do not match the conditions checked by the proof");
1076
1676
  isCorrect = false;
1077
- queryResultErrors.expiry_date.disclose = {
1078
- message: "Expiry date is not set in the query result",
1677
+ queryResultErrors.expiry_date.commitment = {
1678
+ expected: `Commitment: ${calculatedParamCommitment}`,
1679
+ received: `Commitment: ${paramCommitment}`,
1680
+ message: "The conditions for the expiry date check do not match the conditions checked by the proof",
1079
1681
  };
1080
1682
  }
1683
+ const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } = this.checkExpiryDatePublicInputs(proof, queryResult);
1684
+ isCorrect = isCorrect && isCorrectExpiryDate;
1685
+ queryResultErrors = {
1686
+ ...queryResultErrors,
1687
+ ...queryResultErrorsExpiryDate,
1688
+ };
1081
1689
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1082
1690
  }
1083
1691
  else if (proof.name === "exclusion_check_nationality") {
@@ -1091,39 +1699,24 @@ class ZKPassport {
1091
1699
  message: "Failed to check the link between the validity of the ID and the nationality exclusion check",
1092
1700
  };
1093
1701
  }
1094
- const countryList = (0, utils_1.getCountryListFromExclusionProof)(proofData);
1095
- if (queryResult.nationality &&
1096
- queryResult.nationality.out &&
1097
- queryResult.nationality.out.result) {
1098
- if (!queryResult.nationality.out.expected?.every((country) => countryList.includes(country))) {
1099
- console.warn("Nationality exclusion list does not match the one from the query results");
1100
- isCorrect = false;
1101
- queryResultErrors.nationality.out = {
1102
- expected: queryResult.nationality.out.expected,
1103
- received: countryList,
1104
- message: "Nationality exclusion list does not match the one from the query results",
1105
- };
1106
- }
1107
- }
1108
- else if (!queryResult.nationality || !queryResult.nationality.out) {
1109
- console.warn("Nationality exclusion is not set in the query result");
1702
+ const countryList = (proof.committedInputs?.exclusion_check_nationality).countries;
1703
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1704
+ const calculatedParamCommitment = await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.NATIONALITY_EXCLUSION, countryList, true);
1705
+ if (paramCommittment !== calculatedParamCommitment) {
1706
+ console.warn("The committed country list for the exclusion check does not match the one from the proof");
1110
1707
  isCorrect = false;
1111
- queryResultErrors.nationality.out = {
1112
- message: "Nationality exclusion is not set in the query result",
1708
+ queryResultErrors.nationality.commitment = {
1709
+ expected: `Commitment: ${calculatedParamCommitment}`,
1710
+ received: `Commitment: ${paramCommittment}`,
1711
+ message: "The committed country list for the exclusion check does not match the one from the proof",
1113
1712
  };
1114
1713
  }
1115
- // Check the countryList is in ascending order
1116
- // If the prover doesn't use a sorted list then the proof cannot be trusted
1117
- // as it is requirement in the circuit for the exclusion check to work
1118
- for (let i = 1; i < countryList.length; i++) {
1119
- if (countryList[i] < countryList[i - 1]) {
1120
- console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
1121
- isCorrect = false;
1122
- queryResultErrors.nationality.out = {
1123
- message: "The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
1124
- };
1125
- }
1126
- }
1714
+ const { isCorrect: isCorrectNationalityExclusion, queryResultErrors: queryResultErrorsNationalityExclusion, } = this.checkNationalityExclusionPublicInputs(queryResult, countryList);
1715
+ isCorrect = isCorrect && isCorrectNationalityExclusion;
1716
+ queryResultErrors = {
1717
+ ...queryResultErrors,
1718
+ ...queryResultErrorsNationalityExclusion,
1719
+ };
1127
1720
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1128
1721
  }
1129
1722
  else if (proof.name === "exclusion_check_issuing_country") {
@@ -1137,39 +1730,24 @@ class ZKPassport {
1137
1730
  message: "Failed to check the link between the validity of the ID and the issuing country exclusion check",
1138
1731
  };
1139
1732
  }
1140
- const countryList = (0, utils_1.getCountryListFromExclusionProof)(proofData);
1141
- if (queryResult.issuing_country &&
1142
- queryResult.issuing_country.out &&
1143
- queryResult.issuing_country.out.result) {
1144
- if (!queryResult.issuing_country.out.expected?.every((country) => countryList.includes(country))) {
1145
- console.warn("Issuing country exclusion list does not match the one from the query results");
1146
- isCorrect = false;
1147
- queryResultErrors.issuing_country.out = {
1148
- expected: queryResult.issuing_country.out.expected,
1149
- received: countryList,
1150
- message: "Issuing country exclusion list does not match the one from the query results",
1151
- };
1152
- }
1153
- }
1154
- else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
1155
- console.warn("Issuing country exclusion is not set in the query result");
1733
+ const countryList = (proof.committedInputs?.exclusion_check_issuing_country).countries;
1734
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1735
+ const calculatedParamCommitment = await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_EXCLUSION, countryList, true);
1736
+ if (paramCommittment !== calculatedParamCommitment) {
1737
+ console.warn("The committed country list for the issuing country exclusion check does not match the one from the proof");
1156
1738
  isCorrect = false;
1157
- queryResultErrors.issuing_country.out = {
1158
- message: "Issuing country exclusion is not set in the query result",
1739
+ queryResultErrors.issuing_country.commitment = {
1740
+ expected: `Commitment: ${calculatedParamCommitment}`,
1741
+ received: `Commitment: ${paramCommittment}`,
1742
+ message: "The committed country list for the issuing country exclusion check does not match the one from the proof",
1159
1743
  };
1160
1744
  }
1161
- // Check the countryList is in ascending order
1162
- // If the prover doesn't use a sorted list then the proof cannot be trusted
1163
- // as it is requirement in the circuit for the exclusion check to work
1164
- for (let i = 1; i < countryList.length; i++) {
1165
- if (countryList[i] < countryList[i - 1]) {
1166
- console.warn("The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted");
1167
- isCorrect = false;
1168
- queryResultErrors.issuing_country.out = {
1169
- message: "The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
1170
- };
1171
- }
1172
- }
1745
+ const { isCorrect: isCorrectIssuingCountryExclusion, queryResultErrors: queryResultErrorsIssuingCountryExclusion, } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList);
1746
+ isCorrect = isCorrect && isCorrectIssuingCountryExclusion;
1747
+ queryResultErrors = {
1748
+ ...queryResultErrors,
1749
+ ...queryResultErrorsIssuingCountryExclusion,
1750
+ };
1173
1751
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1174
1752
  }
1175
1753
  else if (proof.name === "inclusion_check_nationality") {
@@ -1183,27 +1761,24 @@ class ZKPassport {
1183
1761
  message: "Failed to check the link between the validity of the ID and the nationality inclusion check",
1184
1762
  };
1185
1763
  }
1186
- const countryList = (0, utils_1.getCountryListFromInclusionProof)(proofData);
1187
- if (queryResult.nationality &&
1188
- queryResult.nationality.in &&
1189
- queryResult.nationality.in.result) {
1190
- if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
1191
- console.warn("Nationality inclusion list does not match the one from the query results");
1192
- isCorrect = false;
1193
- queryResultErrors.nationality.in = {
1194
- expected: queryResult.nationality.in.expected,
1195
- received: countryList,
1196
- message: "Nationality inclusion list does not match the one from the query results",
1197
- };
1198
- }
1199
- }
1200
- else if (!queryResult.nationality || !queryResult.nationality.in) {
1201
- console.warn("Nationality inclusion is not set in the query result");
1764
+ const countryList = (proof.committedInputs?.inclusion_check_nationality).countries;
1765
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1766
+ const calculatedParamCommitment = await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.NATIONALITY_INCLUSION, countryList, false);
1767
+ if (paramCommittment !== calculatedParamCommitment) {
1768
+ console.warn("The committed country list for the nationality inclusion check does not match the one from the proof");
1202
1769
  isCorrect = false;
1203
- queryResultErrors.nationality.in = {
1204
- message: "Nationality inclusion is not set in the query result",
1770
+ queryResultErrors.nationality.commitment = {
1771
+ expected: `Commitment: ${calculatedParamCommitment}`,
1772
+ received: `Commitment: ${paramCommittment}`,
1773
+ message: "The committed country list for the nationality inclusion check does not match the one from the proof",
1205
1774
  };
1206
1775
  }
1776
+ const { isCorrect: isCorrectNationalityInclusion, queryResultErrors: queryResultErrorsNationalityInclusion, } = this.checkNationalityInclusionPublicInputs(queryResult, countryList);
1777
+ isCorrect = isCorrect && isCorrectNationalityInclusion;
1778
+ queryResultErrors = {
1779
+ ...queryResultErrors,
1780
+ ...queryResultErrorsNationalityInclusion,
1781
+ };
1207
1782
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1208
1783
  }
1209
1784
  else if (proof.name === "inclusion_check_issuing_country") {
@@ -1217,27 +1792,24 @@ class ZKPassport {
1217
1792
  message: "Failed to check the link between the validity of the ID and the issuing country inclusion check",
1218
1793
  };
1219
1794
  }
1220
- const countryList = (0, utils_1.getCountryListFromInclusionProof)(proofData);
1221
- if (queryResult.issuing_country &&
1222
- queryResult.issuing_country.in &&
1223
- queryResult.issuing_country.in.result) {
1224
- if (!queryResult.issuing_country.in.expected?.every((country) => countryList.includes(country))) {
1225
- console.warn("Issuing country inclusion list does not match the one from the query results");
1226
- isCorrect = false;
1227
- queryResultErrors.issuing_country.in = {
1228
- expected: queryResult.issuing_country.in.expected,
1229
- received: countryList,
1230
- message: "Issuing country inclusion list does not match the one from the query results",
1231
- };
1232
- }
1233
- }
1234
- else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
1235
- console.warn("Issuing country inclusion is not set in the query result");
1795
+ const countryList = (proof.committedInputs?.inclusion_check_issuing_country).countries;
1796
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1797
+ const calculatedParamCommitment = await (0, utils_1.getCountryParameterCommitment)(utils_1.ProofType.ISSUING_COUNTRY_INCLUSION, countryList, false);
1798
+ if (paramCommittment !== calculatedParamCommitment) {
1799
+ console.warn("The committed country list for the issuing country inclusion check does not match the one from the proof");
1236
1800
  isCorrect = false;
1237
- queryResultErrors.issuing_country.in = {
1238
- message: "Issuing country inclusion is not set in the query result",
1801
+ queryResultErrors.issuing_country.commitment = {
1802
+ expected: `Commitment: ${calculatedParamCommitment}`,
1803
+ received: `Commitment: ${paramCommittment}`,
1804
+ message: "The committed country list for the issuing country inclusion check does not match the one from the proof",
1239
1805
  };
1240
1806
  }
1807
+ const { isCorrect: isCorrectIssuingCountryInclusion, queryResultErrors: queryResultErrorsIssuingCountryInclusion, } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList);
1808
+ isCorrect = isCorrect && isCorrectIssuingCountryInclusion;
1809
+ queryResultErrors = {
1810
+ ...queryResultErrors,
1811
+ ...queryResultErrorsIssuingCountryInclusion,
1812
+ };
1241
1813
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1242
1814
  }
1243
1815
  }
@@ -1272,15 +1844,51 @@ class ZKPassport {
1272
1844
  // Only proceed with the proof verification if the public inputs are correct
1273
1845
  if (verified) {
1274
1846
  for (const proof of proofs) {
1275
- const proofData = (0, utils_1.getProofData)(proof.proof, true);
1847
+ const proofData = (0, utils_1.getProofData)(proof.proof, (0, utils_1.getNumberOfPublicInputs)(proof.name));
1276
1848
  const hostedPackagedCircuit = await (0, utils_1.getHostedPackagedCircuitByName)(proof.version, proof.name);
1277
- const vkeyBytes = buffer_1.Buffer.from(hostedPackagedCircuit.vkey, "base64");
1278
- try {
1279
- verified = await verifier.verifyUltraHonkProof(proofData, new Uint8Array(vkeyBytes));
1849
+ if (proof.name?.startsWith("outer_evm")) {
1850
+ try {
1851
+ const { createPublicClient, http } = await Promise.resolve().then(() => tslib_1.__importStar(require("viem")));
1852
+ const { sepolia } = await Promise.resolve().then(() => tslib_1.__importStar(require("viem/chains")));
1853
+ const verifierDetails = this.getSolidityVerifierDetails("ethereum_sepolia");
1854
+ const client = createPublicClient({
1855
+ chain: sepolia,
1856
+ transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
1857
+ });
1858
+ const params = this.getSolidityVerifierParameters(proof);
1859
+ const result = await client.readContract({
1860
+ address: verifierDetails.address,
1861
+ abi: verifierDetails.abi,
1862
+ functionName: "verifyProof",
1863
+ args: [
1864
+ params.vkeyHash,
1865
+ params.proof,
1866
+ params.publicInputs,
1867
+ params.committedInputs,
1868
+ params.committedInputCounts,
1869
+ params.validityPeriodInDays,
1870
+ ],
1871
+ });
1872
+ const isVerified = Array.isArray(result) ? Boolean(result[0]) : false;
1873
+ verified = isVerified;
1874
+ }
1875
+ catch (error) {
1876
+ console.warn("Error verifying proof", error);
1877
+ verified = false;
1878
+ }
1280
1879
  }
1281
- catch (e) {
1282
- console.warn("Error verifying proof", e);
1283
- verified = false;
1880
+ else {
1881
+ const vkeyBytes = buffer_1.Buffer.from(hostedPackagedCircuit.vkey, "base64");
1882
+ try {
1883
+ verified = await verifier.verifyUltraHonkProof({
1884
+ proof: buffer_1.Buffer.from(proofData.proof.join(""), "hex"),
1885
+ publicInputs: proofData.publicInputs,
1886
+ }, new Uint8Array(vkeyBytes));
1887
+ }
1888
+ catch (e) {
1889
+ console.warn("Error verifying proof", e);
1890
+ verified = false;
1891
+ }
1284
1892
  }
1285
1893
  if (!verified) {
1286
1894
  // Break the loop if the proof is not valid
@@ -1293,6 +1901,150 @@ class ZKPassport {
1293
1901
  uniqueIdentifier = verified ? uniqueIdentifier : undefined;
1294
1902
  return { uniqueIdentifier, verified, queryResultErrors };
1295
1903
  }
1904
+ getSolidityVerifierDetails(network) {
1905
+ if (network === "ethereum_sepolia") {
1906
+ return {
1907
+ address: "0xca644D3424c2ee577FaaF2b56C0f9D1937E8e87C",
1908
+ abi: ZKPassportVerifier_json_1.default.abi,
1909
+ };
1910
+ }
1911
+ else if (network === "local_anvil") {
1912
+ return {
1913
+ address: "0x0",
1914
+ abi: ZKPassportVerifier_json_1.default.abi,
1915
+ };
1916
+ }
1917
+ throw new Error(`Unsupported network: ${network}`);
1918
+ }
1919
+ getSolidityVerifierParameters(proof, validityPeriodInDays = 7) {
1920
+ if (!proof.name?.startsWith("outer_evm")) {
1921
+ throw new Error("This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.");
1922
+ }
1923
+ const proofData = (0, utils_1.getProofData)(proof.proof, (0, utils_1.getNumberOfPublicInputs)(proof.name));
1924
+ // For EVM optimised proofs, the first 16 bytes of the proof are the aggregation object
1925
+ // and should be moved at the end of the public inputs
1926
+ const actualProof = proofData.proof.slice(16);
1927
+ const actualPublicInputs = proofData.publicInputs.concat(proofData.proof.slice(0, 16).map((x) => `0x${x}`));
1928
+ let committedInputCounts = [];
1929
+ let committedInputs = [];
1930
+ for (const key in proof.committedInputs) {
1931
+ const committedInputCount = (0, utils_1.getCommittedInputCount)(key);
1932
+ const circuitName = key;
1933
+ committedInputCounts.push({ circuitName, count: committedInputCount });
1934
+ let compressedCommittedInputs = "";
1935
+ if (circuitName === "inclusion_check_issuing_country_evm" ||
1936
+ circuitName === "inclusion_check_nationality_evm" ||
1937
+ circuitName === "exclusion_check_issuing_country_evm" ||
1938
+ circuitName === "exclusion_check_nationality_evm") {
1939
+ const value = proof.committedInputs[circuitName];
1940
+ const formattedCountries = value.countries;
1941
+ if (circuitName === "exclusion_check_issuing_country_evm" ||
1942
+ circuitName === "exclusion_check_nationality_evm") {
1943
+ formattedCountries.sort((a, b) => a.localeCompare(b));
1944
+ }
1945
+ const proofType = (() => {
1946
+ switch (circuitName) {
1947
+ case "exclusion_check_issuing_country_evm":
1948
+ return utils_1.ProofType.ISSUING_COUNTRY_EXCLUSION;
1949
+ case "exclusion_check_nationality_evm":
1950
+ return utils_1.ProofType.NATIONALITY_EXCLUSION;
1951
+ case "inclusion_check_issuing_country_evm":
1952
+ return utils_1.ProofType.ISSUING_COUNTRY_INCLUSION;
1953
+ case "inclusion_check_nationality_evm":
1954
+ return utils_1.ProofType.NATIONALITY_INCLUSION;
1955
+ }
1956
+ })();
1957
+ compressedCommittedInputs =
1958
+ proofType.toString(16).padStart(2, "0") +
1959
+ (0, utils_1.rightPadArrayWithZeros)(formattedCountries.map((c) => Array.from(new TextEncoder().encode(c))).flat(), 600)
1960
+ .map((x) => x.toString(16).padStart(2, "0"))
1961
+ .join("");
1962
+ }
1963
+ else if (circuitName === "compare_age_evm") {
1964
+ const value = proof.committedInputs[circuitName];
1965
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate));
1966
+ compressedCommittedInputs =
1967
+ utils_1.ProofType.AGE.toString(16).padStart(2, "0") +
1968
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
1969
+ value.minAge.toString(16).padStart(2, "0") +
1970
+ value.maxAge.toString(16).padStart(2, "0");
1971
+ }
1972
+ else if (circuitName === "compare_birthdate_evm") {
1973
+ const value = proof.committedInputs[circuitName];
1974
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate));
1975
+ const minDateBytes = Array.from(new TextEncoder().encode(value.minDate));
1976
+ const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate));
1977
+ compressedCommittedInputs =
1978
+ utils_1.ProofType.BIRTHDATE.toString(16).padStart(2, "0") +
1979
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
1980
+ minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
1981
+ maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
1982
+ }
1983
+ else if (circuitName === "compare_expiry_evm") {
1984
+ const value = proof.committedInputs[circuitName];
1985
+ const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate));
1986
+ const minDateBytes = Array.from(new TextEncoder().encode(value.minDate));
1987
+ const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate));
1988
+ compressedCommittedInputs =
1989
+ utils_1.ProofType.EXPIRY_DATE.toString(16).padStart(2, "0") +
1990
+ currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
1991
+ minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
1992
+ maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
1993
+ }
1994
+ else if (circuitName === "disclose_bytes_evm") {
1995
+ const value = proof.committedInputs[circuitName];
1996
+ compressedCommittedInputs =
1997
+ utils_1.ProofType.DISCLOSE.toString(16).padStart(2, "0") +
1998
+ value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
1999
+ value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
2000
+ }
2001
+ else {
2002
+ throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
2003
+ }
2004
+ committedInputs.push({ circuitName, inputs: compressedCommittedInputs });
2005
+ }
2006
+ const parameterCommitments = proofData.publicInputs.slice(11, proofData.publicInputs.length - 1);
2007
+ let compressedCommittedInputs = "";
2008
+ let committedInputCountsArray = [];
2009
+ for (const commitment of parameterCommitments) {
2010
+ const committedInput = committedInputs.find((x) => {
2011
+ const rawHashedInputs = (0, sha256_1.sha256)((0, utils_3.hexToBytes)(x.inputs));
2012
+ // Shift the hash 8 bits to the right (1 byte)
2013
+ // as one byte is dropped in the circuit to fit in the 254-bit field size
2014
+ const hashedInputs = new Uint8Array(rawHashedInputs.length);
2015
+ // Move each byte 1 position to the right (shifting 8 bits)
2016
+ for (let i = 0; i < rawHashedInputs.length - 1; i++) {
2017
+ hashedInputs[i + 1] = rawHashedInputs[i];
2018
+ }
2019
+ // First byte becomes 0 (since we're shifting right)
2020
+ hashedInputs[0] = 0;
2021
+ return (0, utils_2.bytesToHex)(hashedInputs) === commitment.replace("0x", "");
2022
+ });
2023
+ if (committedInput) {
2024
+ const count = committedInputCounts.find((x) => x.circuitName === committedInput.circuitName)?.count;
2025
+ if (count) {
2026
+ committedInputCountsArray.push(count);
2027
+ compressedCommittedInputs += committedInput.inputs;
2028
+ }
2029
+ else {
2030
+ throw new Error(`Unknown circuit name: ${committedInput.circuitName}`);
2031
+ }
2032
+ }
2033
+ else {
2034
+ throw new Error(`Invalid commitment: ${commitment}`);
2035
+ }
2036
+ }
2037
+ const params = {
2038
+ // Make sure the vkeyHash is 32 bytes
2039
+ vkeyHash: `0x${proof.vkeyHash.replace("0x", "").padStart(64, "0")}`,
2040
+ proof: `0x${actualProof.join("")}`,
2041
+ publicInputs: actualPublicInputs,
2042
+ committedInputs: `0x${compressedCommittedInputs}`,
2043
+ committedInputCounts: committedInputCountsArray,
2044
+ validityPeriodInDays,
2045
+ };
2046
+ return params;
2047
+ }
1296
2048
  /**
1297
2049
  * @notice Returns the URL of the request.
1298
2050
  * @param requestId The request ID.
@@ -1302,7 +2054,7 @@ class ZKPassport {
1302
2054
  const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[requestId].publicKey);
1303
2055
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
1304
2056
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
1305
- return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}`;
2057
+ return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
1306
2058
  }
1307
2059
  /**
1308
2060
  * @notice Cancels a request by closing the WebSocket connection and deleting the associated data.