@zkpassport/sdk 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +132 -34
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +130 -10
- package/package.json +1 -1
- package/src/index.ts +148 -20
package/dist/cjs/index.d.ts
CHANGED
|
@@ -134,6 +134,8 @@ export declare class ZKPassport {
|
|
|
134
134
|
private topicToRequestReceived;
|
|
135
135
|
private topicToService;
|
|
136
136
|
private topicToProofs;
|
|
137
|
+
private topicToExpectedProofCount;
|
|
138
|
+
private topicToResults;
|
|
137
139
|
private onRequestReceivedCallbacks;
|
|
138
140
|
private onGeneratingProofCallbacks;
|
|
139
141
|
private onBridgeConnectCallbacks;
|
|
@@ -142,6 +144,8 @@ export declare class ZKPassport {
|
|
|
142
144
|
private onRejectCallbacks;
|
|
143
145
|
private onErrorCallbacks;
|
|
144
146
|
constructor(_domain?: string);
|
|
147
|
+
private handleResult;
|
|
148
|
+
private setExpectedProofCount;
|
|
145
149
|
/**
|
|
146
150
|
* @notice Handle an encrypted message.
|
|
147
151
|
* @param request The request.
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
exports.ZKPassport = exports.MERCOSUR_COUNTRIES = exports.ASEAN_COUNTRIES = exports.SCHENGEN_COUNTRIES = exports.EEA_COUNTRIES = exports.EU_COUNTRIES = exports.SANCTIONED_COUNTRIES = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
27
5
|
const crypto_1 = require("crypto");
|
|
28
6
|
const i18n_iso_countries_1 = require("i18n-iso-countries");
|
|
29
7
|
const utils_1 = require("@zkpassport/utils");
|
|
@@ -35,7 +13,22 @@ const logger_1 = require("./logger");
|
|
|
35
13
|
const node_gzip_1 = require("node-gzip");
|
|
36
14
|
//import initNoirC from '@noir-lang/noirc_abi'
|
|
37
15
|
//import initACVM from '@noir-lang/acvm_js'
|
|
38
|
-
|
|
16
|
+
const en_json_1 = tslib_1.__importDefault(require("i18n-iso-countries/langs/en.json"));
|
|
17
|
+
(0, i18n_iso_countries_1.registerLocale)(en_json_1.default);
|
|
18
|
+
function hasRequestedAccessToField(credentialsRequest, field) {
|
|
19
|
+
const fieldValue = credentialsRequest[field];
|
|
20
|
+
const isDefined = fieldValue !== undefined && fieldValue !== null;
|
|
21
|
+
if (!isDefined) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
for (const key in fieldValue) {
|
|
25
|
+
if (fieldValue[key] !== undefined &&
|
|
26
|
+
fieldValue[key] !== null) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
39
32
|
function normalizeCountry(country) {
|
|
40
33
|
let normalizedCountry;
|
|
41
34
|
const alpha3 = (0, i18n_iso_countries_1.getAlpha3Code)(country, "en");
|
|
@@ -77,6 +70,8 @@ class ZKPassport {
|
|
|
77
70
|
this.topicToRequestReceived = {};
|
|
78
71
|
this.topicToService = {};
|
|
79
72
|
this.topicToProofs = {};
|
|
73
|
+
this.topicToExpectedProofCount = {};
|
|
74
|
+
this.topicToResults = {};
|
|
80
75
|
this.onRequestReceivedCallbacks = {};
|
|
81
76
|
this.onGeneratingProofCallbacks = {};
|
|
82
77
|
this.onBridgeConnectCallbacks = {};
|
|
@@ -95,6 +90,70 @@ class ZKPassport {
|
|
|
95
90
|
await Promise.all([initACVM(acvm), initNoirC(noirc)])
|
|
96
91
|
this.wasmVerifierInit = true
|
|
97
92
|
}*/
|
|
93
|
+
async handleResult(topic) {
|
|
94
|
+
const result = this.topicToResults[topic];
|
|
95
|
+
// Clear the results straight away to avoid concurrency issues
|
|
96
|
+
delete this.topicToResults[topic];
|
|
97
|
+
// Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
|
|
98
|
+
const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
|
|
99
|
+
await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
|
|
100
|
+
uniqueIdentifier,
|
|
101
|
+
verified,
|
|
102
|
+
result,
|
|
103
|
+
})));
|
|
104
|
+
// Clear the expected proof count
|
|
105
|
+
delete this.topicToExpectedProofCount[topic];
|
|
106
|
+
}
|
|
107
|
+
setExpectedProofCount(topic) {
|
|
108
|
+
const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
|
|
109
|
+
const neededCircuits = [];
|
|
110
|
+
// Determine which circuits are needed based on the requested fields
|
|
111
|
+
for (const field of fields) {
|
|
112
|
+
for (const key in this.topicToConfig[topic][field]) {
|
|
113
|
+
switch (key) {
|
|
114
|
+
case "eq":
|
|
115
|
+
case "disclose":
|
|
116
|
+
if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
|
|
117
|
+
neededCircuits.push("disclose_bytes");
|
|
118
|
+
}
|
|
119
|
+
else if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
120
|
+
neededCircuits.push("compare_age");
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case "gte":
|
|
124
|
+
case "gt":
|
|
125
|
+
case "lte":
|
|
126
|
+
case "lt":
|
|
127
|
+
case "range":
|
|
128
|
+
if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
129
|
+
neededCircuits.push("compare_age");
|
|
130
|
+
}
|
|
131
|
+
else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
|
|
132
|
+
neededCircuits.push("compare_expiry");
|
|
133
|
+
}
|
|
134
|
+
else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
|
|
135
|
+
neededCircuits.push("compare_birthdate");
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
case "in":
|
|
139
|
+
if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
|
|
140
|
+
neededCircuits.push("inclusion_check_country");
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case "out":
|
|
144
|
+
if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
|
|
145
|
+
neededCircuits.push("exclusion_check_country");
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// From the circuits needed, determine the expected proof count
|
|
152
|
+
// There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
|
|
153
|
+
// Each separate needed circuit adds 1 disclosure proof
|
|
154
|
+
this.topicToExpectedProofCount[topic] =
|
|
155
|
+
neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
|
|
156
|
+
}
|
|
98
157
|
/**
|
|
99
158
|
* @notice Handle an encrypted message.
|
|
100
159
|
* @param request The request.
|
|
@@ -127,19 +186,44 @@ class ZKPassport {
|
|
|
127
186
|
};
|
|
128
187
|
this.topicToProofs[topic].push(processedProof);
|
|
129
188
|
await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
|
|
189
|
+
// If the results were received before all the proofs were generated,
|
|
190
|
+
// we can handle the result now
|
|
191
|
+
if (this.topicToResults[topic] &&
|
|
192
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
193
|
+
await this.handleResult(topic);
|
|
194
|
+
}
|
|
130
195
|
}
|
|
131
196
|
else if (request.method === "done") {
|
|
132
197
|
logger_1.noLogger.debug(`User sent the query result`);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
})));
|
|
198
|
+
this.topicToResults[topic] = request.params;
|
|
199
|
+
// Make sure all the proofs have been received, otherwise we'll handle the result later
|
|
200
|
+
// once the proofs have all been received
|
|
201
|
+
if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
202
|
+
await this.handleResult(topic);
|
|
203
|
+
}
|
|
140
204
|
}
|
|
141
205
|
else if (request.method === "error") {
|
|
142
|
-
|
|
206
|
+
const error = request.params.error;
|
|
207
|
+
if (error && error === "This ID is not supported yet") {
|
|
208
|
+
// This means the user has an ID that is not supported yet
|
|
209
|
+
// So we won't receive any proofs and we can handle the result now
|
|
210
|
+
this.topicToExpectedProofCount[topic] = 0;
|
|
211
|
+
if (this.topicToResults[topic]) {
|
|
212
|
+
await this.handleResult(topic);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (error && error.startsWith("Cannot generate proof")) {
|
|
216
|
+
// This means one of the disclosure proofs failed to be generated
|
|
217
|
+
// So we need to remove one from the expected proof count
|
|
218
|
+
this.topicToExpectedProofCount[topic] -= 1;
|
|
219
|
+
// If the expected proof count is now equal to the number of proofs received
|
|
220
|
+
// and the results were received, we can handle the result now
|
|
221
|
+
if (this.topicToResults[topic] &&
|
|
222
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
223
|
+
await this.handleResult(topic);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)));
|
|
143
227
|
}
|
|
144
228
|
}
|
|
145
229
|
getZkPassportRequest(topic) {
|
|
@@ -195,6 +279,7 @@ class ZKPassport {
|
|
|
195
279
|
const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
|
|
196
280
|
const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
|
|
197
281
|
const pubkey = (0, utils_2.bytesToHex)(this.topicToKeyPair[topic].publicKey);
|
|
282
|
+
this.setExpectedProofCount(topic);
|
|
198
283
|
return {
|
|
199
284
|
url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
|
|
200
285
|
requestId: topic,
|
|
@@ -225,6 +310,7 @@ class ZKPassport {
|
|
|
225
310
|
this.topicToConfig[topic] = {};
|
|
226
311
|
this.topicToService[topic] = { name, logo, purpose, scope };
|
|
227
312
|
this.topicToProofs[topic] = [];
|
|
313
|
+
this.topicToExpectedProofCount[topic] = 0;
|
|
228
314
|
this.onRequestReceivedCallbacks[topic] = [];
|
|
229
315
|
this.onGeneratingProofCallbacks[topic] = [];
|
|
230
316
|
this.onBridgeConnectCallbacks[topic] = [];
|
|
@@ -766,6 +852,16 @@ class ZKPassport {
|
|
|
766
852
|
isCorrect = false;
|
|
767
853
|
break;
|
|
768
854
|
}
|
|
855
|
+
// Check the countryList is in ascending order
|
|
856
|
+
// If the prover doesn't use a sorted list then the proof cannot be trusted
|
|
857
|
+
// as it is requirement in the circuit for the exclusion check to work
|
|
858
|
+
for (let i = 1; i < countryList.length; i++) {
|
|
859
|
+
if (countryList[i] < countryList[i - 1]) {
|
|
860
|
+
console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
|
|
861
|
+
isCorrect = false;
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
769
865
|
uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
|
|
770
866
|
}
|
|
771
867
|
else if (proof.name === "inclusion_check_country") {
|
|
@@ -810,12 +906,12 @@ class ZKPassport {
|
|
|
810
906
|
proofsToVerify = this.topicToProofs[requestId];
|
|
811
907
|
if (!proofsToVerify || proofsToVerify.length < 4) {
|
|
812
908
|
// It may happen that a request returns a result without proofs
|
|
813
|
-
// Meaning the ID is supported yet by ZKPassport circuits,
|
|
909
|
+
// Meaning the ID is not supported yet by ZKPassport circuits,
|
|
814
910
|
// so the results has to be trusted and cannot be independently verified
|
|
815
911
|
return { uniqueIdentifier: undefined, verified: false };
|
|
816
912
|
}
|
|
817
913
|
}
|
|
818
|
-
const { BarretenbergVerifier } = await Promise.resolve().then(() => __importStar(require("@aztec/bb.js")));
|
|
914
|
+
const { BarretenbergVerifier } = await Promise.resolve().then(() => tslib_1.__importStar(require("@aztec/bb.js")));
|
|
819
915
|
const verifier = new BarretenbergVerifier();
|
|
820
916
|
/*if (!this.wasmVerifierInit) {
|
|
821
917
|
await this.initWasmVerifier()
|
|
@@ -872,6 +968,8 @@ class ZKPassport {
|
|
|
872
968
|
delete this.topicToConfig[requestId];
|
|
873
969
|
delete this.topicToSharedSecret[requestId];
|
|
874
970
|
delete this.topicToProofs[requestId];
|
|
971
|
+
delete this.topicToExpectedProofCount[requestId];
|
|
972
|
+
delete this.topicToResults[requestId];
|
|
875
973
|
this.onRequestReceivedCallbacks[requestId] = [];
|
|
876
974
|
this.onGeneratingProofCallbacks[requestId] = [];
|
|
877
975
|
this.onBridgeConnectCallbacks[requestId] = [];
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -134,6 +134,8 @@ export declare class ZKPassport {
|
|
|
134
134
|
private topicToRequestReceived;
|
|
135
135
|
private topicToService;
|
|
136
136
|
private topicToProofs;
|
|
137
|
+
private topicToExpectedProofCount;
|
|
138
|
+
private topicToResults;
|
|
137
139
|
private onRequestReceivedCallbacks;
|
|
138
140
|
private onGeneratingProofCallbacks;
|
|
139
141
|
private onBridgeConnectCallbacks;
|
|
@@ -142,6 +144,8 @@ export declare class ZKPassport {
|
|
|
142
144
|
private onRejectCallbacks;
|
|
143
145
|
private onErrorCallbacks;
|
|
144
146
|
constructor(_domain?: string);
|
|
147
|
+
private handleResult;
|
|
148
|
+
private setExpectedProofCount;
|
|
145
149
|
/**
|
|
146
150
|
* @notice Handle an encrypted message.
|
|
147
151
|
* @param request The request.
|
package/dist/esm/index.js
CHANGED
|
@@ -9,7 +9,22 @@ import { noLogger as logger } from "./logger";
|
|
|
9
9
|
import { ungzip } from "node-gzip";
|
|
10
10
|
//import initNoirC from '@noir-lang/noirc_abi'
|
|
11
11
|
//import initACVM from '@noir-lang/acvm_js'
|
|
12
|
-
|
|
12
|
+
import i18en from "i18n-iso-countries/langs/en.json";
|
|
13
|
+
registerLocale(i18en);
|
|
14
|
+
function hasRequestedAccessToField(credentialsRequest, field) {
|
|
15
|
+
const fieldValue = credentialsRequest[field];
|
|
16
|
+
const isDefined = fieldValue !== undefined && fieldValue !== null;
|
|
17
|
+
if (!isDefined) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
for (const key in fieldValue) {
|
|
21
|
+
if (fieldValue[key] !== undefined &&
|
|
22
|
+
fieldValue[key] !== null) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
13
28
|
function normalizeCountry(country) {
|
|
14
29
|
let normalizedCountry;
|
|
15
30
|
const alpha3 = getAlpha3Code(country, "en");
|
|
@@ -45,6 +60,8 @@ export class ZKPassport {
|
|
|
45
60
|
this.topicToRequestReceived = {};
|
|
46
61
|
this.topicToService = {};
|
|
47
62
|
this.topicToProofs = {};
|
|
63
|
+
this.topicToExpectedProofCount = {};
|
|
64
|
+
this.topicToResults = {};
|
|
48
65
|
this.onRequestReceivedCallbacks = {};
|
|
49
66
|
this.onGeneratingProofCallbacks = {};
|
|
50
67
|
this.onBridgeConnectCallbacks = {};
|
|
@@ -63,6 +80,70 @@ export class ZKPassport {
|
|
|
63
80
|
await Promise.all([initACVM(acvm), initNoirC(noirc)])
|
|
64
81
|
this.wasmVerifierInit = true
|
|
65
82
|
}*/
|
|
83
|
+
async handleResult(topic) {
|
|
84
|
+
const result = this.topicToResults[topic];
|
|
85
|
+
// Clear the results straight away to avoid concurrency issues
|
|
86
|
+
delete this.topicToResults[topic];
|
|
87
|
+
// Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
|
|
88
|
+
const { uniqueIdentifier, verified } = await this.verify(topic, this.topicToProofs[topic], result);
|
|
89
|
+
await Promise.all(this.onResultCallbacks[topic].map((callback) => callback({
|
|
90
|
+
uniqueIdentifier,
|
|
91
|
+
verified,
|
|
92
|
+
result,
|
|
93
|
+
})));
|
|
94
|
+
// Clear the expected proof count
|
|
95
|
+
delete this.topicToExpectedProofCount[topic];
|
|
96
|
+
}
|
|
97
|
+
setExpectedProofCount(topic) {
|
|
98
|
+
const fields = Object.keys(this.topicToConfig[topic]).filter((key) => hasRequestedAccessToField(this.topicToConfig[topic], key));
|
|
99
|
+
const neededCircuits = [];
|
|
100
|
+
// Determine which circuits are needed based on the requested fields
|
|
101
|
+
for (const field of fields) {
|
|
102
|
+
for (const key in this.topicToConfig[topic][field]) {
|
|
103
|
+
switch (key) {
|
|
104
|
+
case "eq":
|
|
105
|
+
case "disclose":
|
|
106
|
+
if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
|
|
107
|
+
neededCircuits.push("disclose_bytes");
|
|
108
|
+
}
|
|
109
|
+
else if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
110
|
+
neededCircuits.push("compare_age");
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case "gte":
|
|
114
|
+
case "gt":
|
|
115
|
+
case "lte":
|
|
116
|
+
case "lt":
|
|
117
|
+
case "range":
|
|
118
|
+
if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
119
|
+
neededCircuits.push("compare_age");
|
|
120
|
+
}
|
|
121
|
+
else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
|
|
122
|
+
neededCircuits.push("compare_expiry");
|
|
123
|
+
}
|
|
124
|
+
else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
|
|
125
|
+
neededCircuits.push("compare_birthdate");
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case "in":
|
|
129
|
+
if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
|
|
130
|
+
neededCircuits.push("inclusion_check_country");
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case "out":
|
|
134
|
+
if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
|
|
135
|
+
neededCircuits.push("exclusion_check_country");
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// From the circuits needed, determine the expected proof count
|
|
142
|
+
// There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
|
|
143
|
+
// Each separate needed circuit adds 1 disclosure proof
|
|
144
|
+
this.topicToExpectedProofCount[topic] =
|
|
145
|
+
neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length;
|
|
146
|
+
}
|
|
66
147
|
/**
|
|
67
148
|
* @notice Handle an encrypted message.
|
|
68
149
|
* @param request The request.
|
|
@@ -95,19 +176,44 @@ export class ZKPassport {
|
|
|
95
176
|
};
|
|
96
177
|
this.topicToProofs[topic].push(processedProof);
|
|
97
178
|
await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
|
|
179
|
+
// If the results were received before all the proofs were generated,
|
|
180
|
+
// we can handle the result now
|
|
181
|
+
if (this.topicToResults[topic] &&
|
|
182
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
183
|
+
await this.handleResult(topic);
|
|
184
|
+
}
|
|
98
185
|
}
|
|
99
186
|
else if (request.method === "done") {
|
|
100
187
|
logger.debug(`User sent the query result`);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
})));
|
|
188
|
+
this.topicToResults[topic] = request.params;
|
|
189
|
+
// Make sure all the proofs have been received, otherwise we'll handle the result later
|
|
190
|
+
// once the proofs have all been received
|
|
191
|
+
if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
192
|
+
await this.handleResult(topic);
|
|
193
|
+
}
|
|
108
194
|
}
|
|
109
195
|
else if (request.method === "error") {
|
|
110
|
-
|
|
196
|
+
const error = request.params.error;
|
|
197
|
+
if (error && error === "This ID is not supported yet") {
|
|
198
|
+
// This means the user has an ID that is not supported yet
|
|
199
|
+
// So we won't receive any proofs and we can handle the result now
|
|
200
|
+
this.topicToExpectedProofCount[topic] = 0;
|
|
201
|
+
if (this.topicToResults[topic]) {
|
|
202
|
+
await this.handleResult(topic);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (error && error.startsWith("Cannot generate proof")) {
|
|
206
|
+
// This means one of the disclosure proofs failed to be generated
|
|
207
|
+
// So we need to remove one from the expected proof count
|
|
208
|
+
this.topicToExpectedProofCount[topic] -= 1;
|
|
209
|
+
// If the expected proof count is now equal to the number of proofs received
|
|
210
|
+
// and the results were received, we can handle the result now
|
|
211
|
+
if (this.topicToResults[topic] &&
|
|
212
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
213
|
+
await this.handleResult(topic);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)));
|
|
111
217
|
}
|
|
112
218
|
}
|
|
113
219
|
getZkPassportRequest(topic) {
|
|
@@ -163,6 +269,7 @@ export class ZKPassport {
|
|
|
163
269
|
const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
|
|
164
270
|
const base64Service = Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
|
|
165
271
|
const pubkey = bytesToHex(this.topicToKeyPair[topic].publicKey);
|
|
272
|
+
this.setExpectedProofCount(topic);
|
|
166
273
|
return {
|
|
167
274
|
url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
|
|
168
275
|
requestId: topic,
|
|
@@ -193,6 +300,7 @@ export class ZKPassport {
|
|
|
193
300
|
this.topicToConfig[topic] = {};
|
|
194
301
|
this.topicToService[topic] = { name, logo, purpose, scope };
|
|
195
302
|
this.topicToProofs[topic] = [];
|
|
303
|
+
this.topicToExpectedProofCount[topic] = 0;
|
|
196
304
|
this.onRequestReceivedCallbacks[topic] = [];
|
|
197
305
|
this.onGeneratingProofCallbacks[topic] = [];
|
|
198
306
|
this.onBridgeConnectCallbacks[topic] = [];
|
|
@@ -734,6 +842,16 @@ export class ZKPassport {
|
|
|
734
842
|
isCorrect = false;
|
|
735
843
|
break;
|
|
736
844
|
}
|
|
845
|
+
// Check the countryList is in ascending order
|
|
846
|
+
// If the prover doesn't use a sorted list then the proof cannot be trusted
|
|
847
|
+
// as it is requirement in the circuit for the exclusion check to work
|
|
848
|
+
for (let i = 1; i < countryList.length; i++) {
|
|
849
|
+
if (countryList[i] < countryList[i - 1]) {
|
|
850
|
+
console.warn("The nationality exclusion list has not been sorted, and thus the proof cannot be trusted");
|
|
851
|
+
isCorrect = false;
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
737
855
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
738
856
|
}
|
|
739
857
|
else if (proof.name === "inclusion_check_country") {
|
|
@@ -778,7 +896,7 @@ export class ZKPassport {
|
|
|
778
896
|
proofsToVerify = this.topicToProofs[requestId];
|
|
779
897
|
if (!proofsToVerify || proofsToVerify.length < 4) {
|
|
780
898
|
// It may happen that a request returns a result without proofs
|
|
781
|
-
// Meaning the ID is supported yet by ZKPassport circuits,
|
|
899
|
+
// Meaning the ID is not supported yet by ZKPassport circuits,
|
|
782
900
|
// so the results has to be trusted and cannot be independently verified
|
|
783
901
|
return { uniqueIdentifier: undefined, verified: false };
|
|
784
902
|
}
|
|
@@ -840,6 +958,8 @@ export class ZKPassport {
|
|
|
840
958
|
delete this.topicToConfig[requestId];
|
|
841
959
|
delete this.topicToSharedSecret[requestId];
|
|
842
960
|
delete this.topicToProofs[requestId];
|
|
961
|
+
delete this.topicToExpectedProofCount[requestId];
|
|
962
|
+
delete this.topicToResults[requestId];
|
|
843
963
|
this.onRequestReceivedCallbacks[requestId] = [];
|
|
844
964
|
this.onGeneratingProofCallbacks[requestId] = [];
|
|
845
965
|
this.onBridgeConnectCallbacks[requestId] = [];
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
DisclosedData,
|
|
31
31
|
formatName,
|
|
32
32
|
getHostedPackagedCircuitByName,
|
|
33
|
+
Query,
|
|
33
34
|
} from "@zkpassport/utils"
|
|
34
35
|
import { bytesToHex } from "@noble/ciphers/utils"
|
|
35
36
|
import { getWebSocketClient, WebSocketClient } from "./websocket"
|
|
@@ -39,8 +40,26 @@ import { noLogger as logger } from "./logger"
|
|
|
39
40
|
import { ungzip } from "node-gzip"
|
|
40
41
|
//import initNoirC from '@noir-lang/noirc_abi'
|
|
41
42
|
//import initACVM from '@noir-lang/acvm_js'
|
|
43
|
+
import i18en from "i18n-iso-countries/langs/en.json"
|
|
42
44
|
|
|
43
|
-
registerLocale(
|
|
45
|
+
registerLocale(i18en)
|
|
46
|
+
|
|
47
|
+
function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
|
|
48
|
+
const fieldValue = credentialsRequest[field as keyof Query]
|
|
49
|
+
const isDefined = fieldValue !== undefined && fieldValue !== null
|
|
50
|
+
if (!isDefined) {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
for (const key in fieldValue) {
|
|
54
|
+
if (
|
|
55
|
+
fieldValue[key as keyof typeof fieldValue] !== undefined &&
|
|
56
|
+
fieldValue[key as keyof typeof fieldValue] !== null
|
|
57
|
+
) {
|
|
58
|
+
return true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
44
63
|
|
|
45
64
|
function normalizeCountry(country: CountryName | Alpha3Code) {
|
|
46
65
|
let normalizedCountry: Alpha3Code | undefined
|
|
@@ -241,6 +260,8 @@ export class ZKPassport {
|
|
|
241
260
|
{ name: string; logo: string; purpose: string; scope?: string }
|
|
242
261
|
> = {}
|
|
243
262
|
private topicToProofs: Record<string, Array<ProofResult>> = {}
|
|
263
|
+
private topicToExpectedProofCount: Record<string, number> = {}
|
|
264
|
+
private topicToResults: Record<string, QueryResult> = {}
|
|
244
265
|
|
|
245
266
|
private onRequestReceivedCallbacks: Record<string, Array<() => void>> = {}
|
|
246
267
|
private onGeneratingProofCallbacks: Record<string, Array<(topic: string) => void>> = {}
|
|
@@ -274,6 +295,79 @@ export class ZKPassport {
|
|
|
274
295
|
this.wasmVerifierInit = true
|
|
275
296
|
}*/
|
|
276
297
|
|
|
298
|
+
private async handleResult(topic: string) {
|
|
299
|
+
const result = this.topicToResults[topic]
|
|
300
|
+
// Clear the results straight away to avoid concurrency issues
|
|
301
|
+
delete this.topicToResults[topic]
|
|
302
|
+
// Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
|
|
303
|
+
const { uniqueIdentifier, verified } = await this.verify(
|
|
304
|
+
topic,
|
|
305
|
+
this.topicToProofs[topic],
|
|
306
|
+
result,
|
|
307
|
+
)
|
|
308
|
+
await Promise.all(
|
|
309
|
+
this.onResultCallbacks[topic].map((callback) =>
|
|
310
|
+
callback({
|
|
311
|
+
uniqueIdentifier,
|
|
312
|
+
verified,
|
|
313
|
+
result,
|
|
314
|
+
}),
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
// Clear the expected proof count
|
|
318
|
+
delete this.topicToExpectedProofCount[topic]
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private setExpectedProofCount(topic: string) {
|
|
322
|
+
const fields = Object.keys(this.topicToConfig[topic] as Query).filter((key) =>
|
|
323
|
+
hasRequestedAccessToField(this.topicToConfig[topic] as Query, key as IDCredential),
|
|
324
|
+
)
|
|
325
|
+
const neededCircuits: string[] = []
|
|
326
|
+
// Determine which circuits are needed based on the requested fields
|
|
327
|
+
for (const field of fields) {
|
|
328
|
+
for (const key in this.topicToConfig[topic][field as IDCredential]) {
|
|
329
|
+
switch (key) {
|
|
330
|
+
case "eq":
|
|
331
|
+
case "disclose":
|
|
332
|
+
if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
|
|
333
|
+
neededCircuits.push("disclose_bytes")
|
|
334
|
+
} else if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
335
|
+
neededCircuits.push("compare_age")
|
|
336
|
+
}
|
|
337
|
+
break
|
|
338
|
+
case "gte":
|
|
339
|
+
case "gt":
|
|
340
|
+
case "lte":
|
|
341
|
+
case "lt":
|
|
342
|
+
case "range":
|
|
343
|
+
if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
344
|
+
neededCircuits.push("compare_age")
|
|
345
|
+
} else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
|
|
346
|
+
neededCircuits.push("compare_expiry")
|
|
347
|
+
} else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
|
|
348
|
+
neededCircuits.push("compare_birthdate")
|
|
349
|
+
}
|
|
350
|
+
break
|
|
351
|
+
case "in":
|
|
352
|
+
if (field === "nationality" && !neededCircuits.includes("inclusion_check_country")) {
|
|
353
|
+
neededCircuits.push("inclusion_check_country")
|
|
354
|
+
}
|
|
355
|
+
break
|
|
356
|
+
case "out":
|
|
357
|
+
if (field === "nationality" && !neededCircuits.includes("exclusion_check_country")) {
|
|
358
|
+
neededCircuits.push("exclusion_check_country")
|
|
359
|
+
}
|
|
360
|
+
break
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// From the circuits needed, determine the expected proof count
|
|
365
|
+
// There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
|
|
366
|
+
// Each separate needed circuit adds 1 disclosure proof
|
|
367
|
+
this.topicToExpectedProofCount[topic] =
|
|
368
|
+
neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length
|
|
369
|
+
}
|
|
370
|
+
|
|
277
371
|
/**
|
|
278
372
|
* @notice Handle an encrypted message.
|
|
279
373
|
* @param request The request.
|
|
@@ -310,27 +404,45 @@ export class ZKPassport {
|
|
|
310
404
|
await Promise.all(
|
|
311
405
|
this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)),
|
|
312
406
|
)
|
|
407
|
+
// If the results were received before all the proofs were generated,
|
|
408
|
+
// we can handle the result now
|
|
409
|
+
if (
|
|
410
|
+
this.topicToResults[topic] &&
|
|
411
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
|
|
412
|
+
) {
|
|
413
|
+
await this.handleResult(topic)
|
|
414
|
+
}
|
|
313
415
|
} else if (request.method === "done") {
|
|
314
416
|
logger.debug(`User sent the query result`)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
await Promise.all(
|
|
322
|
-
this.onResultCallbacks[topic].map((callback) =>
|
|
323
|
-
callback({
|
|
324
|
-
uniqueIdentifier,
|
|
325
|
-
verified,
|
|
326
|
-
result: request.params,
|
|
327
|
-
}),
|
|
328
|
-
),
|
|
329
|
-
)
|
|
417
|
+
this.topicToResults[topic] = request.params
|
|
418
|
+
// Make sure all the proofs have been received, otherwise we'll handle the result later
|
|
419
|
+
// once the proofs have all been received
|
|
420
|
+
if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
421
|
+
await this.handleResult(topic)
|
|
422
|
+
}
|
|
330
423
|
} else if (request.method === "error") {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
424
|
+
const error = request.params.error
|
|
425
|
+
if (error && error === "This ID is not supported yet") {
|
|
426
|
+
// This means the user has an ID that is not supported yet
|
|
427
|
+
// So we won't receive any proofs and we can handle the result now
|
|
428
|
+
this.topicToExpectedProofCount[topic] = 0
|
|
429
|
+
if (this.topicToResults[topic]) {
|
|
430
|
+
await this.handleResult(topic)
|
|
431
|
+
}
|
|
432
|
+
} else if (error && error.startsWith("Cannot generate proof")) {
|
|
433
|
+
// This means one of the disclosure proofs failed to be generated
|
|
434
|
+
// So we need to remove one from the expected proof count
|
|
435
|
+
this.topicToExpectedProofCount[topic] -= 1
|
|
436
|
+
// If the expected proof count is now equal to the number of proofs received
|
|
437
|
+
// and the results were received, we can handle the result now
|
|
438
|
+
if (
|
|
439
|
+
this.topicToResults[topic] &&
|
|
440
|
+
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
|
|
441
|
+
) {
|
|
442
|
+
await this.handleResult(topic)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)))
|
|
334
446
|
}
|
|
335
447
|
}
|
|
336
448
|
|
|
@@ -395,6 +507,7 @@ export class ZKPassport {
|
|
|
395
507
|
"base64",
|
|
396
508
|
)
|
|
397
509
|
const pubkey = bytesToHex(this.topicToKeyPair[topic].publicKey)
|
|
510
|
+
this.setExpectedProofCount(topic)
|
|
398
511
|
return {
|
|
399
512
|
url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}`,
|
|
400
513
|
requestId: topic,
|
|
@@ -453,6 +566,7 @@ export class ZKPassport {
|
|
|
453
566
|
this.topicToConfig[topic] = {}
|
|
454
567
|
this.topicToService[topic] = { name, logo, purpose, scope }
|
|
455
568
|
this.topicToProofs[topic] = []
|
|
569
|
+
this.topicToExpectedProofCount[topic] = 0
|
|
456
570
|
|
|
457
571
|
this.onRequestReceivedCallbacks[topic] = []
|
|
458
572
|
this.onGeneratingProofCallbacks[topic] = []
|
|
@@ -1107,6 +1221,18 @@ export class ZKPassport {
|
|
|
1107
1221
|
isCorrect = false
|
|
1108
1222
|
break
|
|
1109
1223
|
}
|
|
1224
|
+
// Check the countryList is in ascending order
|
|
1225
|
+
// If the prover doesn't use a sorted list then the proof cannot be trusted
|
|
1226
|
+
// as it is requirement in the circuit for the exclusion check to work
|
|
1227
|
+
for (let i = 1; i < countryList.length; i++) {
|
|
1228
|
+
if (countryList[i] < countryList[i - 1]) {
|
|
1229
|
+
console.warn(
|
|
1230
|
+
"The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
|
|
1231
|
+
)
|
|
1232
|
+
isCorrect = false
|
|
1233
|
+
break
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1110
1236
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
1111
1237
|
} else if (proof.name === "inclusion_check_country") {
|
|
1112
1238
|
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
@@ -1160,7 +1286,7 @@ export class ZKPassport {
|
|
|
1160
1286
|
proofsToVerify = this.topicToProofs[requestId]
|
|
1161
1287
|
if (!proofsToVerify || proofsToVerify.length < 4) {
|
|
1162
1288
|
// It may happen that a request returns a result without proofs
|
|
1163
|
-
// Meaning the ID is supported yet by ZKPassport circuits,
|
|
1289
|
+
// Meaning the ID is not supported yet by ZKPassport circuits,
|
|
1164
1290
|
// so the results has to be trusted and cannot be independently verified
|
|
1165
1291
|
return { uniqueIdentifier: undefined, verified: false }
|
|
1166
1292
|
}
|
|
@@ -1231,6 +1357,8 @@ export class ZKPassport {
|
|
|
1231
1357
|
delete this.topicToConfig[requestId]
|
|
1232
1358
|
delete this.topicToSharedSecret[requestId]
|
|
1233
1359
|
delete this.topicToProofs[requestId]
|
|
1360
|
+
delete this.topicToExpectedProofCount[requestId]
|
|
1361
|
+
delete this.topicToResults[requestId]
|
|
1234
1362
|
this.onRequestReceivedCallbacks[requestId] = []
|
|
1235
1363
|
this.onGeneratingProofCallbacks[requestId] = []
|
|
1236
1364
|
this.onBridgeConnectCallbacks[requestId] = []
|