@zkpassport/sdk 0.3.4 → 0.4.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 +56 -14
- package/dist/cjs/index.d.ts +19 -7
- package/dist/cjs/index.js +192 -104
- package/dist/esm/assets/abi/ZKPassportVerifier.json +56 -14
- package/dist/esm/index.d.ts +19 -7
- package/dist/esm/index.js +193 -105
- package/package.json +3 -2
- package/src/assets/abi/ZKPassportVerifier.json +56 -14
- package/src/index.ts +221 -120
- package/src/encryption.ts +0 -45
- package/src/json-rpc.ts +0 -61
- package/src/mobile.ts +0 -186
- package/src/websocket.ts +0 -16
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode } from "@zkpassport/utils";
|
|
1
|
+
import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode, BoundData } from "@zkpassport/utils";
|
|
2
2
|
export type QueryResultError<T> = {
|
|
3
3
|
expected?: T;
|
|
4
4
|
received?: T;
|
|
5
5
|
message: string;
|
|
6
6
|
};
|
|
7
7
|
export type QueryResultErrors = {
|
|
8
|
-
[key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose"]: {
|
|
8
|
+
[key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose" | "bind"]: {
|
|
9
9
|
disclose?: QueryResultError<string | number | Date>;
|
|
10
10
|
gte?: QueryResultError<number | Date>;
|
|
11
11
|
lte?: QueryResultError<number | Date>;
|
|
@@ -150,6 +150,12 @@ export type QueryBuilder = {
|
|
|
150
150
|
* @param key The attribute to disclose.
|
|
151
151
|
*/
|
|
152
152
|
disclose: (key: DisclosableIDCredential) => QueryBuilder;
|
|
153
|
+
/**
|
|
154
|
+
* Binds a value to the request.
|
|
155
|
+
* @param key The key of the value to bind.
|
|
156
|
+
* @param value The value to bind the request to.
|
|
157
|
+
*/
|
|
158
|
+
bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder;
|
|
153
159
|
/**
|
|
154
160
|
* Builds the request.
|
|
155
161
|
*
|
|
@@ -163,9 +169,8 @@ export declare class ZKPassport {
|
|
|
163
169
|
private domain;
|
|
164
170
|
private topicToConfig;
|
|
165
171
|
private topicToLocalConfig;
|
|
166
|
-
private
|
|
167
|
-
private
|
|
168
|
-
private topicToSharedSecret;
|
|
172
|
+
private topicToPublicKey;
|
|
173
|
+
private topicToBridge;
|
|
169
174
|
private topicToRequestReceived;
|
|
170
175
|
private topicToService;
|
|
171
176
|
private topicToProofs;
|
|
@@ -197,14 +202,16 @@ export declare class ZKPassport {
|
|
|
197
202
|
* @param scope Scope this request to a specific use case
|
|
198
203
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
199
204
|
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
205
|
+
* @param evmChain The EVM chain to use for the request (if using the proof onchain)
|
|
200
206
|
* @returns The query builder object.
|
|
201
207
|
*/
|
|
202
|
-
request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }: {
|
|
208
|
+
request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }: {
|
|
203
209
|
name: string;
|
|
204
210
|
logo: string;
|
|
205
211
|
purpose: string;
|
|
206
212
|
scope?: string;
|
|
207
213
|
mode?: ProofMode;
|
|
214
|
+
evmChain?: EVMChain;
|
|
208
215
|
validity?: number;
|
|
209
216
|
devMode?: boolean;
|
|
210
217
|
topicOverride?: string;
|
|
@@ -223,20 +230,25 @@ export declare class ZKPassport {
|
|
|
223
230
|
private checkIssuingCountryInclusionPublicInputs;
|
|
224
231
|
private checkScopeFromDisclosureProof;
|
|
225
232
|
private checkCertificateRegistryRoot;
|
|
233
|
+
private checkBindPublicInputs;
|
|
226
234
|
private checkPublicInputs;
|
|
227
235
|
/**
|
|
228
236
|
* @notice Verify the proofs received from the mobile app.
|
|
229
237
|
* @param proofs The proofs to verify.
|
|
230
238
|
* @param queryResult The query result to verify against
|
|
231
239
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
240
|
+
* @param scope Scope this request to a specific use case
|
|
241
|
+
* @param evmChain The EVM chain to use for the verification (if using the proof onchain)
|
|
242
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
232
243
|
* @returns An object containing the unique identifier associated to the user
|
|
233
244
|
* and a boolean indicating whether the proofs were successfully verified.
|
|
234
245
|
*/
|
|
235
|
-
verify({ proofs, queryResult, validity, scope, devMode, }: {
|
|
246
|
+
verify({ proofs, queryResult, validity, scope, evmChain, devMode, }: {
|
|
236
247
|
proofs: Array<ProofResult>;
|
|
237
248
|
queryResult: QueryResult;
|
|
238
249
|
validity?: number;
|
|
239
250
|
scope?: string;
|
|
251
|
+
evmChain?: EVMChain;
|
|
240
252
|
devMode?: boolean;
|
|
241
253
|
}): Promise<{
|
|
242
254
|
uniqueIdentifier: string | undefined;
|
package/dist/cjs/index.js
CHANGED
|
@@ -2,21 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZKPassport = exports.MERCOSUR_COUNTRIES = exports.ASEAN_COUNTRIES = exports.SCHENGEN_COUNTRIES = exports.EEA_COUNTRIES = exports.EU_COUNTRIES = exports.SANCTIONED_COUNTRIES = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const crypto_1 = require("crypto");
|
|
6
5
|
const i18n_iso_countries_1 = require("i18n-iso-countries");
|
|
7
6
|
const utils_1 = require("@zkpassport/utils");
|
|
8
7
|
const utils_2 = require("@noble/ciphers/utils");
|
|
9
|
-
const websocket_1 = require("./websocket");
|
|
10
|
-
const json_rpc_1 = require("./json-rpc");
|
|
11
|
-
const encryption_1 = require("./encryption");
|
|
12
8
|
const logger_1 = require("./logger");
|
|
13
|
-
const pako_1 = require("pako");
|
|
14
9
|
const en_json_1 = tslib_1.__importDefault(require("i18n-iso-countries/langs/en.json"));
|
|
15
10
|
const buffer_1 = require("buffer/");
|
|
16
11
|
const sha2_1 = require("@noble/hashes/sha2");
|
|
17
12
|
const utils_3 = require("@noble/hashes/utils");
|
|
18
13
|
const ZKPassportVerifier_json_1 = tslib_1.__importDefault(require("./assets/abi/ZKPassportVerifier.json"));
|
|
19
14
|
const registry_1 = require("@zkpassport/registry");
|
|
15
|
+
const bridge_1 = require("@obsidion/bridge");
|
|
20
16
|
const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
|
|
21
17
|
// If Buffer is not defined, then we use the Buffer from the buffer package
|
|
22
18
|
if (typeof globalThis.Buffer === "undefined") {
|
|
@@ -25,6 +21,24 @@ if (typeof globalThis.Buffer === "undefined") {
|
|
|
25
21
|
window.Buffer = buffer_1.Buffer;
|
|
26
22
|
}
|
|
27
23
|
}
|
|
24
|
+
function getChainIdFromEVMChain(chain) {
|
|
25
|
+
if (chain === "ethereum_sepolia") {
|
|
26
|
+
return 11155111;
|
|
27
|
+
}
|
|
28
|
+
else if (chain === "local_anvil") {
|
|
29
|
+
return 31337;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Unsupported chain: ${chain}`);
|
|
32
|
+
}
|
|
33
|
+
function getEVMChainFromChainId(chainId) {
|
|
34
|
+
if (chainId === 11155111) {
|
|
35
|
+
return "ethereum_sepolia";
|
|
36
|
+
}
|
|
37
|
+
else if (chainId === 31337) {
|
|
38
|
+
return "local_anvil";
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Unsupported chain ID: ${chainId}`);
|
|
41
|
+
}
|
|
28
42
|
(0, i18n_iso_countries_1.registerLocale)(en_json_1.default);
|
|
29
43
|
function hasRequestedAccessToField(credentialsRequest, field) {
|
|
30
44
|
const fieldValue = credentialsRequest[field];
|
|
@@ -79,9 +93,8 @@ class ZKPassport {
|
|
|
79
93
|
constructor(_domain) {
|
|
80
94
|
this.topicToConfig = {};
|
|
81
95
|
this.topicToLocalConfig = {};
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
84
|
-
this.topicToSharedSecret = {};
|
|
96
|
+
this.topicToPublicKey = {};
|
|
97
|
+
this.topicToBridge = {};
|
|
85
98
|
this.topicToRequestReceived = {};
|
|
86
99
|
this.topicToService = {};
|
|
87
100
|
this.topicToProofs = {};
|
|
@@ -110,6 +123,9 @@ class ZKPassport {
|
|
|
110
123
|
queryResult: result,
|
|
111
124
|
validity: this.topicToLocalConfig[topic]?.validity,
|
|
112
125
|
scope: this.topicToService[topic]?.scope,
|
|
126
|
+
evmChain: this.topicToService[topic]?.chainId
|
|
127
|
+
? getEVMChainFromChainId(this.topicToService[topic]?.chainId)
|
|
128
|
+
: undefined,
|
|
113
129
|
devMode: this.topicToLocalConfig[topic]?.devMode,
|
|
114
130
|
});
|
|
115
131
|
delete this.topicToProofs[topic];
|
|
@@ -185,6 +201,9 @@ class ZKPassport {
|
|
|
185
201
|
}
|
|
186
202
|
}
|
|
187
203
|
}
|
|
204
|
+
if (this.topicToConfig[topic].bind) {
|
|
205
|
+
neededCircuits.push("bind");
|
|
206
|
+
}
|
|
188
207
|
// From the circuits needed, determine the expected proof count
|
|
189
208
|
// There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
|
|
190
209
|
// Each separate needed circuit adds 1 disclosure proof
|
|
@@ -197,7 +216,7 @@ class ZKPassport {
|
|
|
197
216
|
* @param request The request.
|
|
198
217
|
* @param outerRequest The outer request.
|
|
199
218
|
*/
|
|
200
|
-
async handleEncryptedMessage(topic, request
|
|
219
|
+
async handleEncryptedMessage(topic, request) {
|
|
201
220
|
logger_1.noLogger.debug("Received encrypted message:", request);
|
|
202
221
|
if (request.method === "accept") {
|
|
203
222
|
logger_1.noLogger.debug(`User accepted the request and is generating a proof`);
|
|
@@ -209,30 +228,8 @@ class ZKPassport {
|
|
|
209
228
|
}
|
|
210
229
|
else if (request.method === "proof") {
|
|
211
230
|
logger_1.noLogger.debug(`User generated proof`);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const bytesCommittedInputs = request.params.committedInputs
|
|
215
|
-
? buffer_1.Buffer.from(request.params.committedInputs, "base64")
|
|
216
|
-
: null;
|
|
217
|
-
const uncompressedProof = (0, pako_1.inflate)(bytesProof);
|
|
218
|
-
const uncompressedCommittedInputs = bytesCommittedInputs
|
|
219
|
-
? (0, pako_1.inflate)(bytesCommittedInputs)
|
|
220
|
-
: null;
|
|
221
|
-
// The gzip lib in the app compress the proof as ASCII
|
|
222
|
-
// and since the app passes the proof as a hex string, we can
|
|
223
|
-
// just decode the bytes as hex characters using the TextDecoder
|
|
224
|
-
const hexProof = new TextDecoder().decode(uncompressedProof);
|
|
225
|
-
const processedProof = {
|
|
226
|
-
proof: hexProof,
|
|
227
|
-
vkeyHash: request.params.vkeyHash,
|
|
228
|
-
name: request.params.name,
|
|
229
|
-
version: request.params.version,
|
|
230
|
-
committedInputs: uncompressedCommittedInputs
|
|
231
|
-
? JSON.parse(new TextDecoder().decode(uncompressedCommittedInputs))
|
|
232
|
-
: undefined,
|
|
233
|
-
};
|
|
234
|
-
this.topicToProofs[topic].push(processedProof);
|
|
235
|
-
await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(processedProof)));
|
|
231
|
+
this.topicToProofs[topic].push(request.params);
|
|
232
|
+
await Promise.all(this.onProofGeneratedCallbacks[topic].map((callback) => callback(request.params)));
|
|
236
233
|
// If the results were received before all the proofs were generated,
|
|
237
234
|
// we can handle the result now
|
|
238
235
|
if (this.topicToResults[topic] &&
|
|
@@ -329,10 +326,17 @@ class ZKPassport {
|
|
|
329
326
|
};
|
|
330
327
|
return this.getZkPassportRequest(topic);
|
|
331
328
|
},
|
|
329
|
+
bind: (key, value) => {
|
|
330
|
+
this.topicToConfig[topic].bind = {
|
|
331
|
+
...this.topicToConfig[topic].bind,
|
|
332
|
+
[key]: value,
|
|
333
|
+
};
|
|
334
|
+
return this.getZkPassportRequest(topic);
|
|
335
|
+
},
|
|
332
336
|
done: () => {
|
|
333
337
|
const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
|
|
334
338
|
const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
|
|
335
|
-
const pubkey =
|
|
339
|
+
const pubkey = this.topicToPublicKey[topic];
|
|
336
340
|
this.setExpectedProofCount(topic);
|
|
337
341
|
return {
|
|
338
342
|
url: `https://zkpassport.id/r?d=${this.domain}&t=${topic}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[topic].mode}`,
|
|
@@ -344,7 +348,7 @@ class ZKPassport {
|
|
|
344
348
|
onResult: (callback) => this.onResultCallbacks[topic].push(callback),
|
|
345
349
|
onReject: (callback) => this.onRejectCallbacks[topic].push(callback),
|
|
346
350
|
onError: (callback) => this.onErrorCallbacks[topic].push(callback),
|
|
347
|
-
isBridgeConnected: () => this.
|
|
351
|
+
isBridgeConnected: () => this.topicToBridge[topic].isBridgeConnected(),
|
|
348
352
|
requestReceived: () => this.topicToRequestReceived[topic] === true,
|
|
349
353
|
};
|
|
350
354
|
},
|
|
@@ -358,17 +362,23 @@ class ZKPassport {
|
|
|
358
362
|
* @param scope Scope this request to a specific use case
|
|
359
363
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
360
364
|
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
365
|
+
* @param evmChain The EVM chain to use for the request (if using the proof onchain)
|
|
361
366
|
* @returns The query builder object.
|
|
362
367
|
*/
|
|
363
|
-
async request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }) {
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
};
|
|
368
|
+
async request({ name, logo, purpose, scope, mode, evmChain, validity, devMode, topicOverride, keyPairOverride, }) {
|
|
369
|
+
const bridge = await bridge_1.Bridge.create({
|
|
370
|
+
keyPair: keyPairOverride,
|
|
371
|
+
bridgeId: topicOverride,
|
|
372
|
+
});
|
|
373
|
+
const topic = bridge.connection.getBridgeId();
|
|
370
374
|
this.topicToConfig[topic] = {};
|
|
371
|
-
this.topicToService[topic] = {
|
|
375
|
+
this.topicToService[topic] = {
|
|
376
|
+
name,
|
|
377
|
+
logo,
|
|
378
|
+
purpose,
|
|
379
|
+
scope,
|
|
380
|
+
chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
|
|
381
|
+
};
|
|
372
382
|
this.topicToProofs[topic] = [];
|
|
373
383
|
this.topicToExpectedProofCount[topic] = 0;
|
|
374
384
|
this.topicToLocalConfig[topic] = {
|
|
@@ -384,53 +394,21 @@ class ZKPassport {
|
|
|
384
394
|
this.onResultCallbacks[topic] = [];
|
|
385
395
|
this.onRejectCallbacks[topic] = [];
|
|
386
396
|
this.onErrorCallbacks[topic] = [];
|
|
387
|
-
|
|
388
|
-
this.
|
|
389
|
-
|
|
390
|
-
logger_1.noLogger.
|
|
397
|
+
this.topicToPublicKey[topic] = bridge.getPublicKey();
|
|
398
|
+
this.topicToBridge[topic] = bridge;
|
|
399
|
+
bridge.onConnect(async (reconnection) => {
|
|
400
|
+
logger_1.noLogger.debug("Bridge connected");
|
|
401
|
+
logger_1.noLogger.debug("Is reconnection:", reconnection);
|
|
391
402
|
await Promise.all(this.onBridgeConnectCallbacks[topic].map((callback) => callback()));
|
|
392
|
-
};
|
|
393
|
-
wsClient.addEventListener("message", async (event) => {
|
|
394
|
-
logger_1.noLogger.debug("[frontend] Received message:", event.data);
|
|
395
|
-
try {
|
|
396
|
-
const data = JSON.parse(event.data);
|
|
397
|
-
// Handshake happens when the mobile app scans the QR code and connects to the bridge
|
|
398
|
-
if (data.method === "handshake") {
|
|
399
|
-
logger_1.noLogger.debug("[frontend] Received handshake:", event.data);
|
|
400
|
-
this.topicToRequestReceived[topic] = true;
|
|
401
|
-
this.topicToSharedSecret[topic] = await (0, encryption_1.getSharedSecret)((0, utils_2.bytesToHex)(keyPair.privateKey), data.params.pubkey);
|
|
402
|
-
logger_1.noLogger.debug("[frontend] Shared secret:", buffer_1.Buffer.from(this.topicToSharedSecret[topic]).toString("hex"));
|
|
403
|
-
const encryptedMessage = await (0, json_rpc_1.createEncryptedJsonRpcRequest)("hello", null, this.topicToSharedSecret[topic], topic);
|
|
404
|
-
logger_1.noLogger.debug("[frontend] Sending encrypted message:", encryptedMessage);
|
|
405
|
-
wsClient.send(JSON.stringify(encryptedMessage));
|
|
406
|
-
await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()));
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
// Handle encrypted messages
|
|
410
|
-
if (data.method === "encryptedMessage") {
|
|
411
|
-
// Decode the payload from base64 to Uint8Array
|
|
412
|
-
const payload = new Uint8Array(atob(data.params.payload)
|
|
413
|
-
.split("")
|
|
414
|
-
.map((c) => c.charCodeAt(0)));
|
|
415
|
-
try {
|
|
416
|
-
// Decrypt the payload using the shared secret
|
|
417
|
-
const decrypted = await (0, encryption_1.decrypt)(payload, this.topicToSharedSecret[topic], topic);
|
|
418
|
-
const decryptedJson = JSON.parse(decrypted);
|
|
419
|
-
this.handleEncryptedMessage(topic, decryptedJson, data);
|
|
420
|
-
}
|
|
421
|
-
catch (error) {
|
|
422
|
-
logger_1.noLogger.error("[frontend] Error decrypting message:", error);
|
|
423
|
-
}
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
catch (error) {
|
|
428
|
-
logger_1.noLogger.error("[frontend] Error:", error);
|
|
429
|
-
}
|
|
430
403
|
});
|
|
431
|
-
|
|
432
|
-
logger_1.noLogger.
|
|
433
|
-
|
|
404
|
+
bridge.onSecureChannelEstablished(async () => {
|
|
405
|
+
logger_1.noLogger.debug("Secure channel established");
|
|
406
|
+
await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()));
|
|
407
|
+
});
|
|
408
|
+
bridge.onSecureMessage(async (message) => {
|
|
409
|
+
logger_1.noLogger.debug("Received message:", message);
|
|
410
|
+
this.handleEncryptedMessage(topic, message);
|
|
411
|
+
});
|
|
434
412
|
return this.getZkPassportRequest(topic);
|
|
435
413
|
}
|
|
436
414
|
checkDiscloseBytesPublicInputs(proof, queryResult) {
|
|
@@ -451,6 +429,7 @@ class ZKPassport {
|
|
|
451
429
|
fullname: {},
|
|
452
430
|
document_number: {},
|
|
453
431
|
outer: {},
|
|
432
|
+
bind: {},
|
|
454
433
|
};
|
|
455
434
|
let isCorrect = true;
|
|
456
435
|
// We can't be certain that the disclosed data is for a passport or an ID card
|
|
@@ -763,6 +742,7 @@ class ZKPassport {
|
|
|
763
742
|
fullname: {},
|
|
764
743
|
document_number: {},
|
|
765
744
|
outer: {},
|
|
745
|
+
bind: {},
|
|
766
746
|
};
|
|
767
747
|
let isCorrect = true;
|
|
768
748
|
const currentTime = new Date();
|
|
@@ -872,6 +852,7 @@ class ZKPassport {
|
|
|
872
852
|
fullname: {},
|
|
873
853
|
document_number: {},
|
|
874
854
|
outer: {},
|
|
855
|
+
bind: {},
|
|
875
856
|
};
|
|
876
857
|
let isCorrect = true;
|
|
877
858
|
const currentTime = new Date();
|
|
@@ -975,6 +956,7 @@ class ZKPassport {
|
|
|
975
956
|
fullname: {},
|
|
976
957
|
document_number: {},
|
|
977
958
|
outer: {},
|
|
959
|
+
bind: {},
|
|
978
960
|
};
|
|
979
961
|
let isCorrect = true;
|
|
980
962
|
const currentTime = new Date();
|
|
@@ -1078,6 +1060,7 @@ class ZKPassport {
|
|
|
1078
1060
|
fullname: {},
|
|
1079
1061
|
document_number: {},
|
|
1080
1062
|
outer: {},
|
|
1063
|
+
bind: {},
|
|
1081
1064
|
};
|
|
1082
1065
|
let isCorrect = true;
|
|
1083
1066
|
if (queryResult.nationality &&
|
|
@@ -1132,6 +1115,7 @@ class ZKPassport {
|
|
|
1132
1115
|
fullname: {},
|
|
1133
1116
|
document_number: {},
|
|
1134
1117
|
outer: {},
|
|
1118
|
+
bind: {},
|
|
1135
1119
|
};
|
|
1136
1120
|
let isCorrect = true;
|
|
1137
1121
|
if (queryResult.issuing_country &&
|
|
@@ -1186,6 +1170,7 @@ class ZKPassport {
|
|
|
1186
1170
|
fullname: {},
|
|
1187
1171
|
document_number: {},
|
|
1188
1172
|
outer: {},
|
|
1173
|
+
bind: {},
|
|
1189
1174
|
};
|
|
1190
1175
|
let isCorrect = true;
|
|
1191
1176
|
if (queryResult.nationality &&
|
|
@@ -1228,6 +1213,7 @@ class ZKPassport {
|
|
|
1228
1213
|
fullname: {},
|
|
1229
1214
|
document_number: {},
|
|
1230
1215
|
outer: {},
|
|
1216
|
+
bind: {},
|
|
1231
1217
|
};
|
|
1232
1218
|
let isCorrect = true;
|
|
1233
1219
|
if (queryResult.issuing_country &&
|
|
@@ -1252,13 +1238,14 @@ class ZKPassport {
|
|
|
1252
1238
|
}
|
|
1253
1239
|
return { isCorrect, queryResultErrors };
|
|
1254
1240
|
}
|
|
1255
|
-
checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope) {
|
|
1241
|
+
checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope, chainId) {
|
|
1256
1242
|
let isCorrect = true;
|
|
1257
|
-
if (this.domain &&
|
|
1243
|
+
if (this.domain &&
|
|
1244
|
+
(0, utils_1.getServiceScopeHash)(this.domain, chainId) !== BigInt(proofData.publicInputs[1])) {
|
|
1258
1245
|
console.warn("The proof comes from a different domain than the one expected");
|
|
1259
1246
|
isCorrect = false;
|
|
1260
1247
|
queryResultErrors[key].scope = {
|
|
1261
|
-
expected: `Scope: ${(0, utils_1.
|
|
1248
|
+
expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
|
|
1262
1249
|
received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
|
|
1263
1250
|
message: "The proof comes from a different domain than the one expected",
|
|
1264
1251
|
};
|
|
@@ -1302,7 +1289,52 @@ class ZKPassport {
|
|
|
1302
1289
|
}
|
|
1303
1290
|
return { isCorrect, queryResultErrors };
|
|
1304
1291
|
}
|
|
1305
|
-
|
|
1292
|
+
checkBindPublicInputs(queryResult, boundData) {
|
|
1293
|
+
const queryResultErrors = {
|
|
1294
|
+
sig_check_dsc: {},
|
|
1295
|
+
sig_check_id_data: {},
|
|
1296
|
+
data_check_integrity: {},
|
|
1297
|
+
disclose: {},
|
|
1298
|
+
age: {},
|
|
1299
|
+
birthdate: {},
|
|
1300
|
+
expiry_date: {},
|
|
1301
|
+
document_type: {},
|
|
1302
|
+
issuing_country: {},
|
|
1303
|
+
gender: {},
|
|
1304
|
+
nationality: {},
|
|
1305
|
+
firstname: {},
|
|
1306
|
+
lastname: {},
|
|
1307
|
+
fullname: {},
|
|
1308
|
+
document_number: {},
|
|
1309
|
+
outer: {},
|
|
1310
|
+
bind: {},
|
|
1311
|
+
};
|
|
1312
|
+
let isCorrect = true;
|
|
1313
|
+
if (queryResult.bind) {
|
|
1314
|
+
if (queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
|
|
1315
|
+
boundData.user_address?.toLowerCase().replace("0x", "")) {
|
|
1316
|
+
console.warn("Bound user address does not match the one from the query results");
|
|
1317
|
+
isCorrect = false;
|
|
1318
|
+
queryResultErrors.bind.eq = {
|
|
1319
|
+
expected: queryResult.bind.user_address,
|
|
1320
|
+
received: boundData.user_address,
|
|
1321
|
+
message: "Bound user address does not match the one from the query results",
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
if (queryResult.bind.custom_data?.trim().toLowerCase() !==
|
|
1325
|
+
boundData.custom_data?.trim().toLowerCase()) {
|
|
1326
|
+
console.warn("Bound custom data does not match the one from the query results");
|
|
1327
|
+
isCorrect = false;
|
|
1328
|
+
queryResultErrors.bind.eq = {
|
|
1329
|
+
expected: queryResult.bind.custom_data,
|
|
1330
|
+
received: boundData.custom_data,
|
|
1331
|
+
message: "Bound custom data does not match the one from the query results",
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return { isCorrect, queryResultErrors };
|
|
1336
|
+
}
|
|
1337
|
+
async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
|
|
1306
1338
|
let commitmentIn;
|
|
1307
1339
|
let commitmentOut;
|
|
1308
1340
|
let isCorrect = true;
|
|
@@ -1326,6 +1358,7 @@ class ZKPassport {
|
|
|
1326
1358
|
fullname: {},
|
|
1327
1359
|
document_number: {},
|
|
1328
1360
|
outer: {},
|
|
1361
|
+
bind: {},
|
|
1329
1362
|
};
|
|
1330
1363
|
// Since the order is important for the commitments, we need to sort the proofs
|
|
1331
1364
|
// by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
|
|
@@ -1342,6 +1375,7 @@ class ZKPassport {
|
|
|
1342
1375
|
"inclusion_check_nationality",
|
|
1343
1376
|
"exclusion_check_issuing_country",
|
|
1344
1377
|
"inclusion_check_issuing_country",
|
|
1378
|
+
"bind",
|
|
1345
1379
|
];
|
|
1346
1380
|
const getIndex = (proof) => {
|
|
1347
1381
|
const name = proof.name || "";
|
|
@@ -1387,11 +1421,12 @@ class ZKPassport {
|
|
|
1387
1421
|
message: "The proof does not verify all the requested conditions and information",
|
|
1388
1422
|
};
|
|
1389
1423
|
}
|
|
1390
|
-
if (this.domain &&
|
|
1424
|
+
if (this.domain &&
|
|
1425
|
+
(0, utils_1.getServiceScopeHash)(this.domain, chainId) !== (0, utils_1.getScopeFromOuterProof)(proofData)) {
|
|
1391
1426
|
console.warn("The proof comes from a different domain than the one expected");
|
|
1392
1427
|
isCorrect = false;
|
|
1393
1428
|
queryResultErrors.outer.scope = {
|
|
1394
|
-
expected: `Scope: ${(0, utils_1.
|
|
1429
|
+
expected: `Scope: ${(0, utils_1.getServiceScopeHash)(this.domain, chainId).toString()}`,
|
|
1395
1430
|
received: `Scope: ${(0, utils_1.getScopeFromOuterProof)(proofData).toString()}`,
|
|
1396
1431
|
message: "The proof comes from a different domain than the one expected",
|
|
1397
1432
|
};
|
|
@@ -1577,6 +1612,27 @@ class ZKPassport {
|
|
|
1577
1612
|
...queryResultErrorsIssuingCountryExclusion,
|
|
1578
1613
|
};
|
|
1579
1614
|
}
|
|
1615
|
+
else if (!!committedInputs?.bind) {
|
|
1616
|
+
const bindCommittedInputs = committedInputs?.bind;
|
|
1617
|
+
const bindParameterCommitment = isForEVM
|
|
1618
|
+
? await (0, utils_1.getBindEVMParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data))
|
|
1619
|
+
: await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
|
|
1620
|
+
if (!paramCommitments.includes(bindParameterCommitment)) {
|
|
1621
|
+
console.warn("This proof does not verify the bound data");
|
|
1622
|
+
isCorrect = false;
|
|
1623
|
+
queryResultErrors.bind.commitment = {
|
|
1624
|
+
expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
|
|
1625
|
+
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
1626
|
+
message: "This proof does not verify the bound data",
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
|
|
1630
|
+
isCorrect = isCorrect && isCorrectBind;
|
|
1631
|
+
queryResultErrors = {
|
|
1632
|
+
...queryResultErrors,
|
|
1633
|
+
...queryResultErrorsBind,
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1580
1636
|
uniqueIdentifier = (0, utils_1.getNullifierFromOuterProof)(proofData).toString(10);
|
|
1581
1637
|
}
|
|
1582
1638
|
else if (proof.name?.startsWith("sig_check_dsc")) {
|
|
@@ -1898,6 +1954,27 @@ class ZKPassport {
|
|
|
1898
1954
|
};
|
|
1899
1955
|
uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
|
|
1900
1956
|
}
|
|
1957
|
+
else if (proof.name === "bind") {
|
|
1958
|
+
const bindCommittedInputs = proof.committedInputs?.bind;
|
|
1959
|
+
const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
|
|
1960
|
+
const calculatedParamCommitment = await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
|
|
1961
|
+
if (paramCommittment !== calculatedParamCommitment) {
|
|
1962
|
+
console.warn("The bound data does not match the one from the proof");
|
|
1963
|
+
isCorrect = false;
|
|
1964
|
+
queryResultErrors.bind.commitment = {
|
|
1965
|
+
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
1966
|
+
received: `Commitment: ${paramCommittment}`,
|
|
1967
|
+
message: "The bound data does not match the one from the proof",
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
|
|
1971
|
+
isCorrect = isCorrect && isCorrectBind;
|
|
1972
|
+
queryResultErrors = {
|
|
1973
|
+
...queryResultErrors,
|
|
1974
|
+
...queryResultErrorsBind,
|
|
1975
|
+
};
|
|
1976
|
+
uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
|
|
1977
|
+
}
|
|
1901
1978
|
}
|
|
1902
1979
|
return { isCorrect, uniqueIdentifier, queryResultErrors };
|
|
1903
1980
|
}
|
|
@@ -1906,10 +1983,13 @@ class ZKPassport {
|
|
|
1906
1983
|
* @param proofs The proofs to verify.
|
|
1907
1984
|
* @param queryResult The query result to verify against
|
|
1908
1985
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
1986
|
+
* @param scope Scope this request to a specific use case
|
|
1987
|
+
* @param evmChain The EVM chain to use for the verification (if using the proof onchain)
|
|
1988
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
1909
1989
|
* @returns An object containing the unique identifier associated to the user
|
|
1910
1990
|
* and a boolean indicating whether the proofs were successfully verified.
|
|
1911
1991
|
*/
|
|
1912
|
-
async verify({ proofs, queryResult, validity, scope, devMode = false, }) {
|
|
1992
|
+
async verify({ proofs, queryResult, validity, scope, evmChain, devMode = false, }) {
|
|
1913
1993
|
// If no proofs were generated, the results can't be trusted.
|
|
1914
1994
|
// We still return it but verified will be false
|
|
1915
1995
|
if (!proofs || proofs.length === 0) {
|
|
@@ -1931,7 +2011,8 @@ class ZKPassport {
|
|
|
1931
2011
|
let verified = true;
|
|
1932
2012
|
let uniqueIdentifier;
|
|
1933
2013
|
let queryResultErrors;
|
|
1934
|
-
const
|
|
2014
|
+
const chainId = evmChain ? getChainIdFromEVMChain(evmChain) : undefined;
|
|
2015
|
+
const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope, chainId);
|
|
1935
2016
|
uniqueIdentifier = uniqueIdentifierFromPublicInputs;
|
|
1936
2017
|
verified = isCorrect;
|
|
1937
2018
|
queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
|
|
@@ -2009,7 +2090,7 @@ class ZKPassport {
|
|
|
2009
2090
|
if (network === "ethereum_sepolia") {
|
|
2010
2091
|
return {
|
|
2011
2092
|
...baseConfig,
|
|
2012
|
-
address: "
|
|
2093
|
+
address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
|
|
2013
2094
|
};
|
|
2014
2095
|
}
|
|
2015
2096
|
else if (network === "local_anvil") {
|
|
@@ -2102,6 +2183,14 @@ class ZKPassport {
|
|
|
2102
2183
|
value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
2103
2184
|
value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
2104
2185
|
}
|
|
2186
|
+
else if (circuitName === "bind_evm") {
|
|
2187
|
+
const value = proof.committedInputs[circuitName];
|
|
2188
|
+
compressedCommittedInputs =
|
|
2189
|
+
utils_1.ProofType.BIND.toString(16).padStart(2, "0") +
|
|
2190
|
+
(0, utils_1.rightPadArrayWithZeros)((0, utils_1.formatBoundData)(value.data), 500)
|
|
2191
|
+
.map((x) => x.toString(16).padStart(2, "0"))
|
|
2192
|
+
.join("");
|
|
2193
|
+
}
|
|
2105
2194
|
else {
|
|
2106
2195
|
throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
|
|
2107
2196
|
}
|
|
@@ -2158,7 +2247,7 @@ class ZKPassport {
|
|
|
2158
2247
|
* @returns The URL of the request.
|
|
2159
2248
|
*/
|
|
2160
2249
|
getUrl(requestId) {
|
|
2161
|
-
const pubkey =
|
|
2250
|
+
const pubkey = this.topicToPublicKey[requestId];
|
|
2162
2251
|
const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString("base64");
|
|
2163
2252
|
const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[requestId])).toString("base64");
|
|
2164
2253
|
return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}`;
|
|
@@ -2168,14 +2257,13 @@ class ZKPassport {
|
|
|
2168
2257
|
* @param requestId The request ID.
|
|
2169
2258
|
*/
|
|
2170
2259
|
cancelRequest(requestId) {
|
|
2171
|
-
if (this.
|
|
2172
|
-
this.
|
|
2173
|
-
delete this.
|
|
2260
|
+
if (this.topicToBridge[requestId]) {
|
|
2261
|
+
this.topicToBridge[requestId].close();
|
|
2262
|
+
delete this.topicToBridge[requestId];
|
|
2174
2263
|
}
|
|
2175
|
-
delete this.
|
|
2264
|
+
delete this.topicToPublicKey[requestId];
|
|
2176
2265
|
delete this.topicToConfig[requestId];
|
|
2177
2266
|
delete this.topicToLocalConfig[requestId];
|
|
2178
|
-
delete this.topicToSharedSecret[requestId];
|
|
2179
2267
|
delete this.topicToProofs[requestId];
|
|
2180
2268
|
delete this.topicToExpectedProofCount[requestId];
|
|
2181
2269
|
delete this.topicToFailedProofCount[requestId];
|
|
@@ -2191,7 +2279,7 @@ class ZKPassport {
|
|
|
2191
2279
|
* @notice Clears all requests.
|
|
2192
2280
|
*/
|
|
2193
2281
|
clearAllRequests() {
|
|
2194
|
-
for (const requestId in this.
|
|
2282
|
+
for (const requestId in this.topicToBridge) {
|
|
2195
2283
|
this.cancelRequest(requestId);
|
|
2196
2284
|
}
|
|
2197
2285
|
}
|