@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/src/index.ts
CHANGED
|
@@ -53,6 +53,10 @@ import {
|
|
|
53
53
|
getCommittedInputCount,
|
|
54
54
|
ProofMode,
|
|
55
55
|
ProofType,
|
|
56
|
+
getScopeHash,
|
|
57
|
+
ProofData,
|
|
58
|
+
getScopeFromOuterProof,
|
|
59
|
+
getSubscopeFromOuterProof,
|
|
56
60
|
} from "@zkpassport/utils"
|
|
57
61
|
import { bytesToHex } from "@noble/ciphers/utils"
|
|
58
62
|
import { getWebSocketClient, WebSocketClient } from "./websocket"
|
|
@@ -62,9 +66,10 @@ import { noLogger as logger } from "./logger"
|
|
|
62
66
|
import { inflate } from "pako"
|
|
63
67
|
import i18en from "i18n-iso-countries/langs/en.json"
|
|
64
68
|
import { Buffer } from "buffer/"
|
|
65
|
-
import { sha256 } from "@noble/hashes/
|
|
69
|
+
import { sha256 } from "@noble/hashes/sha2"
|
|
66
70
|
import { hexToBytes } from "@noble/hashes/utils"
|
|
67
71
|
import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json"
|
|
72
|
+
import { RegistryClient } from "@zkpassport/registry"
|
|
68
73
|
|
|
69
74
|
const DEFAULT_DATE_VALUE = new Date(1111, 10, 11)
|
|
70
75
|
|
|
@@ -101,6 +106,7 @@ export type QueryResultErrors = {
|
|
|
101
106
|
commitment?: QueryResultError<string>
|
|
102
107
|
date?: QueryResultError<string>
|
|
103
108
|
certificate?: QueryResultError<string>
|
|
109
|
+
scope?: QueryResultError<string>
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
|
|
@@ -111,6 +117,9 @@ export type SolidityVerifierParameters = {
|
|
|
111
117
|
committedInputs: string
|
|
112
118
|
committedInputCounts: number[]
|
|
113
119
|
validityPeriodInDays: number
|
|
120
|
+
scope: string
|
|
121
|
+
subscope: string
|
|
122
|
+
devMode: boolean
|
|
114
123
|
}
|
|
115
124
|
|
|
116
125
|
export type EVMChain = "ethereum_sepolia" | "local_anvil"
|
|
@@ -135,9 +144,12 @@ function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredentia
|
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
function normalizeCountry(country: CountryName | Alpha3Code) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
if (country === "Zero Knowledge Republic") {
|
|
148
|
+
return "ZKR"
|
|
149
|
+
}
|
|
150
|
+
let normalizedCountry: Alpha3Code | "ZKR" | undefined
|
|
151
|
+
const alpha3 = getAlpha3Code(country as CountryName, "en") as Alpha3Code | "ZKR" | undefined
|
|
152
|
+
normalizedCountry = alpha3 || (country as Alpha3Code) || "ZKR"
|
|
141
153
|
return normalizedCountry
|
|
142
154
|
}
|
|
143
155
|
|
|
@@ -336,6 +348,7 @@ export class ZKPassport {
|
|
|
336
348
|
{
|
|
337
349
|
validity: number
|
|
338
350
|
mode: ProofMode
|
|
351
|
+
devMode: boolean
|
|
339
352
|
}
|
|
340
353
|
> = {}
|
|
341
354
|
private topicToKeyPair: Record<string, { privateKey: Uint8Array; publicKey: Uint8Array }> = {}
|
|
@@ -386,6 +399,8 @@ export class ZKPassport {
|
|
|
386
399
|
proofs: this.topicToProofs[topic],
|
|
387
400
|
queryResult: result,
|
|
388
401
|
validity: this.topicToLocalConfig[topic]?.validity,
|
|
402
|
+
scope: this.topicToService[topic]?.scope,
|
|
403
|
+
devMode: this.topicToLocalConfig[topic]?.devMode,
|
|
389
404
|
})
|
|
390
405
|
delete this.topicToProofs[topic]
|
|
391
406
|
const hasFailedProofs = this.topicToFailedProofCount[topic] > 0
|
|
@@ -674,6 +689,7 @@ export class ZKPassport {
|
|
|
674
689
|
* @param purpose To explain what you want to do with the user's data
|
|
675
690
|
* @param scope Scope this request to a specific use case
|
|
676
691
|
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
692
|
+
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
677
693
|
* @returns The query builder object.
|
|
678
694
|
*/
|
|
679
695
|
public async request({
|
|
@@ -683,6 +699,7 @@ export class ZKPassport {
|
|
|
683
699
|
scope,
|
|
684
700
|
mode,
|
|
685
701
|
validity,
|
|
702
|
+
devMode,
|
|
686
703
|
topicOverride,
|
|
687
704
|
keyPairOverride,
|
|
688
705
|
}: {
|
|
@@ -692,6 +709,7 @@ export class ZKPassport {
|
|
|
692
709
|
scope?: string
|
|
693
710
|
mode?: ProofMode
|
|
694
711
|
validity?: number
|
|
712
|
+
devMode?: boolean
|
|
695
713
|
topicOverride?: string
|
|
696
714
|
keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
|
|
697
715
|
}): Promise<QueryBuilder> {
|
|
@@ -711,6 +729,7 @@ export class ZKPassport {
|
|
|
711
729
|
// Default to 6 months
|
|
712
730
|
validity: validity || 6 * 30,
|
|
713
731
|
mode: mode || "fast",
|
|
732
|
+
devMode: devMode || false,
|
|
714
733
|
}
|
|
715
734
|
|
|
716
735
|
this.onRequestReceivedCallbacks[topic] = []
|
|
@@ -1756,20 +1775,76 @@ export class ZKPassport {
|
|
|
1756
1775
|
return { isCorrect, queryResultErrors }
|
|
1757
1776
|
}
|
|
1758
1777
|
|
|
1778
|
+
private checkScopeFromDisclosureProof(
|
|
1779
|
+
proofData: ProofData,
|
|
1780
|
+
queryResultErrors: QueryResultErrors,
|
|
1781
|
+
key: string,
|
|
1782
|
+
scope?: string,
|
|
1783
|
+
) {
|
|
1784
|
+
let isCorrect = true
|
|
1785
|
+
if (this.domain && getScopeHash(this.domain) !== BigInt(proofData.publicInputs[1])) {
|
|
1786
|
+
console.warn("The proof comes from a different domain than the one expected")
|
|
1787
|
+
isCorrect = false
|
|
1788
|
+
queryResultErrors[key as keyof QueryResultErrors].scope = {
|
|
1789
|
+
expected: `Scope: ${getScopeHash(this.domain).toString()}`,
|
|
1790
|
+
received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
|
|
1791
|
+
message: "The proof comes from a different domain than the one expected",
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (scope && getScopeHash(scope) !== BigInt(proofData.publicInputs[2])) {
|
|
1795
|
+
console.warn("The proof uses a different scope than the one expected")
|
|
1796
|
+
isCorrect = false
|
|
1797
|
+
queryResultErrors[key as keyof QueryResultErrors].scope = {
|
|
1798
|
+
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
1799
|
+
received: `Scope: ${BigInt(proofData.publicInputs[2]).toString()}`,
|
|
1800
|
+
message: "The proof uses a different scope than the one expected",
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return { isCorrect, queryResultErrors }
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
private async checkCertificateRegistryRoot(
|
|
1807
|
+
root: string,
|
|
1808
|
+
queryResultErrors: any,
|
|
1809
|
+
outer?: boolean,
|
|
1810
|
+
) {
|
|
1811
|
+
let isCorrect = true
|
|
1812
|
+
try {
|
|
1813
|
+
// Maintained certificate registry settled onchain
|
|
1814
|
+
// Here we use Ethereum Sepolia
|
|
1815
|
+
const registryClient = new RegistryClient({ chainId: 11155111 })
|
|
1816
|
+
await registryClient.getCertificates(`0x${root}`)
|
|
1817
|
+
} catch (error) {
|
|
1818
|
+
console.warn(error)
|
|
1819
|
+
// Check the legacy static roots that were used before the registry was deployed onchain
|
|
1820
|
+
const VALID_CERTIFICATE_REGISTRY_ROOT = [
|
|
1821
|
+
BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
|
|
1822
|
+
BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
|
|
1823
|
+
BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
|
|
1824
|
+
]
|
|
1825
|
+
if (!VALID_CERTIFICATE_REGISTRY_ROOT.includes(BigInt(root))) {
|
|
1826
|
+
console.warn("The ID was signed by an unrecognized root certificate")
|
|
1827
|
+
isCorrect = false
|
|
1828
|
+
queryResultErrors[outer ? "outer" : "sig_check_dsc"].certificate = {
|
|
1829
|
+
expected: `A valid root from ZKPassport Registry`,
|
|
1830
|
+
received: `Got invalid certificate registry root: ${root}`,
|
|
1831
|
+
message: "The ID was signed by an unrecognized root certificate",
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
return { isCorrect, queryResultErrors }
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1759
1838
|
private async checkPublicInputs(
|
|
1760
1839
|
proofs: Array<ProofResult>,
|
|
1761
1840
|
queryResult: QueryResult,
|
|
1762
1841
|
validity?: number,
|
|
1842
|
+
scope?: string,
|
|
1763
1843
|
) {
|
|
1764
1844
|
let commitmentIn: bigint | undefined
|
|
1765
1845
|
let commitmentOut: bigint | undefined
|
|
1766
1846
|
let isCorrect = true
|
|
1767
1847
|
let uniqueIdentifier: string | undefined
|
|
1768
|
-
const VALID_CERTIFICATE_REGISTRY_ROOT = [
|
|
1769
|
-
BigInt("20192042006788880778219739574377003123593792072535937278552252195461520776494"),
|
|
1770
|
-
BigInt("21301853597069384763054217328384418971999152625381818922211526730996340553696"),
|
|
1771
|
-
BigInt("10839898448097753834842514286432152806152415606387598803678317315409344029817"),
|
|
1772
|
-
]
|
|
1773
1848
|
const currentTime = new Date()
|
|
1774
1849
|
const today = new Date(
|
|
1775
1850
|
currentTime.getFullYear(),
|
|
@@ -1827,14 +1902,18 @@ export class ZKPassport {
|
|
|
1827
1902
|
if (proof.name?.startsWith("outer")) {
|
|
1828
1903
|
const isForEVM = proof.name?.startsWith("outer_evm")
|
|
1829
1904
|
const certificateRegistryRoot = getCertificateRegistryRootFromOuterProof(proofData)
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1905
|
+
const {
|
|
1906
|
+
isCorrect: isCorrectCertificateRegistryRoot,
|
|
1907
|
+
queryResultErrors: queryResultErrorsCertificateRegistryRoot,
|
|
1908
|
+
} = await this.checkCertificateRegistryRoot(
|
|
1909
|
+
certificateRegistryRoot.toString(16),
|
|
1910
|
+
queryResultErrors,
|
|
1911
|
+
true,
|
|
1912
|
+
)
|
|
1913
|
+
isCorrect = isCorrect && isCorrectCertificateRegistryRoot
|
|
1914
|
+
queryResultErrors = {
|
|
1915
|
+
...queryResultErrors,
|
|
1916
|
+
...queryResultErrorsCertificateRegistryRoot,
|
|
1838
1917
|
}
|
|
1839
1918
|
const currentDate = getCurrentDateFromOuterProof(proofData)
|
|
1840
1919
|
const todayToCurrentDate = today.getTime() - currentDate.getTime()
|
|
@@ -1866,6 +1945,24 @@ export class ZKPassport {
|
|
|
1866
1945
|
message: "The proof does not verify all the requested conditions and information",
|
|
1867
1946
|
}
|
|
1868
1947
|
}
|
|
1948
|
+
if (this.domain && getScopeHash(this.domain) !== getScopeFromOuterProof(proofData)) {
|
|
1949
|
+
console.warn("The proof comes from a different domain than the one expected")
|
|
1950
|
+
isCorrect = false
|
|
1951
|
+
queryResultErrors.outer.scope = {
|
|
1952
|
+
expected: `Scope: ${getScopeHash(this.domain).toString()}`,
|
|
1953
|
+
received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
|
|
1954
|
+
message: "The proof comes from a different domain than the one expected",
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
if (scope && getScopeHash(scope) !== getSubscopeFromOuterProof(proofData)) {
|
|
1958
|
+
console.warn("The proof uses a different scope than the one expected")
|
|
1959
|
+
isCorrect = false
|
|
1960
|
+
queryResultErrors.outer.scope = {
|
|
1961
|
+
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
1962
|
+
received: `Scope: ${getSubscopeFromOuterProof(proofData).toString()}`,
|
|
1963
|
+
message: "The proof uses a different scope than the one expected",
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1869
1966
|
if (!!committedInputs?.compare_age) {
|
|
1870
1967
|
const ageCommittedInputs = committedInputs?.compare_age as AgeCommittedInputs
|
|
1871
1968
|
const ageParameterCommitment = isForEVM
|
|
@@ -2113,14 +2210,18 @@ export class ZKPassport {
|
|
|
2113
2210
|
} else if (proof.name?.startsWith("sig_check_dsc")) {
|
|
2114
2211
|
commitmentOut = getCommitmentFromDSCProof(proofData)
|
|
2115
2212
|
const merkleRoot = getMerkleRootFromDSCProof(proofData)
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2213
|
+
const {
|
|
2214
|
+
isCorrect: isCorrectCertificateRegistryRoot,
|
|
2215
|
+
queryResultErrors: queryResultErrorsCertificateRegistryRoot,
|
|
2216
|
+
} = await this.checkCertificateRegistryRoot(
|
|
2217
|
+
merkleRoot.toString(16),
|
|
2218
|
+
queryResultErrors,
|
|
2219
|
+
false,
|
|
2220
|
+
)
|
|
2221
|
+
isCorrect = isCorrect && isCorrectCertificateRegistryRoot
|
|
2222
|
+
queryResultErrors = {
|
|
2223
|
+
...queryResultErrors,
|
|
2224
|
+
...queryResultErrorsCertificateRegistryRoot,
|
|
2124
2225
|
}
|
|
2125
2226
|
} else if (proof.name?.startsWith("sig_check_id_data")) {
|
|
2126
2227
|
commitmentIn = getCommitmentInFromIDDataProof(proofData)
|
|
@@ -2194,12 +2295,20 @@ export class ZKPassport {
|
|
|
2194
2295
|
message: "The disclosed data does not match the data committed by the proof",
|
|
2195
2296
|
}
|
|
2196
2297
|
}
|
|
2298
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2299
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "disclose", scope)
|
|
2300
|
+
isCorrect = isCorrect && isCorrectScope
|
|
2301
|
+
queryResultErrors = {
|
|
2302
|
+
...queryResultErrors,
|
|
2303
|
+
...queryResultErrorsScope,
|
|
2304
|
+
}
|
|
2197
2305
|
const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
|
|
2198
2306
|
this.checkDiscloseBytesPublicInputs(proof, queryResult)
|
|
2199
|
-
isCorrect = isCorrect && isCorrectDisclose
|
|
2307
|
+
isCorrect = isCorrect && isCorrectDisclose && isCorrectScope
|
|
2200
2308
|
queryResultErrors = {
|
|
2201
2309
|
...queryResultErrors,
|
|
2202
2310
|
...queryResultErrorsDisclose,
|
|
2311
|
+
...queryResultErrorsScope,
|
|
2203
2312
|
}
|
|
2204
2313
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2205
2314
|
} else if (proof.name === "compare_age") {
|
|
@@ -2235,12 +2344,15 @@ export class ZKPassport {
|
|
|
2235
2344
|
"The conditions for the age check do not match the conditions checked by the proof",
|
|
2236
2345
|
}
|
|
2237
2346
|
}
|
|
2347
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2348
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "age", scope)
|
|
2238
2349
|
const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
|
|
2239
2350
|
this.checkAgePublicInputs(proof, queryResult)
|
|
2240
|
-
isCorrect = isCorrect && isCorrectAge
|
|
2351
|
+
isCorrect = isCorrect && isCorrectAge && isCorrectScope
|
|
2241
2352
|
queryResultErrors = {
|
|
2242
2353
|
...queryResultErrors,
|
|
2243
2354
|
...queryResultErrorsAge,
|
|
2355
|
+
...queryResultErrorsScope,
|
|
2244
2356
|
}
|
|
2245
2357
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2246
2358
|
} else if (proof.name === "compare_birthdate") {
|
|
@@ -2277,12 +2389,15 @@ export class ZKPassport {
|
|
|
2277
2389
|
"The conditions for the birthdate check do not match the conditions checked by the proof",
|
|
2278
2390
|
}
|
|
2279
2391
|
}
|
|
2392
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2393
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "birthdate", scope)
|
|
2280
2394
|
const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
|
|
2281
2395
|
this.checkBirthdatePublicInputs(proof, queryResult)
|
|
2282
|
-
isCorrect = isCorrect && isCorrectBirthdate
|
|
2396
|
+
isCorrect = isCorrect && isCorrectBirthdate && isCorrectScope
|
|
2283
2397
|
queryResultErrors = {
|
|
2284
2398
|
...queryResultErrors,
|
|
2285
2399
|
...queryResultErrorsBirthdate,
|
|
2400
|
+
...queryResultErrorsScope,
|
|
2286
2401
|
}
|
|
2287
2402
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2288
2403
|
} else if (proof.name === "compare_expiry") {
|
|
@@ -2318,12 +2433,15 @@ export class ZKPassport {
|
|
|
2318
2433
|
"The conditions for the expiry date check do not match the conditions checked by the proof",
|
|
2319
2434
|
}
|
|
2320
2435
|
}
|
|
2436
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2437
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "expiry_date", scope)
|
|
2321
2438
|
const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
|
|
2322
2439
|
this.checkExpiryDatePublicInputs(proof, queryResult)
|
|
2323
|
-
isCorrect = isCorrect && isCorrectExpiryDate
|
|
2440
|
+
isCorrect = isCorrect && isCorrectExpiryDate && isCorrectScope
|
|
2324
2441
|
queryResultErrors = {
|
|
2325
2442
|
...queryResultErrors,
|
|
2326
2443
|
...queryResultErrorsExpiryDate,
|
|
2444
|
+
...queryResultErrorsScope,
|
|
2327
2445
|
}
|
|
2328
2446
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2329
2447
|
} else if (proof.name === "exclusion_check_nationality") {
|
|
@@ -2361,15 +2479,17 @@ export class ZKPassport {
|
|
|
2361
2479
|
"The committed country list for the exclusion check does not match the one from the proof",
|
|
2362
2480
|
}
|
|
2363
2481
|
}
|
|
2364
|
-
|
|
2482
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2483
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2365
2484
|
const {
|
|
2366
2485
|
isCorrect: isCorrectNationalityExclusion,
|
|
2367
2486
|
queryResultErrors: queryResultErrorsNationalityExclusion,
|
|
2368
2487
|
} = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
|
|
2369
|
-
isCorrect = isCorrect && isCorrectNationalityExclusion
|
|
2488
|
+
isCorrect = isCorrect && isCorrectNationalityExclusion && isCorrectScope
|
|
2370
2489
|
queryResultErrors = {
|
|
2371
2490
|
...queryResultErrors,
|
|
2372
2491
|
...queryResultErrorsNationalityExclusion,
|
|
2492
|
+
...queryResultErrorsScope,
|
|
2373
2493
|
}
|
|
2374
2494
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2375
2495
|
} else if (proof.name === "exclusion_check_issuing_country") {
|
|
@@ -2407,14 +2527,17 @@ export class ZKPassport {
|
|
|
2407
2527
|
"The committed country list for the issuing country exclusion check does not match the one from the proof",
|
|
2408
2528
|
}
|
|
2409
2529
|
}
|
|
2530
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2531
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2410
2532
|
const {
|
|
2411
2533
|
isCorrect: isCorrectIssuingCountryExclusion,
|
|
2412
2534
|
queryResultErrors: queryResultErrorsIssuingCountryExclusion,
|
|
2413
2535
|
} = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
|
|
2414
|
-
isCorrect = isCorrect && isCorrectIssuingCountryExclusion
|
|
2536
|
+
isCorrect = isCorrect && isCorrectIssuingCountryExclusion && isCorrectScope
|
|
2415
2537
|
queryResultErrors = {
|
|
2416
2538
|
...queryResultErrors,
|
|
2417
2539
|
...queryResultErrorsIssuingCountryExclusion,
|
|
2540
|
+
...queryResultErrorsScope,
|
|
2418
2541
|
}
|
|
2419
2542
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2420
2543
|
} else if (proof.name === "inclusion_check_nationality") {
|
|
@@ -2452,14 +2575,17 @@ export class ZKPassport {
|
|
|
2452
2575
|
"The committed country list for the nationality inclusion check does not match the one from the proof",
|
|
2453
2576
|
}
|
|
2454
2577
|
}
|
|
2578
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2579
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2455
2580
|
const {
|
|
2456
2581
|
isCorrect: isCorrectNationalityInclusion,
|
|
2457
2582
|
queryResultErrors: queryResultErrorsNationalityInclusion,
|
|
2458
2583
|
} = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
|
|
2459
|
-
isCorrect = isCorrect && isCorrectNationalityInclusion
|
|
2584
|
+
isCorrect = isCorrect && isCorrectNationalityInclusion && isCorrectScope
|
|
2460
2585
|
queryResultErrors = {
|
|
2461
2586
|
...queryResultErrors,
|
|
2462
2587
|
...queryResultErrorsNationalityInclusion,
|
|
2588
|
+
...queryResultErrorsScope,
|
|
2463
2589
|
}
|
|
2464
2590
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2465
2591
|
} else if (proof.name === "inclusion_check_issuing_country") {
|
|
@@ -2497,14 +2623,17 @@ export class ZKPassport {
|
|
|
2497
2623
|
"The committed country list for the issuing country inclusion check does not match the one from the proof",
|
|
2498
2624
|
}
|
|
2499
2625
|
}
|
|
2626
|
+
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2627
|
+
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2500
2628
|
const {
|
|
2501
2629
|
isCorrect: isCorrectIssuingCountryInclusion,
|
|
2502
2630
|
queryResultErrors: queryResultErrorsIssuingCountryInclusion,
|
|
2503
2631
|
} = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
|
|
2504
|
-
isCorrect = isCorrect && isCorrectIssuingCountryInclusion
|
|
2632
|
+
isCorrect = isCorrect && isCorrectIssuingCountryInclusion && isCorrectScope
|
|
2505
2633
|
queryResultErrors = {
|
|
2506
2634
|
...queryResultErrors,
|
|
2507
2635
|
...queryResultErrorsIssuingCountryInclusion,
|
|
2636
|
+
...queryResultErrorsScope,
|
|
2508
2637
|
}
|
|
2509
2638
|
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2510
2639
|
}
|
|
@@ -2524,10 +2653,14 @@ export class ZKPassport {
|
|
|
2524
2653
|
proofs,
|
|
2525
2654
|
queryResult,
|
|
2526
2655
|
validity,
|
|
2656
|
+
scope,
|
|
2657
|
+
devMode = false,
|
|
2527
2658
|
}: {
|
|
2528
2659
|
proofs: Array<ProofResult>
|
|
2529
2660
|
queryResult: QueryResult
|
|
2530
2661
|
validity?: number
|
|
2662
|
+
scope?: string
|
|
2663
|
+
devMode?: boolean
|
|
2531
2664
|
}): Promise<{
|
|
2532
2665
|
uniqueIdentifier: string | undefined
|
|
2533
2666
|
verified: boolean
|
|
@@ -2555,10 +2688,19 @@ export class ZKPassport {
|
|
|
2555
2688
|
isCorrect,
|
|
2556
2689
|
uniqueIdentifier: uniqueIdentifierFromPublicInputs,
|
|
2557
2690
|
queryResultErrors: queryResultErrorsFromPublicInputs,
|
|
2558
|
-
} = await this.checkPublicInputs(proofs, formattedResult, validity)
|
|
2691
|
+
} = await this.checkPublicInputs(proofs, formattedResult, validity, scope)
|
|
2559
2692
|
uniqueIdentifier = uniqueIdentifierFromPublicInputs
|
|
2560
2693
|
verified = isCorrect
|
|
2561
2694
|
queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
|
|
2695
|
+
if (uniqueIdentifier && BigInt(uniqueIdentifier) === BigInt(0) && !devMode) {
|
|
2696
|
+
// If the unique identifier is 0 and it is not in dev mode,
|
|
2697
|
+
// the proofs are considered invalid as these are mock proofs only meant
|
|
2698
|
+
// for testing purposes
|
|
2699
|
+
verified = false
|
|
2700
|
+
console.warn(
|
|
2701
|
+
"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.",
|
|
2702
|
+
)
|
|
2703
|
+
}
|
|
2562
2704
|
// Only proceed with the proof verification if the public inputs are correct
|
|
2563
2705
|
if (verified) {
|
|
2564
2706
|
for (const proof of proofs) {
|
|
@@ -2571,24 +2713,24 @@ export class ZKPassport {
|
|
|
2571
2713
|
try {
|
|
2572
2714
|
const { createPublicClient, http } = await import("viem")
|
|
2573
2715
|
const { sepolia } = await import("viem/chains")
|
|
2574
|
-
const
|
|
2716
|
+
const { address, abi, functionName } =
|
|
2717
|
+
this.getSolidityVerifierDetails("ethereum_sepolia")
|
|
2575
2718
|
const client = createPublicClient({
|
|
2576
2719
|
chain: sepolia,
|
|
2577
2720
|
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
|
|
2578
2721
|
})
|
|
2579
|
-
const params = this.getSolidityVerifierParameters(
|
|
2722
|
+
const params = this.getSolidityVerifierParameters({
|
|
2723
|
+
proof,
|
|
2724
|
+
validityPeriodInDays: validity,
|
|
2725
|
+
domain: this.domain,
|
|
2726
|
+
scope,
|
|
2727
|
+
devMode,
|
|
2728
|
+
})
|
|
2580
2729
|
const result = await client.readContract({
|
|
2581
|
-
address
|
|
2582
|
-
abi
|
|
2583
|
-
functionName
|
|
2584
|
-
args: [
|
|
2585
|
-
params.vkeyHash,
|
|
2586
|
-
params.proof,
|
|
2587
|
-
params.publicInputs,
|
|
2588
|
-
params.committedInputs,
|
|
2589
|
-
params.committedInputCounts,
|
|
2590
|
-
params.validityPeriodInDays,
|
|
2591
|
-
],
|
|
2730
|
+
address,
|
|
2731
|
+
abi,
|
|
2732
|
+
functionName,
|
|
2733
|
+
args: [params],
|
|
2592
2734
|
})
|
|
2593
2735
|
const isVerified = Array.isArray(result) ? Boolean(result[0]) : false
|
|
2594
2736
|
verified = isVerified
|
|
@@ -2624,7 +2766,8 @@ export class ZKPassport {
|
|
|
2624
2766
|
}
|
|
2625
2767
|
|
|
2626
2768
|
public getSolidityVerifierDetails(network: EVMChain): {
|
|
2627
|
-
address: string
|
|
2769
|
+
address: `0x${string}`
|
|
2770
|
+
functionName: string
|
|
2628
2771
|
abi: {
|
|
2629
2772
|
type: "function" | "event" | "constructor"
|
|
2630
2773
|
name: string
|
|
@@ -2632,21 +2775,37 @@ export class ZKPassport {
|
|
|
2632
2775
|
outputs: { name: string; type: string; internalType: string }[]
|
|
2633
2776
|
}[]
|
|
2634
2777
|
} {
|
|
2778
|
+
const baseConfig = {
|
|
2779
|
+
functionName: "verifyProof",
|
|
2780
|
+
abi: ZKPassportVerifierAbi.abi as any,
|
|
2781
|
+
}
|
|
2635
2782
|
if (network === "ethereum_sepolia") {
|
|
2636
2783
|
return {
|
|
2637
|
-
|
|
2638
|
-
|
|
2784
|
+
...baseConfig,
|
|
2785
|
+
address: "0x8c6982D77f7a8f60aE3133cA9b2FAA6f3e78c394",
|
|
2639
2786
|
}
|
|
2640
2787
|
} else if (network === "local_anvil") {
|
|
2641
2788
|
return {
|
|
2789
|
+
...baseConfig,
|
|
2642
2790
|
address: "0x0",
|
|
2643
|
-
abi: ZKPassportVerifierAbi.abi as any,
|
|
2644
2791
|
}
|
|
2645
2792
|
}
|
|
2646
2793
|
throw new Error(`Unsupported network: ${network}`)
|
|
2647
2794
|
}
|
|
2648
2795
|
|
|
2649
|
-
public getSolidityVerifierParameters(
|
|
2796
|
+
public getSolidityVerifierParameters({
|
|
2797
|
+
proof,
|
|
2798
|
+
validityPeriodInDays = 7,
|
|
2799
|
+
domain,
|
|
2800
|
+
scope,
|
|
2801
|
+
devMode = false,
|
|
2802
|
+
}: {
|
|
2803
|
+
proof: ProofResult
|
|
2804
|
+
validityPeriodInDays?: number
|
|
2805
|
+
domain?: string
|
|
2806
|
+
scope?: string
|
|
2807
|
+
devMode?: boolean
|
|
2808
|
+
}) {
|
|
2650
2809
|
if (!proof.name?.startsWith("outer_evm")) {
|
|
2651
2810
|
throw new Error(
|
|
2652
2811
|
"This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.",
|
|
@@ -2779,6 +2938,9 @@ export class ZKPassport {
|
|
|
2779
2938
|
committedInputs: `0x${compressedCommittedInputs}`,
|
|
2780
2939
|
committedInputCounts: committedInputCountsArray,
|
|
2781
2940
|
validityPeriodInDays,
|
|
2941
|
+
scope: domain ?? this.domain,
|
|
2942
|
+
subscope: scope ?? "",
|
|
2943
|
+
devMode,
|
|
2782
2944
|
}
|
|
2783
2945
|
return params
|
|
2784
2946
|
}
|