@zkpassport/sdk 0.3.0 → 0.3.2
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 +178 -31
- package/dist/cjs/index.d.ts +21 -4
- package/dist/cjs/index.js +154 -55
- package/dist/esm/assets/abi/ZKPassportVerifier.json +178 -31
- package/dist/esm/index.d.ts +21 -4
- package/dist/esm/index.js +154 -55
- package/package.json +3 -2
- package/src/assets/abi/ZKPassportVerifier.json +178 -31
- package/src/index.ts +215 -53
package/dist/esm/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type QueryResultErrors = {
|
|
|
17
17
|
commitment?: QueryResultError<string>;
|
|
18
18
|
date?: QueryResultError<string>;
|
|
19
19
|
certificate?: QueryResultError<string>;
|
|
20
|
+
scope?: QueryResultError<string>;
|
|
20
21
|
};
|
|
21
22
|
};
|
|
22
23
|
export type SolidityVerifierParameters = {
|
|
@@ -26,6 +27,9 @@ export type SolidityVerifierParameters = {
|
|
|
26
27
|
committedInputs: string;
|
|
27
28
|
committedInputCounts: number[];
|
|
28
29
|
validityPeriodInDays: number;
|
|
30
|
+
scope: string;
|
|
31
|
+
subscope: string;
|
|
32
|
+
devMode: boolean;
|
|
29
33
|
};
|
|
30
34
|
export type EVMChain = "ethereum_sepolia" | "local_anvil";
|
|
31
35
|
export type * from "@zkpassport/utils";
|
|
@@ -192,15 +196,17 @@ export declare class ZKPassport {
|
|
|
192
196
|
* @param purpose To explain what you want to do with the user's data
|
|
193
197
|
* @param scope Scope this request to a specific use case
|
|
194
198
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
199
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
195
200
|
* @returns The query builder object.
|
|
196
201
|
*/
|
|
197
|
-
request({ name, logo, purpose, scope, mode, validity, topicOverride, keyPairOverride, }: {
|
|
202
|
+
request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }: {
|
|
198
203
|
name: string;
|
|
199
204
|
logo: string;
|
|
200
205
|
purpose: string;
|
|
201
206
|
scope?: string;
|
|
202
207
|
mode?: ProofMode;
|
|
203
208
|
validity?: number;
|
|
209
|
+
devMode?: boolean;
|
|
204
210
|
topicOverride?: string;
|
|
205
211
|
keyPairOverride?: {
|
|
206
212
|
privateKey: Uint8Array;
|
|
@@ -215,6 +221,8 @@ export declare class ZKPassport {
|
|
|
215
221
|
private checkIssuingCountryExclusionPublicInputs;
|
|
216
222
|
private checkNationalityInclusionPublicInputs;
|
|
217
223
|
private checkIssuingCountryInclusionPublicInputs;
|
|
224
|
+
private checkScopeFromDisclosureProof;
|
|
225
|
+
private checkCertificateRegistryRoot;
|
|
218
226
|
private checkPublicInputs;
|
|
219
227
|
/**
|
|
220
228
|
* @notice Verify the proofs received from the mobile app.
|
|
@@ -224,17 +232,20 @@ export declare class ZKPassport {
|
|
|
224
232
|
* @returns An object containing the unique identifier associated to the user
|
|
225
233
|
* and a boolean indicating whether the proofs were successfully verified.
|
|
226
234
|
*/
|
|
227
|
-
verify({ proofs, queryResult, validity, }: {
|
|
235
|
+
verify({ proofs, queryResult, validity, scope, devMode, }: {
|
|
228
236
|
proofs: Array<ProofResult>;
|
|
229
237
|
queryResult: QueryResult;
|
|
230
238
|
validity?: number;
|
|
239
|
+
scope?: string;
|
|
240
|
+
devMode?: boolean;
|
|
231
241
|
}): Promise<{
|
|
232
242
|
uniqueIdentifier: string | undefined;
|
|
233
243
|
verified: boolean;
|
|
234
244
|
queryResultErrors?: QueryResultErrors;
|
|
235
245
|
}>;
|
|
236
246
|
getSolidityVerifierDetails(network: EVMChain): {
|
|
237
|
-
address: string
|
|
247
|
+
address: `0x${string}`;
|
|
248
|
+
functionName: string;
|
|
238
249
|
abi: {
|
|
239
250
|
type: "function" | "event" | "constructor";
|
|
240
251
|
name: string;
|
|
@@ -250,7 +261,13 @@ export declare class ZKPassport {
|
|
|
250
261
|
}[];
|
|
251
262
|
}[];
|
|
252
263
|
};
|
|
253
|
-
getSolidityVerifierParameters(proof
|
|
264
|
+
getSolidityVerifierParameters({ proof, validityPeriodInDays, domain, scope, devMode, }: {
|
|
265
|
+
proof: ProofResult;
|
|
266
|
+
validityPeriodInDays?: number;
|
|
267
|
+
domain?: string;
|
|
268
|
+
scope?: string;
|
|
269
|
+
devMode?: boolean;
|
|
270
|
+
}): SolidityVerifierParameters;
|
|
254
271
|
/**
|
|
255
272
|
* @notice Returns the URL of the request.
|
|
256
273
|
* @param requestId The request ID.
|
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, 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, } from "@zkpassport/utils";
|
|
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,9 +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/
|
|
12
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
13
13
|
import { hexToBytes } from "@noble/hashes/utils";
|
|
14
14
|
import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json";
|
|
15
|
+
import { RegistryClient } from "@zkpassport/registry";
|
|
15
16
|
const DEFAULT_DATE_VALUE = new Date(1111, 10, 11);
|
|
16
17
|
// If Buffer is not defined, then we use the Buffer from the buffer package
|
|
17
18
|
if (typeof globalThis.Buffer === "undefined") {
|
|
@@ -36,9 +37,12 @@ function hasRequestedAccessToField(credentialsRequest, field) {
|
|
|
36
37
|
return false;
|
|
37
38
|
}
|
|
38
39
|
function normalizeCountry(country) {
|
|
40
|
+
if (country === "Zero Knowledge Republic") {
|
|
41
|
+
return "ZKR";
|
|
42
|
+
}
|
|
39
43
|
let normalizedCountry;
|
|
40
44
|
const alpha3 = getAlpha3Code(country, "en");
|
|
41
|
-
normalizedCountry = alpha3 || country;
|
|
45
|
+
normalizedCountry = alpha3 || country || "ZKR";
|
|
42
46
|
return normalizedCountry;
|
|
43
47
|
}
|
|
44
48
|
function numericalCompare(fnName, key, value, requestId, requestIdToConfig) {
|
|
@@ -95,6 +99,8 @@ export class ZKPassport {
|
|
|
95
99
|
proofs: this.topicToProofs[topic],
|
|
96
100
|
queryResult: result,
|
|
97
101
|
validity: this.topicToLocalConfig[topic]?.validity,
|
|
102
|
+
scope: this.topicToService[topic]?.scope,
|
|
103
|
+
devMode: this.topicToLocalConfig[topic]?.devMode,
|
|
98
104
|
});
|
|
99
105
|
delete this.topicToProofs[topic];
|
|
100
106
|
const hasFailedProofs = this.topicToFailedProofCount[topic] > 0;
|
|
@@ -341,9 +347,10 @@ export class ZKPassport {
|
|
|
341
347
|
* @param purpose To explain what you want to do with the user's data
|
|
342
348
|
* @param scope Scope this request to a specific use case
|
|
343
349
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
350
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
344
351
|
* @returns The query builder object.
|
|
345
352
|
*/
|
|
346
|
-
async request({ name, logo, purpose, scope, mode, validity, topicOverride, keyPairOverride, }) {
|
|
353
|
+
async request({ name, logo, purpose, scope, mode, validity, devMode, topicOverride, keyPairOverride, }) {
|
|
347
354
|
const topic = topicOverride || randomBytes(16).toString("hex");
|
|
348
355
|
const keyPair = keyPairOverride || (await generateECDHKeyPair());
|
|
349
356
|
this.topicToKeyPair[topic] = {
|
|
@@ -358,6 +365,7 @@ export class ZKPassport {
|
|
|
358
365
|
// Default to 6 months
|
|
359
366
|
validity: validity || 6 * 30,
|
|
360
367
|
mode: mode || "fast",
|
|
368
|
+
devMode: devMode || false,
|
|
361
369
|
};
|
|
362
370
|
this.onRequestReceivedCallbacks[topic] = [];
|
|
363
371
|
this.onGeneratingProofCallbacks[topic] = [];
|
|
@@ -1234,16 +1242,61 @@ export class ZKPassport {
|
|
|
1234
1242
|
}
|
|
1235
1243
|
return { isCorrect, queryResultErrors };
|
|
1236
1244
|
}
|
|
1237
|
-
|
|
1245
|
+
checkScopeFromDisclosureProof(proofData, queryResultErrors, key, scope) {
|
|
1246
|
+
let isCorrect = true;
|
|
1247
|
+
if (this.domain && getScopeHash(this.domain) !== BigInt(proofData.publicInputs[1])) {
|
|
1248
|
+
console.warn("The proof comes from a different domain than the one expected");
|
|
1249
|
+
isCorrect = false;
|
|
1250
|
+
queryResultErrors[key].scope = {
|
|
1251
|
+
expected: `Scope: ${getScopeHash(this.domain).toString()}`,
|
|
1252
|
+
received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
|
|
1253
|
+
message: "The proof comes from a different domain than the one expected",
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
if (scope && getScopeHash(scope) !== BigInt(proofData.publicInputs[2])) {
|
|
1257
|
+
console.warn("The proof uses a different scope than the one expected");
|
|
1258
|
+
isCorrect = false;
|
|
1259
|
+
queryResultErrors[key].scope = {
|
|
1260
|
+
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
1261
|
+
received: `Scope: ${BigInt(proofData.publicInputs[2]).toString()}`,
|
|
1262
|
+
message: "The proof uses a different scope than the one expected",
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
return { isCorrect, queryResultErrors };
|
|
1266
|
+
}
|
|
1267
|
+
async checkCertificateRegistryRoot(root, queryResultErrors, outer) {
|
|
1268
|
+
let isCorrect = true;
|
|
1269
|
+
try {
|
|
1270
|
+
// Maintained certificate registry settled onchain
|
|
1271
|
+
// Here we use Ethereum Sepolia
|
|
1272
|
+
const registryClient = new RegistryClient({ chainId: 11155111 });
|
|
1273
|
+
await registryClient.getCertificates(`0x${root}`);
|
|
1274
|
+
}
|
|
1275
|
+
catch (error) {
|
|
1276
|
+
console.warn(error);
|
|
1277
|
+
// Check the legacy static roots that were used before the registry was deployed onchain
|
|
1278
|
+
const VALID_CERTIFICATE_REGISTRY_ROOT = [
|
|
1279
|
+
BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
|
|
1280
|
+
BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
|
|
1281
|
+
BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
|
|
1282
|
+
];
|
|
1283
|
+
if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(BigInt(root))) {
|
|
1284
|
+
console.warn("The ID was signed by an unrecognized root certificate");
|
|
1285
|
+
isCorrect = false;
|
|
1286
|
+
queryResultErrors[outer ? "outer" : "sig_check_dsc"].certificate = {
|
|
1287
|
+
expected: `A valid root from ZKPassport Registry`,
|
|
1288
|
+
received: `Got invalid certificate registry root: ${root}`,
|
|
1289
|
+
message: "The ID was signed by an unrecognized root certificate",
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
return { isCorrect, queryResultErrors };
|
|
1294
|
+
}
|
|
1295
|
+
async checkPublicInputs(proofs, queryResult, validity, scope) {
|
|
1238
1296
|
let commitmentIn;
|
|
1239
1297
|
let commitmentOut;
|
|
1240
1298
|
let isCorrect = true;
|
|
1241
1299
|
let uniqueIdentifier;
|
|
1242
|
-
const VALID_CERTIFICATE_REGISTRY_ROOT = [
|
|
1243
|
-
BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
|
|
1244
|
-
BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
|
|
1245
|
-
BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
|
|
1246
|
-
];
|
|
1247
1300
|
const currentTime = new Date();
|
|
1248
1301
|
const today = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate(), 0, 0, 0, 0);
|
|
1249
1302
|
let queryResultErrors = {
|
|
@@ -1291,15 +1344,12 @@ export class ZKPassport {
|
|
|
1291
1344
|
if (proof.name?.startsWith("outer")) {
|
|
1292
1345
|
const isForEVM = proof.name?.startsWith("outer_evm");
|
|
1293
1346
|
const certificateRegistryRoot = getCertificateRegistryRootFromOuterProof(proofData);
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
queryResultErrors
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
message: "The ID was signed by an unrecognized root certificate",
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1347
|
+
const { isCorrect: isCorrectCertificateRegistryRoot, queryResultErrors: queryResultErrorsCertificateRegistryRoot, } = await this.checkCertificateRegistryRoot(certificateRegistryRoot.toString(16), queryResultErrors, true);
|
|
1348
|
+
isCorrect = isCorrect && isCorrectCertificateRegistryRoot;
|
|
1349
|
+
queryResultErrors = {
|
|
1350
|
+
...queryResultErrors,
|
|
1351
|
+
...queryResultErrorsCertificateRegistryRoot,
|
|
1352
|
+
};
|
|
1303
1353
|
const currentDate = getCurrentDateFromOuterProof(proofData);
|
|
1304
1354
|
const todayToCurrentDate = today.getTime() - currentDate.getTime();
|
|
1305
1355
|
const differenceInDays = validity ?? 180;
|
|
@@ -1327,6 +1377,24 @@ export class ZKPassport {
|
|
|
1327
1377
|
message: "The proof does not verify all the requested conditions and information",
|
|
1328
1378
|
};
|
|
1329
1379
|
}
|
|
1380
|
+
if (this.domain && getScopeHash(this.domain) !== getScopeFromOuterProof(proofData)) {
|
|
1381
|
+
console.warn("The proof comes from a different domain than the one expected");
|
|
1382
|
+
isCorrect = false;
|
|
1383
|
+
queryResultErrors.outer.scope = {
|
|
1384
|
+
expected: `Scope: ${getScopeHash(this.domain).toString()}`,
|
|
1385
|
+
received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
|
|
1386
|
+
message: "The proof comes from a different domain than the one expected",
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
if (scope && getScopeHash(scope) !== getSubscopeFromOuterProof(proofData)) {
|
|
1390
|
+
console.warn("The proof uses a different scope than the one expected");
|
|
1391
|
+
isCorrect = false;
|
|
1392
|
+
queryResultErrors.outer.scope = {
|
|
1393
|
+
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
1394
|
+
received: `Scope: ${getSubscopeFromOuterProof(proofData).toString()}`,
|
|
1395
|
+
message: "The proof uses a different scope than the one expected",
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1330
1398
|
if (!!committedInputs?.compare_age) {
|
|
1331
1399
|
const ageCommittedInputs = committedInputs?.compare_age;
|
|
1332
1400
|
const ageParameterCommitment = isForEVM
|
|
@@ -1504,15 +1572,12 @@ export class ZKPassport {
|
|
|
1504
1572
|
else if (proof.name?.startsWith("sig_check_dsc")) {
|
|
1505
1573
|
commitmentOut = getCommitmentFromDSCProof(proofData);
|
|
1506
1574
|
const merkleRoot = getMerkleRootFromDSCProof(proofData);
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
queryResultErrors
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
message: "The ID was signed by an unrecognized root certificate",
|
|
1514
|
-
};
|
|
1515
|
-
}
|
|
1575
|
+
const { isCorrect: isCorrectCertificateRegistryRoot, queryResultErrors: queryResultErrorsCertificateRegistryRoot, } = await this.checkCertificateRegistryRoot(merkleRoot.toString(16), queryResultErrors, false);
|
|
1576
|
+
isCorrect = isCorrect && isCorrectCertificateRegistryRoot;
|
|
1577
|
+
queryResultErrors = {
|
|
1578
|
+
...queryResultErrors,
|
|
1579
|
+
...queryResultErrorsCertificateRegistryRoot,
|
|
1580
|
+
};
|
|
1516
1581
|
}
|
|
1517
1582
|
else if (proof.name?.startsWith("sig_check_id_data")) {
|
|
1518
1583
|
commitmentIn = getCommitmentInFromIDDataProof(proofData);
|
|
@@ -1577,11 +1642,18 @@ export class ZKPassport {
|
|
|
1577
1642
|
message: "The disclosed data does not match the data committed by the proof",
|
|
1578
1643
|
};
|
|
1579
1644
|
}
|
|
1645
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "disclose", scope);
|
|
1646
|
+
isCorrect = isCorrect && isCorrectScope;
|
|
1647
|
+
queryResultErrors = {
|
|
1648
|
+
...queryResultErrors,
|
|
1649
|
+
...queryResultErrorsScope,
|
|
1650
|
+
};
|
|
1580
1651
|
const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } = this.checkDiscloseBytesPublicInputs(proof, queryResult);
|
|
1581
|
-
isCorrect = isCorrect && isCorrectDisclose;
|
|
1652
|
+
isCorrect = isCorrect && isCorrectDisclose && isCorrectScope;
|
|
1582
1653
|
queryResultErrors = {
|
|
1583
1654
|
...queryResultErrors,
|
|
1584
1655
|
...queryResultErrorsDisclose,
|
|
1656
|
+
...queryResultErrorsScope,
|
|
1585
1657
|
};
|
|
1586
1658
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1587
1659
|
}
|
|
@@ -1608,11 +1680,13 @@ export class ZKPassport {
|
|
|
1608
1680
|
message: "The conditions for the age check do not match the conditions checked by the proof",
|
|
1609
1681
|
};
|
|
1610
1682
|
}
|
|
1683
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "age", scope);
|
|
1611
1684
|
const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } = this.checkAgePublicInputs(proof, queryResult);
|
|
1612
|
-
isCorrect = isCorrect && isCorrectAge;
|
|
1685
|
+
isCorrect = isCorrect && isCorrectAge && isCorrectScope;
|
|
1613
1686
|
queryResultErrors = {
|
|
1614
1687
|
...queryResultErrors,
|
|
1615
1688
|
...queryResultErrorsAge,
|
|
1689
|
+
...queryResultErrorsScope,
|
|
1616
1690
|
};
|
|
1617
1691
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1618
1692
|
}
|
|
@@ -1639,11 +1713,13 @@ export class ZKPassport {
|
|
|
1639
1713
|
message: "The conditions for the birthdate check do not match the conditions checked by the proof",
|
|
1640
1714
|
};
|
|
1641
1715
|
}
|
|
1716
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "birthdate", scope);
|
|
1642
1717
|
const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } = this.checkBirthdatePublicInputs(proof, queryResult);
|
|
1643
|
-
isCorrect = isCorrect && isCorrectBirthdate;
|
|
1718
|
+
isCorrect = isCorrect && isCorrectBirthdate && isCorrectScope;
|
|
1644
1719
|
queryResultErrors = {
|
|
1645
1720
|
...queryResultErrors,
|
|
1646
1721
|
...queryResultErrorsBirthdate,
|
|
1722
|
+
...queryResultErrorsScope,
|
|
1647
1723
|
};
|
|
1648
1724
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1649
1725
|
}
|
|
@@ -1670,11 +1746,13 @@ export class ZKPassport {
|
|
|
1670
1746
|
message: "The conditions for the expiry date check do not match the conditions checked by the proof",
|
|
1671
1747
|
};
|
|
1672
1748
|
}
|
|
1749
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "expiry_date", scope);
|
|
1673
1750
|
const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } = this.checkExpiryDatePublicInputs(proof, queryResult);
|
|
1674
|
-
isCorrect = isCorrect && isCorrectExpiryDate;
|
|
1751
|
+
isCorrect = isCorrect && isCorrectExpiryDate && isCorrectScope;
|
|
1675
1752
|
queryResultErrors = {
|
|
1676
1753
|
...queryResultErrors,
|
|
1677
1754
|
...queryResultErrorsExpiryDate,
|
|
1755
|
+
...queryResultErrorsScope,
|
|
1678
1756
|
};
|
|
1679
1757
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1680
1758
|
}
|
|
@@ -1701,11 +1779,13 @@ export class ZKPassport {
|
|
|
1701
1779
|
message: "The committed country list for the exclusion check does not match the one from the proof",
|
|
1702
1780
|
};
|
|
1703
1781
|
}
|
|
1782
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
|
|
1704
1783
|
const { isCorrect: isCorrectNationalityExclusion, queryResultErrors: queryResultErrorsNationalityExclusion, } = this.checkNationalityExclusionPublicInputs(queryResult, countryList);
|
|
1705
|
-
isCorrect = isCorrect && isCorrectNationalityExclusion;
|
|
1784
|
+
isCorrect = isCorrect && isCorrectNationalityExclusion && isCorrectScope;
|
|
1706
1785
|
queryResultErrors = {
|
|
1707
1786
|
...queryResultErrors,
|
|
1708
1787
|
...queryResultErrorsNationalityExclusion,
|
|
1788
|
+
...queryResultErrorsScope,
|
|
1709
1789
|
};
|
|
1710
1790
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1711
1791
|
}
|
|
@@ -1732,11 +1812,13 @@ export class ZKPassport {
|
|
|
1732
1812
|
message: "The committed country list for the issuing country exclusion check does not match the one from the proof",
|
|
1733
1813
|
};
|
|
1734
1814
|
}
|
|
1815
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
|
|
1735
1816
|
const { isCorrect: isCorrectIssuingCountryExclusion, queryResultErrors: queryResultErrorsIssuingCountryExclusion, } = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList);
|
|
1736
|
-
isCorrect = isCorrect && isCorrectIssuingCountryExclusion;
|
|
1817
|
+
isCorrect = isCorrect && isCorrectIssuingCountryExclusion && isCorrectScope;
|
|
1737
1818
|
queryResultErrors = {
|
|
1738
1819
|
...queryResultErrors,
|
|
1739
1820
|
...queryResultErrorsIssuingCountryExclusion,
|
|
1821
|
+
...queryResultErrorsScope,
|
|
1740
1822
|
};
|
|
1741
1823
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1742
1824
|
}
|
|
@@ -1763,11 +1845,13 @@ export class ZKPassport {
|
|
|
1763
1845
|
message: "The committed country list for the nationality inclusion check does not match the one from the proof",
|
|
1764
1846
|
};
|
|
1765
1847
|
}
|
|
1848
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
|
|
1766
1849
|
const { isCorrect: isCorrectNationalityInclusion, queryResultErrors: queryResultErrorsNationalityInclusion, } = this.checkNationalityInclusionPublicInputs(queryResult, countryList);
|
|
1767
|
-
isCorrect = isCorrect && isCorrectNationalityInclusion;
|
|
1850
|
+
isCorrect = isCorrect && isCorrectNationalityInclusion && isCorrectScope;
|
|
1768
1851
|
queryResultErrors = {
|
|
1769
1852
|
...queryResultErrors,
|
|
1770
1853
|
...queryResultErrorsNationalityInclusion,
|
|
1854
|
+
...queryResultErrorsScope,
|
|
1771
1855
|
};
|
|
1772
1856
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1773
1857
|
}
|
|
@@ -1794,11 +1878,13 @@ export class ZKPassport {
|
|
|
1794
1878
|
message: "The committed country list for the issuing country inclusion check does not match the one from the proof",
|
|
1795
1879
|
};
|
|
1796
1880
|
}
|
|
1881
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } = this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope);
|
|
1797
1882
|
const { isCorrect: isCorrectIssuingCountryInclusion, queryResultErrors: queryResultErrorsIssuingCountryInclusion, } = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList);
|
|
1798
|
-
isCorrect = isCorrect && isCorrectIssuingCountryInclusion;
|
|
1883
|
+
isCorrect = isCorrect && isCorrectIssuingCountryInclusion && isCorrectScope;
|
|
1799
1884
|
queryResultErrors = {
|
|
1800
1885
|
...queryResultErrors,
|
|
1801
1886
|
...queryResultErrorsIssuingCountryInclusion,
|
|
1887
|
+
...queryResultErrorsScope,
|
|
1802
1888
|
};
|
|
1803
1889
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10);
|
|
1804
1890
|
}
|
|
@@ -1813,7 +1899,7 @@ export class ZKPassport {
|
|
|
1813
1899
|
* @returns An object containing the unique identifier associated to the user
|
|
1814
1900
|
* and a boolean indicating whether the proofs were successfully verified.
|
|
1815
1901
|
*/
|
|
1816
|
-
async verify({ proofs, queryResult, validity, }) {
|
|
1902
|
+
async verify({ proofs, queryResult, validity, scope, devMode = false, }) {
|
|
1817
1903
|
const formattedResult = queryResult;
|
|
1818
1904
|
// Make sure to reconvert the dates to Date objects
|
|
1819
1905
|
if (formattedResult.birthdate && formattedResult.birthdate.disclose) {
|
|
@@ -1827,10 +1913,17 @@ export class ZKPassport {
|
|
|
1827
1913
|
let verified = true;
|
|
1828
1914
|
let uniqueIdentifier;
|
|
1829
1915
|
let queryResultErrors;
|
|
1830
|
-
const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity);
|
|
1916
|
+
const { isCorrect, uniqueIdentifier: uniqueIdentifierFromPublicInputs, queryResultErrors: queryResultErrorsFromPublicInputs, } = await this.checkPublicInputs(proofs, formattedResult, validity, scope);
|
|
1831
1917
|
uniqueIdentifier = uniqueIdentifierFromPublicInputs;
|
|
1832
1918
|
verified = isCorrect;
|
|
1833
1919
|
queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs;
|
|
1920
|
+
if (uniqueIdentifier && BigInt(uniqueIdentifier) === BigInt(0) && !devMode) {
|
|
1921
|
+
// If the unique identifier is 0 and it is not in dev mode,
|
|
1922
|
+
// the proofs are considered invalid as these are mock proofs only meant
|
|
1923
|
+
// for testing purposes
|
|
1924
|
+
verified = false;
|
|
1925
|
+
console.warn("You are trying to verify a mock proof. This is only allowed in dev mode. To enable dev mode, set the `devMode` parameter to `true` in the request function parameters.");
|
|
1926
|
+
}
|
|
1834
1927
|
// Only proceed with the proof verification if the public inputs are correct
|
|
1835
1928
|
if (verified) {
|
|
1836
1929
|
for (const proof of proofs) {
|
|
@@ -1840,24 +1933,23 @@ export class ZKPassport {
|
|
|
1840
1933
|
try {
|
|
1841
1934
|
const { createPublicClient, http } = await import("viem");
|
|
1842
1935
|
const { sepolia } = await import("viem/chains");
|
|
1843
|
-
const
|
|
1936
|
+
const { address, abi, functionName } = this.getSolidityVerifierDetails("ethereum_sepolia");
|
|
1844
1937
|
const client = createPublicClient({
|
|
1845
1938
|
chain: sepolia,
|
|
1846
1939
|
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
|
|
1847
1940
|
});
|
|
1848
|
-
const params = this.getSolidityVerifierParameters(
|
|
1941
|
+
const params = this.getSolidityVerifierParameters({
|
|
1942
|
+
proof,
|
|
1943
|
+
validityPeriodInDays: validity,
|
|
1944
|
+
domain: this.domain,
|
|
1945
|
+
scope,
|
|
1946
|
+
devMode,
|
|
1947
|
+
});
|
|
1849
1948
|
const result = await client.readContract({
|
|
1850
|
-
address
|
|
1851
|
-
abi
|
|
1852
|
-
functionName
|
|
1853
|
-
args: [
|
|
1854
|
-
params.vkeyHash,
|
|
1855
|
-
params.proof,
|
|
1856
|
-
params.publicInputs,
|
|
1857
|
-
params.committedInputs,
|
|
1858
|
-
params.committedInputCounts,
|
|
1859
|
-
params.validityPeriodInDays,
|
|
1860
|
-
],
|
|
1949
|
+
address,
|
|
1950
|
+
abi,
|
|
1951
|
+
functionName,
|
|
1952
|
+
args: [params],
|
|
1861
1953
|
});
|
|
1862
1954
|
const isVerified = Array.isArray(result) ? Boolean(result[0]) : false;
|
|
1863
1955
|
verified = isVerified;
|
|
@@ -1892,21 +1984,25 @@ export class ZKPassport {
|
|
|
1892
1984
|
return { uniqueIdentifier, verified, queryResultErrors };
|
|
1893
1985
|
}
|
|
1894
1986
|
getSolidityVerifierDetails(network) {
|
|
1987
|
+
const baseConfig = {
|
|
1988
|
+
functionName: "verifyProof",
|
|
1989
|
+
abi: ZKPassportVerifierAbi.abi,
|
|
1990
|
+
};
|
|
1895
1991
|
if (network === "ethereum_sepolia") {
|
|
1896
1992
|
return {
|
|
1897
|
-
|
|
1898
|
-
|
|
1993
|
+
...baseConfig,
|
|
1994
|
+
address: "0x8c6982D77f7a8f60aE3133cA9b2FAA6f3e78c394",
|
|
1899
1995
|
};
|
|
1900
1996
|
}
|
|
1901
1997
|
else if (network === "local_anvil") {
|
|
1902
1998
|
return {
|
|
1999
|
+
...baseConfig,
|
|
1903
2000
|
address: "0x0",
|
|
1904
|
-
abi: ZKPassportVerifierAbi.abi,
|
|
1905
2001
|
};
|
|
1906
2002
|
}
|
|
1907
2003
|
throw new Error(`Unsupported network: ${network}`);
|
|
1908
2004
|
}
|
|
1909
|
-
getSolidityVerifierParameters(proof, validityPeriodInDays = 7) {
|
|
2005
|
+
getSolidityVerifierParameters({ proof, validityPeriodInDays = 7, domain, scope, devMode = false, }) {
|
|
1910
2006
|
if (!proof.name?.startsWith("outer_evm")) {
|
|
1911
2007
|
throw new Error("This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.");
|
|
1912
2008
|
}
|
|
@@ -2032,6 +2128,9 @@ export class ZKPassport {
|
|
|
2032
2128
|
committedInputs: `0x${compressedCommittedInputs}`,
|
|
2033
2129
|
committedInputCounts: committedInputCountsArray,
|
|
2034
2130
|
validityPeriodInDays,
|
|
2131
|
+
scope: domain ?? this.domain,
|
|
2132
|
+
subscope: scope ?? "",
|
|
2133
|
+
devMode,
|
|
2035
2134
|
};
|
|
2036
2135
|
return params;
|
|
2037
2136
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zkpassport/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Privacy-preserving identity verification using passports and ID cards",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"@noble/ciphers": "^1.2.1",
|
|
43
43
|
"@noble/hashes": "^1.7.2",
|
|
44
44
|
"@noble/secp256k1": "^2.2.3",
|
|
45
|
-
"@zkpassport/
|
|
45
|
+
"@zkpassport/registry": "^0.1.8",
|
|
46
|
+
"@zkpassport/utils": "^0.7.3",
|
|
46
47
|
"buffer": "^6.0.3",
|
|
47
48
|
"i18n-iso-countries": "^7.12.0",
|
|
48
49
|
"pako": "^2.1.0",
|