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