@zkpassport/sdk 0.5.6 → 0.5.7-beta.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/index.cjs +3169 -0
- package/dist/cjs/{index.d.ts → index.d.cts} +14 -10
- package/dist/cjs/logger.cjs +47 -0
- package/dist/cjs/{logger.d.ts → logger.d.cts} +4 -2
- package/dist/esm/index.js +2 -2
- package/package.json +33 -36
- package/dist/cjs/assets/abi/ZKPassportVerifier.json +0 -744
- package/dist/cjs/encryption.d.ts +0 -7
- package/dist/cjs/encryption.js +0 -79
- package/dist/cjs/index.js +0 -2345
- package/dist/cjs/json-rpc.d.ts +0 -6
- package/dist/cjs/json-rpc.js +0 -47
- package/dist/cjs/logger.js +0 -15
- package/dist/cjs/mobile.d.ts +0 -39
- package/dist/cjs/mobile.js +0 -130
- package/dist/cjs/websocket.d.ts +0 -2
- package/dist/cjs/websocket.js +0 -18
- package/dist/esm/encryption.d.ts +0 -7
- package/dist/esm/encryption.js +0 -40
- package/dist/esm/json-rpc.d.ts +0 -6
- package/dist/esm/json-rpc.js +0 -41
- package/dist/esm/mobile.d.ts +0 -39
- package/dist/esm/mobile.js +0 -126
- package/dist/esm/websocket.d.ts +0 -2
- package/dist/esm/websocket.js +0 -15
- package/src/assets/abi/ZKPassportVerifier.json +0 -744
- package/src/index.ts +0 -3174
- package/src/logger.ts +0 -13
- package/tsconfig.json +0 -24
package/src/index.ts
DELETED
|
@@ -1,3174 +0,0 @@
|
|
|
1
|
-
import { Alpha3Code, getAlpha3Code, registerLocale } from "i18n-iso-countries"
|
|
2
|
-
import {
|
|
3
|
-
type DisclosableIDCredential,
|
|
4
|
-
type IDCredential,
|
|
5
|
-
type IDCredentialConfig,
|
|
6
|
-
type IDCredentialValue,
|
|
7
|
-
type NumericalIDCredential,
|
|
8
|
-
type ProofResult,
|
|
9
|
-
type QueryResult,
|
|
10
|
-
type CountryName,
|
|
11
|
-
type JsonRpcRequest,
|
|
12
|
-
getProofData,
|
|
13
|
-
getCommitmentFromDSCProof,
|
|
14
|
-
getCommitmentInFromIDDataProof,
|
|
15
|
-
getCommitmentOutFromIDDataProof,
|
|
16
|
-
getNullifierFromDisclosureProof,
|
|
17
|
-
getCommitmentInFromIntegrityProof,
|
|
18
|
-
getCommitmentOutFromIntegrityProof,
|
|
19
|
-
getCommitmentInFromDisclosureProof,
|
|
20
|
-
getMerkleRootFromDSCProof,
|
|
21
|
-
getCurrentDateFromIntegrityProof,
|
|
22
|
-
DisclosedData,
|
|
23
|
-
formatName,
|
|
24
|
-
getHostedPackagedCircuitByName,
|
|
25
|
-
Query,
|
|
26
|
-
getNumberOfPublicInputs,
|
|
27
|
-
getParameterCommitmentFromDisclosureProof,
|
|
28
|
-
getCountryParameterCommitment,
|
|
29
|
-
getDiscloseParameterCommitment,
|
|
30
|
-
getDateParameterCommitment,
|
|
31
|
-
getFormattedDate,
|
|
32
|
-
getCertificateRegistryRootFromOuterProof,
|
|
33
|
-
getParamCommitmentsFromOuterProof,
|
|
34
|
-
AgeCommittedInputs,
|
|
35
|
-
DiscloseCommittedInputs,
|
|
36
|
-
getCurrentDateFromCommittedInputs,
|
|
37
|
-
getMinAgeFromCommittedInputs,
|
|
38
|
-
getMaxAgeFromCommittedInputs,
|
|
39
|
-
getAgeParameterCommitment,
|
|
40
|
-
DateCommittedInputs,
|
|
41
|
-
CountryCommittedInputs,
|
|
42
|
-
getMinDateFromCommittedInputs,
|
|
43
|
-
getMaxDateFromCommittedInputs,
|
|
44
|
-
getCurrentDateFromOuterProof,
|
|
45
|
-
getNullifierFromOuterProof,
|
|
46
|
-
DisclosureCircuitName,
|
|
47
|
-
getAgeEVMParameterCommitment,
|
|
48
|
-
getDateEVMParameterCommitment,
|
|
49
|
-
getDiscloseEVMParameterCommitment,
|
|
50
|
-
getCountryEVMParameterCommitment,
|
|
51
|
-
rightPadArrayWithZeros,
|
|
52
|
-
getCommittedInputCount,
|
|
53
|
-
ProofMode,
|
|
54
|
-
ProofType,
|
|
55
|
-
getScopeHash,
|
|
56
|
-
ProofData,
|
|
57
|
-
getScopeFromOuterProof,
|
|
58
|
-
getSubscopeFromOuterProof,
|
|
59
|
-
getServiceScopeHash,
|
|
60
|
-
BoundData,
|
|
61
|
-
BindCommittedInputs,
|
|
62
|
-
getBindEVMParameterCommitment,
|
|
63
|
-
getBindParameterCommitment,
|
|
64
|
-
formatBoundData,
|
|
65
|
-
Service,
|
|
66
|
-
CircuitManifest,
|
|
67
|
-
getCircuitRegistryRootFromOuterProof,
|
|
68
|
-
} from "@zkpassport/utils"
|
|
69
|
-
import { bytesToHex } from "@noble/ciphers/utils"
|
|
70
|
-
import { noLogger as logger } from "./logger"
|
|
71
|
-
import i18en from "i18n-iso-countries/langs/en.json"
|
|
72
|
-
import { Buffer } from "buffer/"
|
|
73
|
-
import { sha256 } from "@noble/hashes/sha2"
|
|
74
|
-
import { hexToBytes } from "@noble/hashes/utils"
|
|
75
|
-
import ZKPassportVerifierAbi from "./assets/abi/ZKPassportVerifier.json"
|
|
76
|
-
import { RegistryClient } from "@zkpassport/registry"
|
|
77
|
-
import { Bridge, BridgeInterface } from "@obsidion/bridge"
|
|
78
|
-
|
|
79
|
-
const VERSION = "0.5.5"
|
|
80
|
-
|
|
81
|
-
const DEFAULT_DATE_VALUE = new Date(1111, 10, 11)
|
|
82
|
-
|
|
83
|
-
// If Buffer is not defined, then we use the Buffer from the buffer package
|
|
84
|
-
if (typeof globalThis.Buffer === "undefined") {
|
|
85
|
-
globalThis.Buffer = Buffer as any
|
|
86
|
-
if (typeof window !== "undefined") {
|
|
87
|
-
window.Buffer = Buffer as any
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export type QueryResultError<T> = {
|
|
92
|
-
expected?: T
|
|
93
|
-
received?: T
|
|
94
|
-
message: string
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export type QueryResultErrors = {
|
|
98
|
-
[key in
|
|
99
|
-
| IDCredential
|
|
100
|
-
| "sig_check_dsc"
|
|
101
|
-
| "sig_check_id_data"
|
|
102
|
-
| "data_check_integrity"
|
|
103
|
-
| "outer"
|
|
104
|
-
| "disclose"
|
|
105
|
-
| "bind"]: {
|
|
106
|
-
disclose?: QueryResultError<string | number | Date>
|
|
107
|
-
gte?: QueryResultError<number | Date>
|
|
108
|
-
lte?: QueryResultError<number | Date>
|
|
109
|
-
lt?: QueryResultError<number | Date>
|
|
110
|
-
range?: QueryResultError<[number | Date, number | Date]>
|
|
111
|
-
in?: QueryResultError<string[]>
|
|
112
|
-
out?: QueryResultError<string[]>
|
|
113
|
-
eq?: QueryResultError<string | number | Date>
|
|
114
|
-
commitment?: QueryResultError<string>
|
|
115
|
-
date?: QueryResultError<string>
|
|
116
|
-
certificate?: QueryResultError<string>
|
|
117
|
-
scope?: QueryResultError<string>
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export type SolidityVerifierParameters = {
|
|
122
|
-
vkeyHash: string
|
|
123
|
-
proof: string
|
|
124
|
-
publicInputs: string[]
|
|
125
|
-
committedInputs: string
|
|
126
|
-
committedInputCounts: number[]
|
|
127
|
-
validityPeriodInDays: number
|
|
128
|
-
domain: string
|
|
129
|
-
scope: string
|
|
130
|
-
devMode: boolean
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export type EVMChain = "ethereum_sepolia" | "local_anvil"
|
|
134
|
-
|
|
135
|
-
function getChainIdFromEVMChain(chain: EVMChain): number {
|
|
136
|
-
if (chain === "ethereum_sepolia") {
|
|
137
|
-
return 11155111
|
|
138
|
-
} else if (chain === "local_anvil") {
|
|
139
|
-
return 31337
|
|
140
|
-
}
|
|
141
|
-
throw new Error(`Unsupported chain: ${chain}`)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function getEVMChainFromChainId(chainId: number): EVMChain {
|
|
145
|
-
if (chainId === 11155111) {
|
|
146
|
-
return "ethereum_sepolia"
|
|
147
|
-
} else if (chainId === 31337) {
|
|
148
|
-
return "local_anvil"
|
|
149
|
-
}
|
|
150
|
-
throw new Error(`Unsupported chain ID: ${chainId}`)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
registerLocale(i18en)
|
|
154
|
-
|
|
155
|
-
function hasRequestedAccessToField(credentialsRequest: Query, field: IDCredential): boolean {
|
|
156
|
-
const fieldValue = credentialsRequest[field as keyof Query]
|
|
157
|
-
const isDefined = fieldValue !== undefined && fieldValue !== null
|
|
158
|
-
if (!isDefined) {
|
|
159
|
-
return false
|
|
160
|
-
}
|
|
161
|
-
for (const key in fieldValue) {
|
|
162
|
-
if (
|
|
163
|
-
fieldValue[key as keyof typeof fieldValue] !== undefined &&
|
|
164
|
-
fieldValue[key as keyof typeof fieldValue] !== null
|
|
165
|
-
) {
|
|
166
|
-
return true
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return false
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function normalizeCountry(country: CountryName | Alpha3Code) {
|
|
173
|
-
if (country === "Zero Knowledge Republic") {
|
|
174
|
-
return "ZKR"
|
|
175
|
-
}
|
|
176
|
-
let normalizedCountry: Alpha3Code | "ZKR" | undefined
|
|
177
|
-
const alpha3 = getAlpha3Code(country as CountryName, "en") as Alpha3Code | "ZKR" | undefined
|
|
178
|
-
normalizedCountry = alpha3 || (country as Alpha3Code) || "ZKR"
|
|
179
|
-
return normalizedCountry
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function numericalCompare(
|
|
183
|
-
fnName: "gte" | "gt" | "lte" | "lt",
|
|
184
|
-
key: NumericalIDCredential,
|
|
185
|
-
value: number | Date,
|
|
186
|
-
requestId: string,
|
|
187
|
-
requestIdToConfig: Record<string, Record<string, IDCredentialConfig>>,
|
|
188
|
-
) {
|
|
189
|
-
requestIdToConfig[requestId][key] = {
|
|
190
|
-
...requestIdToConfig[requestId][key],
|
|
191
|
-
[fnName]: value,
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function rangeCompare(
|
|
196
|
-
key: NumericalIDCredential,
|
|
197
|
-
value: [number | Date, number | Date],
|
|
198
|
-
requestId: string,
|
|
199
|
-
requestIdToConfig: Record<string, Record<string, IDCredentialConfig>>,
|
|
200
|
-
) {
|
|
201
|
-
requestIdToConfig[requestId][key] = {
|
|
202
|
-
...requestIdToConfig[requestId][key],
|
|
203
|
-
range: value,
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function generalCompare(
|
|
208
|
-
fnName: "in" | "out" | "eq",
|
|
209
|
-
key: IDCredential,
|
|
210
|
-
value: any,
|
|
211
|
-
requestId: string,
|
|
212
|
-
requestIdToConfig: Record<string, Record<string, IDCredentialConfig>>,
|
|
213
|
-
) {
|
|
214
|
-
requestIdToConfig[requestId][key] = {
|
|
215
|
-
...requestIdToConfig[requestId][key],
|
|
216
|
-
[fnName]: value,
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export type * from "@zkpassport/utils"
|
|
221
|
-
export {
|
|
222
|
-
SANCTIONED_COUNTRIES,
|
|
223
|
-
EU_COUNTRIES,
|
|
224
|
-
EEA_COUNTRIES,
|
|
225
|
-
SCHENGEN_COUNTRIES,
|
|
226
|
-
ASEAN_COUNTRIES,
|
|
227
|
-
MERCOSUR_COUNTRIES,
|
|
228
|
-
} from "@zkpassport/utils"
|
|
229
|
-
|
|
230
|
-
export type QueryBuilderResult = {
|
|
231
|
-
/**
|
|
232
|
-
* The URL of the request.
|
|
233
|
-
*
|
|
234
|
-
* You can either encode the URL in a QR code or let the user click the link
|
|
235
|
-
* to this URL on your website if they're visiting your website on their phone.
|
|
236
|
-
*/
|
|
237
|
-
url: string
|
|
238
|
-
/**
|
|
239
|
-
* The id of the request.
|
|
240
|
-
*/
|
|
241
|
-
requestId: string
|
|
242
|
-
/**
|
|
243
|
-
* Called when the user has scanned the QR code or clicked the link to the request.
|
|
244
|
-
*
|
|
245
|
-
* This means the user is currently viewing the request popup with your website information
|
|
246
|
-
* and the information requested from them.
|
|
247
|
-
*/
|
|
248
|
-
onRequestReceived: (callback: () => void) => void
|
|
249
|
-
/**
|
|
250
|
-
* Called when the user has accepted the request and
|
|
251
|
-
* started to generate the proof on their phone.
|
|
252
|
-
*/
|
|
253
|
-
onGeneratingProof: (callback: () => void) => void
|
|
254
|
-
/**
|
|
255
|
-
* Called when the SDK successfully connects to the bridge with the mobile app.
|
|
256
|
-
*/
|
|
257
|
-
onBridgeConnect: (callback: () => void) => void
|
|
258
|
-
/**
|
|
259
|
-
* Called when the user has generated a proof.
|
|
260
|
-
*
|
|
261
|
-
* There is a minimum of 4 proofs, but there can be more depending
|
|
262
|
-
* on the type of information requested from the user.
|
|
263
|
-
*/
|
|
264
|
-
onProofGenerated: (callback: (proof: ProofResult) => void) => void
|
|
265
|
-
/**
|
|
266
|
-
* Called when the user has sent the query result.
|
|
267
|
-
*
|
|
268
|
-
* The response contains the unique identifier associated to the user,
|
|
269
|
-
* your domain name and chosen scope, along with the query result and whether
|
|
270
|
-
* the proofs were successfully verified.
|
|
271
|
-
*/
|
|
272
|
-
onResult: (
|
|
273
|
-
callback: (response: {
|
|
274
|
-
uniqueIdentifier: string | undefined
|
|
275
|
-
verified: boolean
|
|
276
|
-
result: QueryResult
|
|
277
|
-
queryResultErrors?: QueryResultErrors
|
|
278
|
-
}) => void,
|
|
279
|
-
) => void
|
|
280
|
-
/**
|
|
281
|
-
* Called when the user has rejected the request.
|
|
282
|
-
*/
|
|
283
|
-
onReject: (callback: () => void) => void
|
|
284
|
-
/**
|
|
285
|
-
* Called when an error occurs, such as one of the requirements not being met
|
|
286
|
-
* or a proof failing to be generated.
|
|
287
|
-
*/
|
|
288
|
-
onError: (callback: (error: string) => void) => void
|
|
289
|
-
/**
|
|
290
|
-
* @returns true if the bridge with the mobile app is connected
|
|
291
|
-
*/
|
|
292
|
-
isBridgeConnected: () => boolean
|
|
293
|
-
/**
|
|
294
|
-
* Get if the user has scanned the QR code or the link to this request
|
|
295
|
-
* @returns true if the request has been received by the user on their phone
|
|
296
|
-
*/
|
|
297
|
-
requestReceived: () => boolean
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export type QueryBuilder = {
|
|
301
|
-
/**
|
|
302
|
-
* Requires this attribute to be equal to the provided value.
|
|
303
|
-
* @param key The attribute to compare.
|
|
304
|
-
* @param value The value of the attribute you require.
|
|
305
|
-
*/
|
|
306
|
-
eq: <T extends IDCredential>(key: T, value: IDCredentialValue<T>) => QueryBuilder
|
|
307
|
-
/**
|
|
308
|
-
* Requires this attribute to be greater than or equal to the provided value.
|
|
309
|
-
* @param key The attribute to compare.
|
|
310
|
-
* @param value The value of the attribute you require.
|
|
311
|
-
*/
|
|
312
|
-
gte: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => QueryBuilder
|
|
313
|
-
/**
|
|
314
|
-
* Requires this attribute to be less than or equal to the provided value.
|
|
315
|
-
* @param key The attribute to compare.
|
|
316
|
-
* @param value The value of the attribute you require.
|
|
317
|
-
*/
|
|
318
|
-
lte: <T extends "birthdate" | "expiry_date">(key: T, value: IDCredentialValue<T>) => QueryBuilder
|
|
319
|
-
/**
|
|
320
|
-
* Requires this attribute to be less than the provided value.
|
|
321
|
-
* @param key The attribute to compare.
|
|
322
|
-
* @param value The value of the attribute you require.
|
|
323
|
-
*/
|
|
324
|
-
lt: <T extends "age">(key: T, value: IDCredentialValue<T>) => QueryBuilder
|
|
325
|
-
/**
|
|
326
|
-
* Requires this attribute to be included in the provided range.
|
|
327
|
-
* @param key The attribute to compare.
|
|
328
|
-
* @param start The start of the range.
|
|
329
|
-
* @param end The end of the range.
|
|
330
|
-
*/
|
|
331
|
-
range: <T extends NumericalIDCredential>(
|
|
332
|
-
key: T,
|
|
333
|
-
start: IDCredentialValue<T>,
|
|
334
|
-
end: IDCredentialValue<T>,
|
|
335
|
-
) => QueryBuilder
|
|
336
|
-
/**
|
|
337
|
-
* Requires this attribute to be included in the provided list.
|
|
338
|
-
* @param key The attribute to compare.
|
|
339
|
-
* @param value The list of values to check inclusion against.
|
|
340
|
-
*/
|
|
341
|
-
in: <T extends "nationality" | "issuing_country">(
|
|
342
|
-
key: T,
|
|
343
|
-
value: IDCredentialValue<T>[],
|
|
344
|
-
) => QueryBuilder
|
|
345
|
-
/**
|
|
346
|
-
* Requires this attribute to be excluded from the provided list.
|
|
347
|
-
* @param key The attribute to compare.
|
|
348
|
-
* @param value The list of values to check exclusion against.
|
|
349
|
-
*/
|
|
350
|
-
out: <T extends "nationality" | "issuing_country">(
|
|
351
|
-
key: T,
|
|
352
|
-
value: IDCredentialValue<T>[],
|
|
353
|
-
) => QueryBuilder
|
|
354
|
-
/**
|
|
355
|
-
* Requires this attribute to be disclosed.
|
|
356
|
-
* @param key The attribute to disclose.
|
|
357
|
-
*/
|
|
358
|
-
disclose: (key: DisclosableIDCredential) => QueryBuilder
|
|
359
|
-
/**
|
|
360
|
-
* Binds a value to the request.
|
|
361
|
-
* @param key The key of the value to bind.
|
|
362
|
-
* @param value The value to bind the request to.
|
|
363
|
-
*/
|
|
364
|
-
bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder
|
|
365
|
-
/**
|
|
366
|
-
* Builds the request.
|
|
367
|
-
*
|
|
368
|
-
* This will return the URL of the request, which you can either encode in a QR code
|
|
369
|
-
* or provide as a link to the user if they're visiting your website on their phone.
|
|
370
|
-
* It also returns all the callbacks you can use to handle the user's response.
|
|
371
|
-
*/
|
|
372
|
-
done: () => QueryBuilderResult
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export class ZKPassport {
|
|
376
|
-
private domain: string
|
|
377
|
-
private topicToConfig: Record<string, Record<string, IDCredentialConfig>> = {}
|
|
378
|
-
private topicToLocalConfig: Record<
|
|
379
|
-
string,
|
|
380
|
-
{
|
|
381
|
-
validity: number
|
|
382
|
-
mode: ProofMode
|
|
383
|
-
devMode: boolean
|
|
384
|
-
}
|
|
385
|
-
> = {}
|
|
386
|
-
private topicToPublicKey: Record<string, string> = {}
|
|
387
|
-
private topicToBridge: Record<string, BridgeInterface> = {}
|
|
388
|
-
private topicToRequestReceived: Record<string, boolean> = {}
|
|
389
|
-
private topicToService: Record<string, Service> = {}
|
|
390
|
-
private topicToProofs: Record<string, Array<ProofResult>> = {}
|
|
391
|
-
private topicToExpectedProofCount: Record<string, number> = {}
|
|
392
|
-
private topicToFailedProofCount: Record<string, number> = {}
|
|
393
|
-
private topicToResults: Record<string, QueryResult> = {}
|
|
394
|
-
|
|
395
|
-
private onRequestReceivedCallbacks: Record<string, Array<() => void>> = {}
|
|
396
|
-
private onGeneratingProofCallbacks: Record<string, Array<(topic: string) => void>> = {}
|
|
397
|
-
private onBridgeConnectCallbacks: Record<string, Array<() => void>> = {}
|
|
398
|
-
private onProofGeneratedCallbacks: Record<string, Array<(proof: ProofResult) => void>> = {}
|
|
399
|
-
private onResultCallbacks: Record<
|
|
400
|
-
string,
|
|
401
|
-
Array<
|
|
402
|
-
(response: {
|
|
403
|
-
uniqueIdentifier: string | undefined
|
|
404
|
-
verified: boolean
|
|
405
|
-
result: QueryResult
|
|
406
|
-
queryResultErrors?: QueryResultErrors
|
|
407
|
-
}) => void
|
|
408
|
-
>
|
|
409
|
-
> = {}
|
|
410
|
-
private onRejectCallbacks: Record<string, Array<() => void>> = {}
|
|
411
|
-
private onErrorCallbacks: Record<string, Array<(topic: string) => void>> = {}
|
|
412
|
-
//private wasmVerifierInit: boolean = false
|
|
413
|
-
|
|
414
|
-
constructor(_domain?: string) {
|
|
415
|
-
if (!_domain && typeof window === "undefined") {
|
|
416
|
-
throw new Error("Domain argument is required in Node.js environment")
|
|
417
|
-
}
|
|
418
|
-
this.domain = _domain || window.location.hostname
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
private async handleResult(topic: string) {
|
|
422
|
-
const result = this.topicToResults[topic]
|
|
423
|
-
// Clear the results straight away to avoid concurrency issues
|
|
424
|
-
delete this.topicToResults[topic]
|
|
425
|
-
// Verify the proofs and extract the unique identifier (aka nullifier) and the verification result
|
|
426
|
-
const { uniqueIdentifier, verified, queryResultErrors } = await this.verify({
|
|
427
|
-
proofs: this.topicToProofs[topic],
|
|
428
|
-
queryResult: result,
|
|
429
|
-
validity: this.topicToLocalConfig[topic]?.validity,
|
|
430
|
-
scope: this.topicToService[topic]?.scope,
|
|
431
|
-
evmChain: this.topicToService[topic]?.chainId
|
|
432
|
-
? getEVMChainFromChainId(this.topicToService[topic]?.chainId)
|
|
433
|
-
: undefined,
|
|
434
|
-
devMode: this.topicToLocalConfig[topic]?.devMode,
|
|
435
|
-
})
|
|
436
|
-
delete this.topicToProofs[topic]
|
|
437
|
-
const hasFailedProofs = this.topicToFailedProofCount[topic] > 0
|
|
438
|
-
await Promise.all(
|
|
439
|
-
this.onResultCallbacks[topic].map((callback) =>
|
|
440
|
-
callback({
|
|
441
|
-
// If there are failed proofs, we don't return the unique identifier
|
|
442
|
-
// and we set the verified result to false
|
|
443
|
-
uniqueIdentifier: hasFailedProofs ? undefined : uniqueIdentifier,
|
|
444
|
-
verified: hasFailedProofs ? false : verified,
|
|
445
|
-
result,
|
|
446
|
-
queryResultErrors,
|
|
447
|
-
}),
|
|
448
|
-
),
|
|
449
|
-
)
|
|
450
|
-
// Clear the expected proof count and failed proof count
|
|
451
|
-
delete this.topicToExpectedProofCount[topic]
|
|
452
|
-
delete this.topicToFailedProofCount[topic]
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
private setExpectedProofCount(topic: string) {
|
|
456
|
-
// If the mode is not fast, we'll receive only 1 compressed proof
|
|
457
|
-
if (this.topicToLocalConfig[topic].mode !== "fast") {
|
|
458
|
-
this.topicToExpectedProofCount[topic] = 1
|
|
459
|
-
return
|
|
460
|
-
}
|
|
461
|
-
const fields = Object.keys(this.topicToConfig[topic] as Query).filter((key) =>
|
|
462
|
-
hasRequestedAccessToField(this.topicToConfig[topic] as Query, key as IDCredential),
|
|
463
|
-
)
|
|
464
|
-
const neededCircuits: string[] = []
|
|
465
|
-
// Determine which circuits are needed based on the requested fields
|
|
466
|
-
for (const field of fields) {
|
|
467
|
-
for (const key in this.topicToConfig[topic][field as IDCredential]) {
|
|
468
|
-
switch (key) {
|
|
469
|
-
case "eq":
|
|
470
|
-
case "disclose":
|
|
471
|
-
if (field !== "age" && !neededCircuits.includes("disclose_bytes")) {
|
|
472
|
-
neededCircuits.push("disclose_bytes")
|
|
473
|
-
} else if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
474
|
-
neededCircuits.push("compare_age")
|
|
475
|
-
}
|
|
476
|
-
break
|
|
477
|
-
case "gte":
|
|
478
|
-
case "gt":
|
|
479
|
-
case "lte":
|
|
480
|
-
case "lt":
|
|
481
|
-
case "range":
|
|
482
|
-
if (field === "age" && !neededCircuits.includes("compare_age")) {
|
|
483
|
-
neededCircuits.push("compare_age")
|
|
484
|
-
} else if (field === "expiry_date" && !neededCircuits.includes("compare_expiry")) {
|
|
485
|
-
neededCircuits.push("compare_expiry")
|
|
486
|
-
} else if (field === "birthdate" && !neededCircuits.includes("compare_birthdate")) {
|
|
487
|
-
neededCircuits.push("compare_birthdate")
|
|
488
|
-
}
|
|
489
|
-
break
|
|
490
|
-
case "in":
|
|
491
|
-
if (
|
|
492
|
-
field === "nationality" &&
|
|
493
|
-
!neededCircuits.includes("inclusion_check_nationality")
|
|
494
|
-
) {
|
|
495
|
-
neededCircuits.push("inclusion_check_nationality")
|
|
496
|
-
} else if (
|
|
497
|
-
field === "issuing_country" &&
|
|
498
|
-
!neededCircuits.includes("inclusion_check_issuing_country")
|
|
499
|
-
) {
|
|
500
|
-
neededCircuits.push("inclusion_check_issuing_country")
|
|
501
|
-
}
|
|
502
|
-
break
|
|
503
|
-
case "out":
|
|
504
|
-
if (
|
|
505
|
-
field === "nationality" &&
|
|
506
|
-
!neededCircuits.includes("exclusion_check_nationality")
|
|
507
|
-
) {
|
|
508
|
-
neededCircuits.push("exclusion_check_nationality")
|
|
509
|
-
} else if (
|
|
510
|
-
field === "issuing_country" &&
|
|
511
|
-
!neededCircuits.includes("exclusion_check_issuing_country")
|
|
512
|
-
) {
|
|
513
|
-
neededCircuits.push("exclusion_check_issuing_country")
|
|
514
|
-
}
|
|
515
|
-
break
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
if ((this.topicToConfig[topic] as Query).bind) {
|
|
520
|
-
neededCircuits.push("bind")
|
|
521
|
-
}
|
|
522
|
-
// From the circuits needed, determine the expected proof count
|
|
523
|
-
// There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
|
|
524
|
-
// Each separate needed circuit adds 1 disclosure proof
|
|
525
|
-
this.topicToExpectedProofCount[topic] =
|
|
526
|
-
neededCircuits.length === 0 ? 4 : 3 + neededCircuits.length
|
|
527
|
-
this.topicToFailedProofCount[topic] = 0
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
/**
|
|
531
|
-
* @notice Handle an encrypted message.
|
|
532
|
-
* @param request The request.
|
|
533
|
-
* @param outerRequest The outer request.
|
|
534
|
-
*/
|
|
535
|
-
private async handleEncryptedMessage(topic: string, request: JsonRpcRequest) {
|
|
536
|
-
logger.debug("Received encrypted message:", request)
|
|
537
|
-
if (request.method === "accept") {
|
|
538
|
-
logger.debug(`User accepted the request and is generating a proof`)
|
|
539
|
-
await Promise.all(this.onGeneratingProofCallbacks[topic].map((callback) => callback(topic)))
|
|
540
|
-
} else if (request.method === "reject") {
|
|
541
|
-
logger.debug(`User rejected the request`)
|
|
542
|
-
await Promise.all(this.onRejectCallbacks[topic].map((callback) => callback()))
|
|
543
|
-
} else if (request.method === "proof") {
|
|
544
|
-
logger.debug(`User generated proof`)
|
|
545
|
-
this.topicToProofs[topic].push(request.params)
|
|
546
|
-
await Promise.all(
|
|
547
|
-
this.onProofGeneratedCallbacks[topic].map((callback) => callback(request.params)),
|
|
548
|
-
)
|
|
549
|
-
// If the results were received before all the proofs were generated,
|
|
550
|
-
// we can handle the result now
|
|
551
|
-
if (
|
|
552
|
-
this.topicToResults[topic] &&
|
|
553
|
-
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
|
|
554
|
-
) {
|
|
555
|
-
await this.handleResult(topic)
|
|
556
|
-
}
|
|
557
|
-
} else if (request.method === "done") {
|
|
558
|
-
logger.debug(`User sent the query result`)
|
|
559
|
-
const formattedResult: QueryResult = request.params
|
|
560
|
-
// Make sure to reconvert the dates to Date objects
|
|
561
|
-
if (formattedResult.birthdate && formattedResult.birthdate.disclose) {
|
|
562
|
-
formattedResult.birthdate.disclose.result = new Date(
|
|
563
|
-
formattedResult.birthdate.disclose.result,
|
|
564
|
-
)
|
|
565
|
-
}
|
|
566
|
-
if (formattedResult.expiry_date && formattedResult.expiry_date.disclose) {
|
|
567
|
-
formattedResult.expiry_date.disclose.result = new Date(
|
|
568
|
-
formattedResult.expiry_date.disclose.result,
|
|
569
|
-
)
|
|
570
|
-
}
|
|
571
|
-
this.topicToResults[topic] = formattedResult
|
|
572
|
-
// Make sure all the proofs have been received, otherwise we'll handle the result later
|
|
573
|
-
// once the proofs have all been received
|
|
574
|
-
if (this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length) {
|
|
575
|
-
await this.handleResult(topic)
|
|
576
|
-
}
|
|
577
|
-
} else if (request.method === "error") {
|
|
578
|
-
const error = request.params.error
|
|
579
|
-
if (error && error === "This ID is not supported yet") {
|
|
580
|
-
// This means the user has an ID that is not supported yet
|
|
581
|
-
// So we won't receive any proofs and we can handle the result now
|
|
582
|
-
this.topicToExpectedProofCount[topic] = 0
|
|
583
|
-
this.topicToFailedProofCount[topic] += this.topicToExpectedProofCount[topic]
|
|
584
|
-
if (this.topicToResults[topic]) {
|
|
585
|
-
await this.handleResult(topic)
|
|
586
|
-
}
|
|
587
|
-
} else if (error && error.startsWith("Cannot generate proof")) {
|
|
588
|
-
// This means one of the disclosure proofs failed to be generated
|
|
589
|
-
// So we need to remove one from the expected proof count
|
|
590
|
-
this.topicToExpectedProofCount[topic] -= 1
|
|
591
|
-
this.topicToFailedProofCount[topic] += 1
|
|
592
|
-
// If the expected proof count is now equal to the number of proofs received
|
|
593
|
-
// and the results were received, we can handle the result now
|
|
594
|
-
if (
|
|
595
|
-
this.topicToResults[topic] &&
|
|
596
|
-
this.topicToExpectedProofCount[topic] === this.topicToProofs[topic].length
|
|
597
|
-
) {
|
|
598
|
-
await this.handleResult(topic)
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
await Promise.all(this.onErrorCallbacks[topic].map((callback) => callback(error)))
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
private getZkPassportRequest(topic: string): QueryBuilder {
|
|
606
|
-
return {
|
|
607
|
-
eq: <T extends IDCredential>(key: T, value: IDCredentialValue<T>) => {
|
|
608
|
-
if (key === "issuing_country" || key === "nationality") {
|
|
609
|
-
value = normalizeCountry(value as CountryName) as IDCredentialValue<T>
|
|
610
|
-
}
|
|
611
|
-
generalCompare("eq", key, value, topic, this.topicToConfig)
|
|
612
|
-
return this.getZkPassportRequest(topic)
|
|
613
|
-
},
|
|
614
|
-
gte: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => {
|
|
615
|
-
numericalCompare("gte", key, value, topic, this.topicToConfig)
|
|
616
|
-
if (key === "age" && ((value as number) < 1 || (value as number) >= 100)) {
|
|
617
|
-
throw new Error("Age must be between 1 and 99 (inclusive)")
|
|
618
|
-
}
|
|
619
|
-
return this.getZkPassportRequest(topic)
|
|
620
|
-
},
|
|
621
|
-
/*gt: <T extends NumericalIDCredential>(key: T, value: IDCredentialValue<T>) => {
|
|
622
|
-
numericalCompare('gt', key, value, topic, this.topicToConfig)
|
|
623
|
-
return this.getZkPassportRequest(topic)
|
|
624
|
-
},*/
|
|
625
|
-
lte: <T extends "birthdate" | "expiry_date">(key: T, value: IDCredentialValue<T>) => {
|
|
626
|
-
numericalCompare("lte", key, value, topic, this.topicToConfig)
|
|
627
|
-
return this.getZkPassportRequest(topic)
|
|
628
|
-
},
|
|
629
|
-
lt: <T extends "age">(key: T, value: IDCredentialValue<T>) => {
|
|
630
|
-
numericalCompare("lt", key, value, topic, this.topicToConfig)
|
|
631
|
-
return this.getZkPassportRequest(topic)
|
|
632
|
-
},
|
|
633
|
-
range: <T extends NumericalIDCredential>(
|
|
634
|
-
key: T,
|
|
635
|
-
start: IDCredentialValue<T>,
|
|
636
|
-
end: IDCredentialValue<T>,
|
|
637
|
-
) => {
|
|
638
|
-
rangeCompare(key, [start, end], topic, this.topicToConfig)
|
|
639
|
-
return this.getZkPassportRequest(topic)
|
|
640
|
-
},
|
|
641
|
-
in: <T extends "nationality" | "issuing_country">(key: T, value: IDCredentialValue<T>[]) => {
|
|
642
|
-
value = value.map((v) => normalizeCountry(v as CountryName)) as IDCredentialValue<T>[]
|
|
643
|
-
generalCompare("in", key, value, topic, this.topicToConfig)
|
|
644
|
-
return this.getZkPassportRequest(topic)
|
|
645
|
-
},
|
|
646
|
-
out: <T extends "nationality" | "issuing_country">(key: T, value: IDCredentialValue<T>[]) => {
|
|
647
|
-
value = value.map((v) => normalizeCountry(v as CountryName)) as IDCredentialValue<T>[]
|
|
648
|
-
generalCompare("out", key, value, topic, this.topicToConfig)
|
|
649
|
-
return this.getZkPassportRequest(topic)
|
|
650
|
-
},
|
|
651
|
-
disclose: (key: DisclosableIDCredential) => {
|
|
652
|
-
this.topicToConfig[topic][key] = {
|
|
653
|
-
...this.topicToConfig[topic][key],
|
|
654
|
-
disclose: true,
|
|
655
|
-
}
|
|
656
|
-
return this.getZkPassportRequest(topic)
|
|
657
|
-
},
|
|
658
|
-
bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => {
|
|
659
|
-
this.topicToConfig[topic].bind = {
|
|
660
|
-
...this.topicToConfig[topic].bind,
|
|
661
|
-
[key]: value,
|
|
662
|
-
}
|
|
663
|
-
return this.getZkPassportRequest(topic)
|
|
664
|
-
},
|
|
665
|
-
done: () => {
|
|
666
|
-
this.setExpectedProofCount(topic)
|
|
667
|
-
return {
|
|
668
|
-
url: this._getUrl(topic),
|
|
669
|
-
requestId: topic,
|
|
670
|
-
onRequestReceived: (callback: () => void) =>
|
|
671
|
-
this.onRequestReceivedCallbacks[topic].push(callback),
|
|
672
|
-
onGeneratingProof: (callback: () => void) =>
|
|
673
|
-
this.onGeneratingProofCallbacks[topic].push(callback),
|
|
674
|
-
onBridgeConnect: (callback: () => void) =>
|
|
675
|
-
this.onBridgeConnectCallbacks[topic].push(callback),
|
|
676
|
-
onProofGenerated: (callback: (proof: ProofResult) => void) =>
|
|
677
|
-
this.onProofGeneratedCallbacks[topic].push(callback),
|
|
678
|
-
onResult: (
|
|
679
|
-
callback: (response: {
|
|
680
|
-
uniqueIdentifier: string | undefined
|
|
681
|
-
verified: boolean
|
|
682
|
-
result: QueryResult
|
|
683
|
-
queryResultErrors?: QueryResultErrors
|
|
684
|
-
}) => void,
|
|
685
|
-
) => this.onResultCallbacks[topic].push(callback),
|
|
686
|
-
onReject: (callback: () => void) => this.onRejectCallbacks[topic].push(callback),
|
|
687
|
-
onError: (callback: (error: string) => void) =>
|
|
688
|
-
this.onErrorCallbacks[topic].push(callback),
|
|
689
|
-
isBridgeConnected: () => this.topicToBridge[topic].isBridgeConnected(),
|
|
690
|
-
requestReceived: () => this.topicToRequestReceived[topic] === true,
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* @notice Create a new request
|
|
698
|
-
* @param name Your service name
|
|
699
|
-
* @param logo The logo of your service
|
|
700
|
-
* @param purpose To explain what you want to do with the user's data
|
|
701
|
-
* @param scope Scope this request to a specific use case
|
|
702
|
-
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
703
|
-
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
704
|
-
* @param evmChain The EVM chain to use for the request (if using the proof onchain)
|
|
705
|
-
* @returns The query builder object.
|
|
706
|
-
*/
|
|
707
|
-
public async request({
|
|
708
|
-
name,
|
|
709
|
-
logo,
|
|
710
|
-
purpose,
|
|
711
|
-
scope,
|
|
712
|
-
mode,
|
|
713
|
-
evmChain,
|
|
714
|
-
validity,
|
|
715
|
-
devMode,
|
|
716
|
-
topicOverride,
|
|
717
|
-
keyPairOverride,
|
|
718
|
-
cloudProverUrl,
|
|
719
|
-
bridgeUrl,
|
|
720
|
-
}: {
|
|
721
|
-
name: string
|
|
722
|
-
logo: string
|
|
723
|
-
purpose: string
|
|
724
|
-
scope?: string
|
|
725
|
-
mode?: ProofMode
|
|
726
|
-
evmChain?: EVMChain
|
|
727
|
-
validity?: number
|
|
728
|
-
devMode?: boolean
|
|
729
|
-
topicOverride?: string
|
|
730
|
-
keyPairOverride?: { privateKey: Uint8Array; publicKey: Uint8Array }
|
|
731
|
-
cloudProverUrl?: string
|
|
732
|
-
bridgeUrl?: string
|
|
733
|
-
}): Promise<QueryBuilder> {
|
|
734
|
-
const bridge = await Bridge.create({
|
|
735
|
-
keyPair: keyPairOverride,
|
|
736
|
-
bridgeId: topicOverride,
|
|
737
|
-
bridgeUrl,
|
|
738
|
-
})
|
|
739
|
-
|
|
740
|
-
const topic = bridge.connection.getBridgeId()
|
|
741
|
-
|
|
742
|
-
this.topicToConfig[topic] = {}
|
|
743
|
-
this.topicToService[topic] = {
|
|
744
|
-
name,
|
|
745
|
-
logo,
|
|
746
|
-
purpose,
|
|
747
|
-
scope,
|
|
748
|
-
chainId: evmChain ? getChainIdFromEVMChain(evmChain) : undefined,
|
|
749
|
-
cloudProverUrl,
|
|
750
|
-
bridgeUrl,
|
|
751
|
-
}
|
|
752
|
-
this.topicToProofs[topic] = []
|
|
753
|
-
this.topicToExpectedProofCount[topic] = 0
|
|
754
|
-
this.topicToLocalConfig[topic] = {
|
|
755
|
-
// Default to 6 months
|
|
756
|
-
validity: validity || 6 * 30,
|
|
757
|
-
mode: mode || "fast",
|
|
758
|
-
devMode: devMode || false,
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
this.onRequestReceivedCallbacks[topic] = []
|
|
762
|
-
this.onGeneratingProofCallbacks[topic] = []
|
|
763
|
-
this.onBridgeConnectCallbacks[topic] = []
|
|
764
|
-
this.onProofGeneratedCallbacks[topic] = []
|
|
765
|
-
this.onResultCallbacks[topic] = []
|
|
766
|
-
this.onRejectCallbacks[topic] = []
|
|
767
|
-
this.onErrorCallbacks[topic] = []
|
|
768
|
-
|
|
769
|
-
this.topicToPublicKey[topic] = bridge.getPublicKey()
|
|
770
|
-
|
|
771
|
-
this.topicToBridge[topic] = bridge
|
|
772
|
-
bridge.onConnect(async (reconnection: boolean) => {
|
|
773
|
-
logger.debug("Bridge connected")
|
|
774
|
-
logger.debug("Is reconnection:", reconnection)
|
|
775
|
-
await Promise.all(this.onBridgeConnectCallbacks[topic].map((callback) => callback()))
|
|
776
|
-
})
|
|
777
|
-
bridge.onSecureChannelEstablished(async () => {
|
|
778
|
-
logger.debug("Secure channel established")
|
|
779
|
-
await Promise.all(this.onRequestReceivedCallbacks[topic].map((callback) => callback()))
|
|
780
|
-
})
|
|
781
|
-
bridge.onSecureMessage(async (message: any) => {
|
|
782
|
-
logger.debug("Received message:", message)
|
|
783
|
-
this.handleEncryptedMessage(topic, message)
|
|
784
|
-
})
|
|
785
|
-
return this.getZkPassportRequest(topic)
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
private checkDiscloseBytesPublicInputs(proof: ProofResult, queryResult: QueryResult) {
|
|
789
|
-
const queryResultErrors: QueryResultErrors = {
|
|
790
|
-
sig_check_dsc: {},
|
|
791
|
-
sig_check_id_data: {},
|
|
792
|
-
data_check_integrity: {},
|
|
793
|
-
disclose: {},
|
|
794
|
-
age: {},
|
|
795
|
-
birthdate: {},
|
|
796
|
-
expiry_date: {},
|
|
797
|
-
document_type: {},
|
|
798
|
-
issuing_country: {},
|
|
799
|
-
gender: {},
|
|
800
|
-
nationality: {},
|
|
801
|
-
firstname: {},
|
|
802
|
-
lastname: {},
|
|
803
|
-
fullname: {},
|
|
804
|
-
document_number: {},
|
|
805
|
-
outer: {},
|
|
806
|
-
bind: {},
|
|
807
|
-
}
|
|
808
|
-
let isCorrect = true
|
|
809
|
-
// We can't be certain that the disclosed data is for a passport or an ID card
|
|
810
|
-
// so we need to check both (unless the document type is revealed)
|
|
811
|
-
const disclosedDataPassport = DisclosedData.fromDisclosedBytes(
|
|
812
|
-
(proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
|
|
813
|
-
"passport",
|
|
814
|
-
)
|
|
815
|
-
const disclosedDataIDCard = DisclosedData.fromDisclosedBytes(
|
|
816
|
-
(proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
|
|
817
|
-
"id_card",
|
|
818
|
-
)
|
|
819
|
-
if (queryResult.document_type) {
|
|
820
|
-
// Document type is always at the same index in the disclosed data
|
|
821
|
-
if (
|
|
822
|
-
queryResult.document_type.eq &&
|
|
823
|
-
queryResult.document_type.eq.result &&
|
|
824
|
-
queryResult.document_type.eq.expected !== disclosedDataPassport.documentType
|
|
825
|
-
) {
|
|
826
|
-
console.warn("Document type does not match the expected document type")
|
|
827
|
-
isCorrect = false
|
|
828
|
-
queryResultErrors.document_type.eq = {
|
|
829
|
-
expected: `${queryResult.document_type.eq.expected}`,
|
|
830
|
-
received: `${disclosedDataPassport.documentType ?? disclosedDataIDCard.documentType}`,
|
|
831
|
-
message: "Document type does not match the expected document type",
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (queryResult.document_type.disclose?.result !== disclosedDataIDCard.documentType) {
|
|
835
|
-
console.warn("Document type does not match the disclosed document type in query result")
|
|
836
|
-
isCorrect = false
|
|
837
|
-
queryResultErrors.document_type.disclose = {
|
|
838
|
-
expected: `${queryResult.document_type.disclose?.result}`,
|
|
839
|
-
received: `${disclosedDataIDCard.documentType ?? disclosedDataPassport.documentType}`,
|
|
840
|
-
message: "Document type does not match the disclosed document type in query result",
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
if (queryResult.birthdate) {
|
|
845
|
-
const birthdatePassport = disclosedDataPassport.dateOfBirth
|
|
846
|
-
const birthdateIDCard = disclosedDataIDCard.dateOfBirth
|
|
847
|
-
if (
|
|
848
|
-
queryResult.birthdate.eq &&
|
|
849
|
-
queryResult.birthdate.eq.result &&
|
|
850
|
-
queryResult.birthdate.eq.expected.getTime() !== birthdatePassport.getTime() &&
|
|
851
|
-
queryResult.birthdate.eq.expected.getTime() !== birthdateIDCard.getTime()
|
|
852
|
-
) {
|
|
853
|
-
console.warn("Birthdate does not match the expected birthdate")
|
|
854
|
-
isCorrect = false
|
|
855
|
-
queryResultErrors.birthdate.eq = {
|
|
856
|
-
expected: `${queryResult.birthdate.eq.expected.toISOString()}`,
|
|
857
|
-
received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
|
|
858
|
-
message: "Birthdate does not match the expected birthdate",
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
if (
|
|
862
|
-
queryResult.birthdate.disclose &&
|
|
863
|
-
queryResult.birthdate.disclose.result.getTime() !== birthdatePassport.getTime() &&
|
|
864
|
-
queryResult.birthdate.disclose.result.getTime() !== birthdateIDCard.getTime()
|
|
865
|
-
) {
|
|
866
|
-
console.warn("Birthdate does not match the disclosed birthdate in query result")
|
|
867
|
-
isCorrect = false
|
|
868
|
-
queryResultErrors.birthdate.disclose = {
|
|
869
|
-
expected: `${queryResult.birthdate.disclose.result.toISOString()}`,
|
|
870
|
-
received: `${birthdatePassport?.toISOString() ?? birthdateIDCard?.toISOString()}`,
|
|
871
|
-
message: "Birthdate does not match the disclosed birthdate in query result",
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
if (queryResult.expiry_date) {
|
|
876
|
-
const expiryDatePassport = disclosedDataPassport.dateOfExpiry
|
|
877
|
-
const expiryDateIDCard = disclosedDataIDCard.dateOfExpiry
|
|
878
|
-
if (
|
|
879
|
-
queryResult.expiry_date.eq &&
|
|
880
|
-
queryResult.expiry_date.eq.result &&
|
|
881
|
-
queryResult.expiry_date.eq.expected.getTime() !== expiryDatePassport.getTime() &&
|
|
882
|
-
queryResult.expiry_date.eq.expected.getTime() !== expiryDateIDCard.getTime()
|
|
883
|
-
) {
|
|
884
|
-
console.warn("Expiry date does not match the expected expiry date")
|
|
885
|
-
isCorrect = false
|
|
886
|
-
queryResultErrors.expiry_date.eq = {
|
|
887
|
-
expected: `${queryResult.expiry_date.eq.expected.toISOString()}`,
|
|
888
|
-
received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
|
|
889
|
-
message: "Expiry date does not match the expected expiry date",
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
if (
|
|
893
|
-
queryResult.expiry_date.disclose &&
|
|
894
|
-
queryResult.expiry_date.disclose.result.getTime() !== expiryDatePassport.getTime() &&
|
|
895
|
-
queryResult.expiry_date.disclose.result.getTime() !== expiryDateIDCard.getTime()
|
|
896
|
-
) {
|
|
897
|
-
console.warn("Expiry date does not match the disclosed expiry date in query result")
|
|
898
|
-
isCorrect = false
|
|
899
|
-
queryResultErrors.expiry_date.disclose = {
|
|
900
|
-
expected: `${queryResult.expiry_date.disclose.result.toISOString()}`,
|
|
901
|
-
received: `${expiryDatePassport?.toISOString() ?? expiryDateIDCard?.toISOString()}`,
|
|
902
|
-
message: "Expiry date does not match the disclosed expiry date in query result",
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (queryResult.nationality) {
|
|
907
|
-
const nationalityPassport = disclosedDataPassport.nationality
|
|
908
|
-
const nationalityIDCard = disclosedDataIDCard.nationality
|
|
909
|
-
if (
|
|
910
|
-
queryResult.nationality.eq &&
|
|
911
|
-
queryResult.nationality.eq.result &&
|
|
912
|
-
queryResult.nationality.eq.expected !== nationalityPassport &&
|
|
913
|
-
queryResult.nationality.eq.expected !== nationalityIDCard
|
|
914
|
-
) {
|
|
915
|
-
console.warn("Nationality does not match the expected nationality")
|
|
916
|
-
isCorrect = false
|
|
917
|
-
queryResultErrors.nationality.eq = {
|
|
918
|
-
expected: `${queryResult.nationality.eq.expected}`,
|
|
919
|
-
received: `${nationalityPassport ?? nationalityIDCard}`,
|
|
920
|
-
message: "Nationality does not match the expected nationality",
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
if (
|
|
924
|
-
queryResult.nationality.disclose &&
|
|
925
|
-
queryResult.nationality.disclose.result !== nationalityPassport &&
|
|
926
|
-
queryResult.nationality.disclose.result !== nationalityIDCard
|
|
927
|
-
) {
|
|
928
|
-
console.warn("Nationality does not match the disclosed nationality in query result")
|
|
929
|
-
isCorrect = false
|
|
930
|
-
queryResultErrors.nationality.disclose = {
|
|
931
|
-
expected: `${queryResult.nationality.disclose.result}`,
|
|
932
|
-
received: `${nationalityPassport ?? nationalityIDCard}`,
|
|
933
|
-
message: "Nationality does not match the disclosed nationality in query result",
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (queryResult.document_number) {
|
|
938
|
-
const documentNumberPassport = disclosedDataPassport.documentNumber
|
|
939
|
-
const documentNumberIDCard = disclosedDataIDCard.documentNumber
|
|
940
|
-
if (
|
|
941
|
-
queryResult.document_number.eq &&
|
|
942
|
-
queryResult.document_number.eq.result &&
|
|
943
|
-
queryResult.document_number.eq.expected !== documentNumberPassport &&
|
|
944
|
-
queryResult.document_number.eq.expected !== documentNumberIDCard
|
|
945
|
-
) {
|
|
946
|
-
console.warn("Document number does not match the expected document number")
|
|
947
|
-
isCorrect = false
|
|
948
|
-
queryResultErrors.document_number.eq = {
|
|
949
|
-
expected: `${queryResult.document_number.eq.expected}`,
|
|
950
|
-
received: `${documentNumberPassport ?? documentNumberIDCard}`,
|
|
951
|
-
message: "Document number does not match the expected document number",
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
if (
|
|
955
|
-
queryResult.document_number.disclose &&
|
|
956
|
-
queryResult.document_number.disclose.result !== documentNumberPassport &&
|
|
957
|
-
queryResult.document_number.disclose.result !== documentNumberIDCard
|
|
958
|
-
) {
|
|
959
|
-
console.warn("Document number does not match the disclosed document number in query result")
|
|
960
|
-
isCorrect = false
|
|
961
|
-
queryResultErrors.document_number.disclose = {
|
|
962
|
-
expected: `${queryResult.document_number.disclose.result}`,
|
|
963
|
-
received: `${documentNumberPassport ?? documentNumberIDCard}`,
|
|
964
|
-
message: "Document number does not match the disclosed document number in query result",
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
if (queryResult.gender) {
|
|
969
|
-
const genderPassport = disclosedDataPassport.gender
|
|
970
|
-
const genderIDCard = disclosedDataIDCard.gender
|
|
971
|
-
if (
|
|
972
|
-
queryResult.gender.eq &&
|
|
973
|
-
queryResult.gender.eq.result &&
|
|
974
|
-
queryResult.gender.eq.expected !== genderPassport &&
|
|
975
|
-
queryResult.gender.eq.expected !== genderIDCard
|
|
976
|
-
) {
|
|
977
|
-
console.warn("Gender does not match the expected gender")
|
|
978
|
-
isCorrect = false
|
|
979
|
-
queryResultErrors.gender.eq = {
|
|
980
|
-
expected: `${queryResult.gender.eq.expected}`,
|
|
981
|
-
received: `${genderPassport ?? genderIDCard}`,
|
|
982
|
-
message: "Gender does not match the expected gender",
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
if (
|
|
986
|
-
queryResult.gender.disclose &&
|
|
987
|
-
queryResult.gender.disclose.result !== genderPassport &&
|
|
988
|
-
queryResult.gender.disclose.result !== genderIDCard
|
|
989
|
-
) {
|
|
990
|
-
console.warn("Gender does not match the disclosed gender in query result")
|
|
991
|
-
isCorrect = false
|
|
992
|
-
queryResultErrors.gender.disclose = {
|
|
993
|
-
expected: `${queryResult.gender.disclose.result}`,
|
|
994
|
-
received: `${genderPassport ?? genderIDCard}`,
|
|
995
|
-
message: "Gender does not match the disclosed gender in query result",
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
if (queryResult.issuing_country) {
|
|
1000
|
-
const issuingCountryPassport = disclosedDataPassport.issuingCountry
|
|
1001
|
-
const issuingCountryIDCard = disclosedDataIDCard.issuingCountry
|
|
1002
|
-
if (
|
|
1003
|
-
queryResult.issuing_country.eq &&
|
|
1004
|
-
queryResult.issuing_country.eq.result &&
|
|
1005
|
-
queryResult.issuing_country.eq.expected !== issuingCountryPassport &&
|
|
1006
|
-
queryResult.issuing_country.eq.expected !== issuingCountryIDCard
|
|
1007
|
-
) {
|
|
1008
|
-
console.warn("Issuing country does not match the expected issuing country")
|
|
1009
|
-
isCorrect = false
|
|
1010
|
-
queryResultErrors.issuing_country.eq = {
|
|
1011
|
-
expected: `${queryResult.issuing_country.eq.expected}`,
|
|
1012
|
-
received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
|
|
1013
|
-
message: "Issuing country does not match the expected issuing country",
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
if (
|
|
1017
|
-
queryResult.issuing_country.disclose &&
|
|
1018
|
-
queryResult.issuing_country.disclose.result !== issuingCountryPassport &&
|
|
1019
|
-
queryResult.issuing_country.disclose.result !== issuingCountryIDCard
|
|
1020
|
-
) {
|
|
1021
|
-
console.warn("Issuing country does not match the disclosed issuing country in query result")
|
|
1022
|
-
isCorrect = false
|
|
1023
|
-
queryResultErrors.issuing_country.disclose = {
|
|
1024
|
-
expected: `${queryResult.issuing_country.disclose.result}`,
|
|
1025
|
-
received: `${issuingCountryPassport ?? issuingCountryIDCard}`,
|
|
1026
|
-
message: "Issuing country does not match the disclosed issuing country in query result",
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
if (queryResult.fullname) {
|
|
1031
|
-
const fullnamePassport = disclosedDataPassport.name
|
|
1032
|
-
const fullnameIDCard = disclosedDataIDCard.name
|
|
1033
|
-
if (
|
|
1034
|
-
queryResult.fullname.eq &&
|
|
1035
|
-
queryResult.fullname.eq.result &&
|
|
1036
|
-
formatName(queryResult.fullname.eq.expected).toLowerCase() !==
|
|
1037
|
-
fullnamePassport.toLowerCase() &&
|
|
1038
|
-
formatName(queryResult.fullname.eq.expected).toLowerCase() !== fullnameIDCard.toLowerCase()
|
|
1039
|
-
) {
|
|
1040
|
-
console.warn("Fullname does not match the expected fullname")
|
|
1041
|
-
isCorrect = false
|
|
1042
|
-
queryResultErrors.fullname.eq = {
|
|
1043
|
-
expected: `${queryResult.fullname.eq.expected}`,
|
|
1044
|
-
received: `${fullnamePassport ?? fullnameIDCard}`,
|
|
1045
|
-
message: "Fullname does not match the expected fullname",
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if (
|
|
1049
|
-
queryResult.fullname.disclose &&
|
|
1050
|
-
formatName(queryResult.fullname.disclose.result).toLowerCase() !==
|
|
1051
|
-
fullnamePassport.toLowerCase() &&
|
|
1052
|
-
formatName(queryResult.fullname.disclose.result).toLowerCase() !==
|
|
1053
|
-
fullnameIDCard.toLowerCase()
|
|
1054
|
-
) {
|
|
1055
|
-
console.warn("Fullname does not match the disclosed fullname in query result")
|
|
1056
|
-
isCorrect = false
|
|
1057
|
-
queryResultErrors.fullname.disclose = {
|
|
1058
|
-
expected: `${queryResult.fullname.disclose.result}`,
|
|
1059
|
-
received: `${fullnamePassport ?? fullnameIDCard}`,
|
|
1060
|
-
message: "Fullname does not match the disclosed fullname in query result",
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
if (queryResult.firstname) {
|
|
1065
|
-
// If fullname was not revealed, then the name could be either the first name or last name
|
|
1066
|
-
const firstnamePassport =
|
|
1067
|
-
disclosedDataPassport.firstName && disclosedDataPassport.firstName.length > 0
|
|
1068
|
-
? disclosedDataPassport.firstName
|
|
1069
|
-
: disclosedDataPassport.name
|
|
1070
|
-
const firstnameIDCard =
|
|
1071
|
-
disclosedDataIDCard.firstName && disclosedDataIDCard.firstName.length > 0
|
|
1072
|
-
? disclosedDataIDCard.firstName
|
|
1073
|
-
: disclosedDataIDCard.name
|
|
1074
|
-
if (
|
|
1075
|
-
queryResult.firstname.eq &&
|
|
1076
|
-
queryResult.firstname.eq.result &&
|
|
1077
|
-
formatName(queryResult.firstname.eq.expected).toLowerCase() !==
|
|
1078
|
-
firstnamePassport.toLowerCase() &&
|
|
1079
|
-
formatName(queryResult.firstname.eq.expected).toLowerCase() !==
|
|
1080
|
-
firstnameIDCard.toLowerCase()
|
|
1081
|
-
) {
|
|
1082
|
-
console.warn("Firstname does not match the expected firstname")
|
|
1083
|
-
isCorrect = false
|
|
1084
|
-
queryResultErrors.firstname.eq = {
|
|
1085
|
-
expected: `${queryResult.firstname.eq.expected}`,
|
|
1086
|
-
received: `${firstnamePassport ?? firstnameIDCard}`,
|
|
1087
|
-
message: "Firstname does not match the expected firstname",
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
if (
|
|
1091
|
-
queryResult.firstname.disclose &&
|
|
1092
|
-
formatName(queryResult.firstname.disclose.result).toLowerCase() !==
|
|
1093
|
-
firstnamePassport.toLowerCase() &&
|
|
1094
|
-
formatName(queryResult.firstname.disclose.result).toLowerCase() !==
|
|
1095
|
-
firstnameIDCard.toLowerCase()
|
|
1096
|
-
) {
|
|
1097
|
-
console.warn("Firstname does not match the disclosed firstname in query result")
|
|
1098
|
-
isCorrect = false
|
|
1099
|
-
queryResultErrors.firstname.disclose = {
|
|
1100
|
-
expected: `${queryResult.firstname.disclose.result}`,
|
|
1101
|
-
received: `${firstnamePassport ?? firstnameIDCard}`,
|
|
1102
|
-
message: "Firstname does not match the disclosed firstname in query result",
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
if (queryResult.lastname) {
|
|
1107
|
-
// If fullname was not revealed, then the name could be either the first name or last name
|
|
1108
|
-
const lastnamePassport =
|
|
1109
|
-
disclosedDataPassport.lastName && disclosedDataPassport.lastName.length > 0
|
|
1110
|
-
? disclosedDataPassport.lastName
|
|
1111
|
-
: disclosedDataPassport.name
|
|
1112
|
-
const lastnameIDCard =
|
|
1113
|
-
disclosedDataIDCard.lastName && disclosedDataIDCard.lastName.length > 0
|
|
1114
|
-
? disclosedDataIDCard.lastName
|
|
1115
|
-
: disclosedDataIDCard.name
|
|
1116
|
-
if (
|
|
1117
|
-
queryResult.lastname.eq &&
|
|
1118
|
-
queryResult.lastname.eq.result &&
|
|
1119
|
-
formatName(queryResult.lastname.eq.expected).toLowerCase() !==
|
|
1120
|
-
lastnamePassport.toLowerCase() &&
|
|
1121
|
-
formatName(queryResult.lastname.eq.expected).toLowerCase() !== lastnameIDCard.toLowerCase()
|
|
1122
|
-
) {
|
|
1123
|
-
console.warn("Lastname does not match the expected lastname")
|
|
1124
|
-
isCorrect = false
|
|
1125
|
-
queryResultErrors.lastname.eq = {
|
|
1126
|
-
expected: `${queryResult.lastname.eq.expected}`,
|
|
1127
|
-
received: `${lastnamePassport ?? lastnameIDCard}`,
|
|
1128
|
-
message: "Lastname does not match the expected lastname",
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
if (
|
|
1132
|
-
queryResult.lastname.disclose &&
|
|
1133
|
-
formatName(queryResult.lastname.disclose.result).toLowerCase() !==
|
|
1134
|
-
lastnamePassport.toLowerCase() &&
|
|
1135
|
-
formatName(queryResult.lastname.disclose.result).toLowerCase() !==
|
|
1136
|
-
lastnameIDCard.toLowerCase()
|
|
1137
|
-
) {
|
|
1138
|
-
console.warn("Lastname does not match the disclosed lastname in query result")
|
|
1139
|
-
isCorrect = false
|
|
1140
|
-
queryResultErrors.lastname.disclose = {
|
|
1141
|
-
expected: `${queryResult.lastname.disclose.result}`,
|
|
1142
|
-
received: `${lastnamePassport ?? lastnameIDCard}`,
|
|
1143
|
-
message: "Lastname does not match the disclosed lastname in query result",
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
return { isCorrect, queryResultErrors }
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
private checkAgePublicInputs(proof: ProofResult, queryResult: QueryResult) {
|
|
1151
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1152
|
-
sig_check_dsc: {},
|
|
1153
|
-
sig_check_id_data: {},
|
|
1154
|
-
data_check_integrity: {},
|
|
1155
|
-
disclose: {},
|
|
1156
|
-
age: {},
|
|
1157
|
-
birthdate: {},
|
|
1158
|
-
expiry_date: {},
|
|
1159
|
-
document_type: {},
|
|
1160
|
-
issuing_country: {},
|
|
1161
|
-
gender: {},
|
|
1162
|
-
nationality: {},
|
|
1163
|
-
firstname: {},
|
|
1164
|
-
lastname: {},
|
|
1165
|
-
fullname: {},
|
|
1166
|
-
document_number: {},
|
|
1167
|
-
outer: {},
|
|
1168
|
-
bind: {},
|
|
1169
|
-
}
|
|
1170
|
-
let isCorrect = true
|
|
1171
|
-
const currentTime = new Date()
|
|
1172
|
-
const today = new Date(
|
|
1173
|
-
currentTime.getFullYear(),
|
|
1174
|
-
currentTime.getMonth(),
|
|
1175
|
-
currentTime.getDate(),
|
|
1176
|
-
0,
|
|
1177
|
-
0,
|
|
1178
|
-
0,
|
|
1179
|
-
0,
|
|
1180
|
-
)
|
|
1181
|
-
const minAge = getMinAgeFromCommittedInputs(
|
|
1182
|
-
proof.committedInputs?.compare_age as AgeCommittedInputs,
|
|
1183
|
-
)
|
|
1184
|
-
const maxAge = getMaxAgeFromCommittedInputs(
|
|
1185
|
-
proof.committedInputs?.compare_age as AgeCommittedInputs,
|
|
1186
|
-
)
|
|
1187
|
-
if (queryResult.age) {
|
|
1188
|
-
if (
|
|
1189
|
-
queryResult.age.gte &&
|
|
1190
|
-
queryResult.age.gte.result &&
|
|
1191
|
-
minAge !== (queryResult.age.gte.expected as number)
|
|
1192
|
-
) {
|
|
1193
|
-
console.warn("Age is not greater than or equal to the expected age")
|
|
1194
|
-
isCorrect = false
|
|
1195
|
-
queryResultErrors.age.gte = {
|
|
1196
|
-
expected: queryResult.age.gte.expected,
|
|
1197
|
-
received: minAge,
|
|
1198
|
-
message: "Age is not greater than or equal to the expected age",
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
if (
|
|
1202
|
-
queryResult.age.lt &&
|
|
1203
|
-
queryResult.age.lt.result &&
|
|
1204
|
-
maxAge !== (queryResult.age.lt.expected as number)
|
|
1205
|
-
) {
|
|
1206
|
-
console.warn("Age is not less than the expected age")
|
|
1207
|
-
isCorrect = false
|
|
1208
|
-
queryResultErrors.age.lt = {
|
|
1209
|
-
expected: queryResult.age.lt.expected,
|
|
1210
|
-
received: maxAge,
|
|
1211
|
-
message: "Age is not less than the expected age",
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
if (queryResult.age.range) {
|
|
1215
|
-
if (
|
|
1216
|
-
queryResult.age.range.result &&
|
|
1217
|
-
(minAge !== (queryResult.age.range.expected[0] as number) ||
|
|
1218
|
-
maxAge !== (queryResult.age.range.expected[1] as number))
|
|
1219
|
-
) {
|
|
1220
|
-
console.warn("Age is not in the expected range")
|
|
1221
|
-
isCorrect = false
|
|
1222
|
-
queryResultErrors.age.range = {
|
|
1223
|
-
expected: queryResult.age.range.expected,
|
|
1224
|
-
received: [minAge, maxAge],
|
|
1225
|
-
message: "Age is not in the expected range",
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
if (!queryResult.age.lt && !queryResult.age.range && maxAge != 0) {
|
|
1230
|
-
console.warn("Maximum age should be equal to 0")
|
|
1231
|
-
isCorrect = false
|
|
1232
|
-
queryResultErrors.age.disclose = {
|
|
1233
|
-
expected: 0,
|
|
1234
|
-
received: maxAge,
|
|
1235
|
-
message: "Maximum age should be equal to 0",
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
if (!queryResult.age.gte && !queryResult.age.range && minAge != 0) {
|
|
1239
|
-
console.warn("Minimum age should be equal to 0")
|
|
1240
|
-
isCorrect = false
|
|
1241
|
-
queryResultErrors.age.disclose = {
|
|
1242
|
-
expected: 0,
|
|
1243
|
-
received: minAge,
|
|
1244
|
-
message: "Minimum age should be equal to 0",
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
if (
|
|
1248
|
-
queryResult.age.disclose &&
|
|
1249
|
-
(queryResult.age.disclose.result !== minAge || queryResult.age.disclose.result !== maxAge)
|
|
1250
|
-
) {
|
|
1251
|
-
console.warn("Age does not match the disclosed age in query result")
|
|
1252
|
-
isCorrect = false
|
|
1253
|
-
queryResultErrors.age.disclose = {
|
|
1254
|
-
expected: `${minAge}`,
|
|
1255
|
-
received: `${queryResult.age.disclose.result}`,
|
|
1256
|
-
message: "Age does not match the disclosed age in query result",
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
} else {
|
|
1260
|
-
console.warn("Age is not set in the query result")
|
|
1261
|
-
isCorrect = false
|
|
1262
|
-
queryResultErrors.age.disclose = {
|
|
1263
|
-
message: "Age is not set in the query result",
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
const currentDate = getCurrentDateFromCommittedInputs(
|
|
1267
|
-
proof.committedInputs?.compare_age as AgeCommittedInputs,
|
|
1268
|
-
)
|
|
1269
|
-
if (
|
|
1270
|
-
currentDate.getTime() !== today.getTime() &&
|
|
1271
|
-
currentDate.getTime() !== today.getTime() - 86400000
|
|
1272
|
-
) {
|
|
1273
|
-
console.warn("Current date in the proof is too old")
|
|
1274
|
-
isCorrect = false
|
|
1275
|
-
queryResultErrors.age.disclose = {
|
|
1276
|
-
expected: `${today.toISOString()}`,
|
|
1277
|
-
received: `${currentDate.toISOString()}`,
|
|
1278
|
-
message: "Current date in the proof is too old",
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
return { isCorrect, queryResultErrors }
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
private checkBirthdatePublicInputs(proof: ProofResult, queryResult: QueryResult) {
|
|
1285
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1286
|
-
sig_check_dsc: {},
|
|
1287
|
-
sig_check_id_data: {},
|
|
1288
|
-
data_check_integrity: {},
|
|
1289
|
-
disclose: {},
|
|
1290
|
-
age: {},
|
|
1291
|
-
birthdate: {},
|
|
1292
|
-
expiry_date: {},
|
|
1293
|
-
document_type: {},
|
|
1294
|
-
issuing_country: {},
|
|
1295
|
-
gender: {},
|
|
1296
|
-
nationality: {},
|
|
1297
|
-
firstname: {},
|
|
1298
|
-
lastname: {},
|
|
1299
|
-
fullname: {},
|
|
1300
|
-
document_number: {},
|
|
1301
|
-
outer: {},
|
|
1302
|
-
bind: {},
|
|
1303
|
-
}
|
|
1304
|
-
let isCorrect = true
|
|
1305
|
-
const currentTime = new Date()
|
|
1306
|
-
const today = new Date(
|
|
1307
|
-
currentTime.getFullYear(),
|
|
1308
|
-
currentTime.getMonth(),
|
|
1309
|
-
currentTime.getDate(),
|
|
1310
|
-
0,
|
|
1311
|
-
0,
|
|
1312
|
-
0,
|
|
1313
|
-
)
|
|
1314
|
-
const minDate = getMinDateFromCommittedInputs(
|
|
1315
|
-
proof.committedInputs?.compare_birthdate as DateCommittedInputs,
|
|
1316
|
-
)
|
|
1317
|
-
const maxDate = getMaxDateFromCommittedInputs(
|
|
1318
|
-
proof.committedInputs?.compare_birthdate as DateCommittedInputs,
|
|
1319
|
-
)
|
|
1320
|
-
const currentDate = getCurrentDateFromCommittedInputs(
|
|
1321
|
-
proof.committedInputs?.compare_birthdate as DateCommittedInputs,
|
|
1322
|
-
)
|
|
1323
|
-
if (queryResult.birthdate) {
|
|
1324
|
-
if (
|
|
1325
|
-
queryResult.birthdate.gte &&
|
|
1326
|
-
queryResult.birthdate.gte.result &&
|
|
1327
|
-
minDate !== queryResult.birthdate.gte.expected
|
|
1328
|
-
) {
|
|
1329
|
-
console.warn("Birthdate is not greater than or equal to the expected birthdate")
|
|
1330
|
-
isCorrect = false
|
|
1331
|
-
queryResultErrors.birthdate.gte = {
|
|
1332
|
-
expected: queryResult.birthdate.gte.expected,
|
|
1333
|
-
received: minDate,
|
|
1334
|
-
message: "Birthdate is not greater than or equal to the expected birthdate",
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
if (
|
|
1338
|
-
queryResult.birthdate.lte &&
|
|
1339
|
-
queryResult.birthdate.lte.result &&
|
|
1340
|
-
maxDate !== queryResult.birthdate.lte.expected
|
|
1341
|
-
) {
|
|
1342
|
-
console.warn("Birthdate is not less than the expected birthdate")
|
|
1343
|
-
isCorrect = false
|
|
1344
|
-
queryResultErrors.birthdate.lte = {
|
|
1345
|
-
expected: queryResult.birthdate.lte.expected,
|
|
1346
|
-
received: maxDate,
|
|
1347
|
-
message: "Birthdate is not less than the expected birthdate",
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
if (queryResult.birthdate.range) {
|
|
1351
|
-
if (
|
|
1352
|
-
queryResult.birthdate.range.result &&
|
|
1353
|
-
(minDate !== queryResult.birthdate.range.expected[0] ||
|
|
1354
|
-
maxDate !== queryResult.birthdate.range.expected[1])
|
|
1355
|
-
) {
|
|
1356
|
-
console.warn("Birthdate is not in the expected range")
|
|
1357
|
-
isCorrect = false
|
|
1358
|
-
queryResultErrors.birthdate.range = {
|
|
1359
|
-
expected: queryResult.birthdate.range.expected,
|
|
1360
|
-
received: [minDate, maxDate],
|
|
1361
|
-
message: "Birthdate is not in the expected range",
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
if (
|
|
1366
|
-
!queryResult.birthdate.lte &&
|
|
1367
|
-
!queryResult.birthdate.range &&
|
|
1368
|
-
maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()
|
|
1369
|
-
) {
|
|
1370
|
-
console.warn("Maximum birthdate should be equal to default date value")
|
|
1371
|
-
isCorrect = false
|
|
1372
|
-
queryResultErrors.birthdate.disclose = {
|
|
1373
|
-
expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
|
|
1374
|
-
received: `${maxDate.toISOString()}`,
|
|
1375
|
-
message: "Maximum birthdate should be equal to default date value",
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
if (
|
|
1379
|
-
!queryResult.birthdate.gte &&
|
|
1380
|
-
!queryResult.birthdate.range &&
|
|
1381
|
-
minDate.getTime() != DEFAULT_DATE_VALUE.getTime()
|
|
1382
|
-
) {
|
|
1383
|
-
console.warn("Minimum birthdate should be equal to default date value")
|
|
1384
|
-
isCorrect = false
|
|
1385
|
-
queryResultErrors.birthdate.disclose = {
|
|
1386
|
-
expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
|
|
1387
|
-
received: `${minDate.toISOString()}`,
|
|
1388
|
-
message: "Minimum birthdate should be equal to default date value",
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
} else {
|
|
1392
|
-
console.warn("Birthdate is not set in the query result")
|
|
1393
|
-
isCorrect = false
|
|
1394
|
-
queryResultErrors.birthdate.disclose = {
|
|
1395
|
-
message: "Birthdate is not set in the query result",
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
if (
|
|
1399
|
-
currentDate.getTime() !== today.getTime() &&
|
|
1400
|
-
currentDate.getTime() !== today.getTime() - 86400000
|
|
1401
|
-
) {
|
|
1402
|
-
console.warn("Current date in the proof is too old")
|
|
1403
|
-
isCorrect = false
|
|
1404
|
-
queryResultErrors.age.disclose = {
|
|
1405
|
-
expected: `${today.toISOString()}`,
|
|
1406
|
-
received: `${currentDate.toISOString()}`,
|
|
1407
|
-
message: "Current date in the proof is too old",
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
return { isCorrect, queryResultErrors }
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
private checkExpiryDatePublicInputs(proof: ProofResult, queryResult: QueryResult) {
|
|
1414
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1415
|
-
sig_check_dsc: {},
|
|
1416
|
-
sig_check_id_data: {},
|
|
1417
|
-
data_check_integrity: {},
|
|
1418
|
-
disclose: {},
|
|
1419
|
-
age: {},
|
|
1420
|
-
birthdate: {},
|
|
1421
|
-
expiry_date: {},
|
|
1422
|
-
document_type: {},
|
|
1423
|
-
issuing_country: {},
|
|
1424
|
-
gender: {},
|
|
1425
|
-
nationality: {},
|
|
1426
|
-
firstname: {},
|
|
1427
|
-
lastname: {},
|
|
1428
|
-
fullname: {},
|
|
1429
|
-
document_number: {},
|
|
1430
|
-
outer: {},
|
|
1431
|
-
bind: {},
|
|
1432
|
-
}
|
|
1433
|
-
let isCorrect = true
|
|
1434
|
-
const currentTime = new Date()
|
|
1435
|
-
const today = new Date(
|
|
1436
|
-
currentTime.getFullYear(),
|
|
1437
|
-
currentTime.getMonth(),
|
|
1438
|
-
currentTime.getDate(),
|
|
1439
|
-
0,
|
|
1440
|
-
0,
|
|
1441
|
-
0,
|
|
1442
|
-
)
|
|
1443
|
-
const minDate = getMinDateFromCommittedInputs(
|
|
1444
|
-
proof.committedInputs?.compare_expiry as DateCommittedInputs,
|
|
1445
|
-
)
|
|
1446
|
-
const maxDate = getMaxDateFromCommittedInputs(
|
|
1447
|
-
proof.committedInputs?.compare_expiry as DateCommittedInputs,
|
|
1448
|
-
)
|
|
1449
|
-
const currentDate = getCurrentDateFromCommittedInputs(
|
|
1450
|
-
proof.committedInputs?.compare_expiry as DateCommittedInputs,
|
|
1451
|
-
)
|
|
1452
|
-
if (queryResult.expiry_date) {
|
|
1453
|
-
if (
|
|
1454
|
-
queryResult.expiry_date.gte &&
|
|
1455
|
-
queryResult.expiry_date.gte.result &&
|
|
1456
|
-
minDate !== queryResult.expiry_date.gte.expected
|
|
1457
|
-
) {
|
|
1458
|
-
console.warn("Expiry date is not greater than or equal to the expected expiry date")
|
|
1459
|
-
isCorrect = false
|
|
1460
|
-
queryResultErrors.expiry_date.gte = {
|
|
1461
|
-
expected: queryResult.expiry_date.gte.expected,
|
|
1462
|
-
received: minDate,
|
|
1463
|
-
message: "Expiry date is not greater than or equal to the expected expiry date",
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
if (
|
|
1467
|
-
queryResult.expiry_date.lte &&
|
|
1468
|
-
queryResult.expiry_date.lte.result &&
|
|
1469
|
-
maxDate !== queryResult.expiry_date.lte.expected
|
|
1470
|
-
) {
|
|
1471
|
-
console.warn("Expiry date is not less than the expected expiry date")
|
|
1472
|
-
isCorrect = false
|
|
1473
|
-
queryResultErrors.expiry_date.lte = {
|
|
1474
|
-
expected: queryResult.expiry_date.lte.expected,
|
|
1475
|
-
received: maxDate,
|
|
1476
|
-
message: "Expiry date is not less than the expected expiry date",
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
if (queryResult.expiry_date.range) {
|
|
1480
|
-
if (
|
|
1481
|
-
queryResult.expiry_date.range.result &&
|
|
1482
|
-
(minDate !== queryResult.expiry_date.range.expected[0] ||
|
|
1483
|
-
maxDate !== queryResult.expiry_date.range.expected[1])
|
|
1484
|
-
) {
|
|
1485
|
-
console.warn("Expiry date is not in the expected range")
|
|
1486
|
-
isCorrect = false
|
|
1487
|
-
queryResultErrors.expiry_date.range = {
|
|
1488
|
-
expected: queryResult.expiry_date.range.expected,
|
|
1489
|
-
received: [minDate, maxDate],
|
|
1490
|
-
message: "Expiry date is not in the expected range",
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
if (
|
|
1495
|
-
!queryResult.expiry_date.lte &&
|
|
1496
|
-
!queryResult.expiry_date.range &&
|
|
1497
|
-
maxDate.getTime() != DEFAULT_DATE_VALUE.getTime()
|
|
1498
|
-
) {
|
|
1499
|
-
console.warn("Maximum expiry date should be equal to default date value")
|
|
1500
|
-
isCorrect = false
|
|
1501
|
-
queryResultErrors.expiry_date.disclose = {
|
|
1502
|
-
expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
|
|
1503
|
-
received: `${maxDate.toISOString()}`,
|
|
1504
|
-
message: "Maximum expiry date should be equal to default date value",
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
if (
|
|
1508
|
-
!queryResult.expiry_date.gte &&
|
|
1509
|
-
!queryResult.expiry_date.range &&
|
|
1510
|
-
minDate.getTime() != DEFAULT_DATE_VALUE.getTime()
|
|
1511
|
-
) {
|
|
1512
|
-
console.warn("Minimum expiry date should be equal to default date value")
|
|
1513
|
-
isCorrect = false
|
|
1514
|
-
queryResultErrors.expiry_date.disclose = {
|
|
1515
|
-
expected: `${DEFAULT_DATE_VALUE.toISOString()}`,
|
|
1516
|
-
received: `${minDate.toISOString()}`,
|
|
1517
|
-
message: "Minimum expiry date should be equal to default date value",
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
} else {
|
|
1521
|
-
console.warn("Expiry date is not set in the query result")
|
|
1522
|
-
isCorrect = false
|
|
1523
|
-
queryResultErrors.expiry_date.disclose = {
|
|
1524
|
-
message: "Expiry date is not set in the query result",
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
if (
|
|
1528
|
-
currentDate.getTime() !== today.getTime() &&
|
|
1529
|
-
currentDate.getTime() !== today.getTime() - 86400000
|
|
1530
|
-
) {
|
|
1531
|
-
console.warn("Current date in the proof is too old")
|
|
1532
|
-
isCorrect = false
|
|
1533
|
-
queryResultErrors.age.disclose = {
|
|
1534
|
-
expected: `${today.toISOString()}`,
|
|
1535
|
-
received: `${currentDate.toISOString()}`,
|
|
1536
|
-
message: "Current date in the proof is too old",
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return { isCorrect, queryResultErrors }
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
private checkNationalityExclusionPublicInputs(queryResult: QueryResult, countryList: string[]) {
|
|
1543
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1544
|
-
sig_check_dsc: {},
|
|
1545
|
-
sig_check_id_data: {},
|
|
1546
|
-
data_check_integrity: {},
|
|
1547
|
-
disclose: {},
|
|
1548
|
-
age: {},
|
|
1549
|
-
birthdate: {},
|
|
1550
|
-
expiry_date: {},
|
|
1551
|
-
document_type: {},
|
|
1552
|
-
issuing_country: {},
|
|
1553
|
-
gender: {},
|
|
1554
|
-
nationality: {},
|
|
1555
|
-
firstname: {},
|
|
1556
|
-
lastname: {},
|
|
1557
|
-
fullname: {},
|
|
1558
|
-
document_number: {},
|
|
1559
|
-
outer: {},
|
|
1560
|
-
bind: {},
|
|
1561
|
-
}
|
|
1562
|
-
let isCorrect = true
|
|
1563
|
-
if (
|
|
1564
|
-
queryResult.nationality &&
|
|
1565
|
-
queryResult.nationality.out &&
|
|
1566
|
-
queryResult.nationality.out.result
|
|
1567
|
-
) {
|
|
1568
|
-
if (
|
|
1569
|
-
!queryResult.nationality.out.expected?.every((country) => countryList.includes(country))
|
|
1570
|
-
) {
|
|
1571
|
-
console.warn("Nationality exclusion list does not match the one from the query results")
|
|
1572
|
-
isCorrect = false
|
|
1573
|
-
queryResultErrors.nationality.out = {
|
|
1574
|
-
expected: queryResult.nationality.out.expected,
|
|
1575
|
-
received: countryList,
|
|
1576
|
-
message: "Nationality exclusion list does not match the one from the query results",
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
} else if (!queryResult.nationality || !queryResult.nationality.out) {
|
|
1580
|
-
console.warn("Nationality exclusion is not set in the query result")
|
|
1581
|
-
isCorrect = false
|
|
1582
|
-
queryResultErrors.nationality.out = {
|
|
1583
|
-
message: "Nationality exclusion is not set in the query result",
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
// Check the countryList is in ascending order
|
|
1587
|
-
// If the prover doesn't use a sorted list then the proof cannot be trusted
|
|
1588
|
-
// as it is requirement in the circuit for the exclusion check to work
|
|
1589
|
-
for (let i = 1; i < countryList.length; i++) {
|
|
1590
|
-
if (countryList[i] < countryList[i - 1]) {
|
|
1591
|
-
console.warn(
|
|
1592
|
-
"The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
|
|
1593
|
-
)
|
|
1594
|
-
isCorrect = false
|
|
1595
|
-
queryResultErrors.nationality.out = {
|
|
1596
|
-
message:
|
|
1597
|
-
"The nationality exclusion list has not been sorted, and thus the proof cannot be trusted",
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
return { isCorrect, queryResultErrors }
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
private checkIssuingCountryExclusionPublicInputs(
|
|
1605
|
-
queryResult: QueryResult,
|
|
1606
|
-
countryList: string[],
|
|
1607
|
-
) {
|
|
1608
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1609
|
-
sig_check_dsc: {},
|
|
1610
|
-
sig_check_id_data: {},
|
|
1611
|
-
data_check_integrity: {},
|
|
1612
|
-
disclose: {},
|
|
1613
|
-
age: {},
|
|
1614
|
-
birthdate: {},
|
|
1615
|
-
expiry_date: {},
|
|
1616
|
-
document_type: {},
|
|
1617
|
-
issuing_country: {},
|
|
1618
|
-
gender: {},
|
|
1619
|
-
nationality: {},
|
|
1620
|
-
firstname: {},
|
|
1621
|
-
lastname: {},
|
|
1622
|
-
fullname: {},
|
|
1623
|
-
document_number: {},
|
|
1624
|
-
outer: {},
|
|
1625
|
-
bind: {},
|
|
1626
|
-
}
|
|
1627
|
-
let isCorrect = true
|
|
1628
|
-
|
|
1629
|
-
if (
|
|
1630
|
-
queryResult.issuing_country &&
|
|
1631
|
-
queryResult.issuing_country.out &&
|
|
1632
|
-
queryResult.issuing_country.out.result
|
|
1633
|
-
) {
|
|
1634
|
-
if (
|
|
1635
|
-
!queryResult.issuing_country.out.expected?.every((country) => countryList.includes(country))
|
|
1636
|
-
) {
|
|
1637
|
-
console.warn("Issuing country exclusion list does not match the one from the query results")
|
|
1638
|
-
isCorrect = false
|
|
1639
|
-
queryResultErrors.issuing_country.out = {
|
|
1640
|
-
expected: queryResult.issuing_country.out.expected,
|
|
1641
|
-
received: countryList,
|
|
1642
|
-
message: "Issuing country exclusion list does not match the one from the query results",
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
} else if (!queryResult.issuing_country || !queryResult.issuing_country.out) {
|
|
1646
|
-
console.warn("Issuing country exclusion is not set in the query result")
|
|
1647
|
-
isCorrect = false
|
|
1648
|
-
queryResultErrors.issuing_country.out = {
|
|
1649
|
-
message: "Issuing country exclusion is not set in the query result",
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
// Check the countryList is in ascending order
|
|
1653
|
-
// If the prover doesn't use a sorted list then the proof cannot be trusted
|
|
1654
|
-
// as it is requirement in the circuit for the exclusion check to work
|
|
1655
|
-
for (let i = 1; i < countryList.length; i++) {
|
|
1656
|
-
if (countryList[i] < countryList[i - 1]) {
|
|
1657
|
-
console.warn(
|
|
1658
|
-
"The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
|
|
1659
|
-
)
|
|
1660
|
-
isCorrect = false
|
|
1661
|
-
queryResultErrors.issuing_country.out = {
|
|
1662
|
-
message:
|
|
1663
|
-
"The issuing country exclusion list has not been sorted, and thus the proof cannot be trusted",
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
return { isCorrect, queryResultErrors }
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
private checkNationalityInclusionPublicInputs(queryResult: QueryResult, countryList: string[]) {
|
|
1671
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1672
|
-
sig_check_dsc: {},
|
|
1673
|
-
sig_check_id_data: {},
|
|
1674
|
-
data_check_integrity: {},
|
|
1675
|
-
disclose: {},
|
|
1676
|
-
age: {},
|
|
1677
|
-
birthdate: {},
|
|
1678
|
-
expiry_date: {},
|
|
1679
|
-
document_type: {},
|
|
1680
|
-
issuing_country: {},
|
|
1681
|
-
gender: {},
|
|
1682
|
-
nationality: {},
|
|
1683
|
-
firstname: {},
|
|
1684
|
-
lastname: {},
|
|
1685
|
-
fullname: {},
|
|
1686
|
-
document_number: {},
|
|
1687
|
-
outer: {},
|
|
1688
|
-
bind: {},
|
|
1689
|
-
}
|
|
1690
|
-
let isCorrect = true
|
|
1691
|
-
if (
|
|
1692
|
-
queryResult.nationality &&
|
|
1693
|
-
queryResult.nationality.in &&
|
|
1694
|
-
queryResult.nationality.in.result
|
|
1695
|
-
) {
|
|
1696
|
-
if (!queryResult.nationality.in.expected?.every((country) => countryList.includes(country))) {
|
|
1697
|
-
console.warn("Nationality inclusion list does not match the one from the query results")
|
|
1698
|
-
isCorrect = false
|
|
1699
|
-
queryResultErrors.nationality.in = {
|
|
1700
|
-
expected: queryResult.nationality.in.expected,
|
|
1701
|
-
received: countryList,
|
|
1702
|
-
message: "Nationality inclusion list does not match the one from the query results",
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
} else if (!queryResult.nationality || !queryResult.nationality.in) {
|
|
1706
|
-
console.warn("Nationality inclusion is not set in the query result")
|
|
1707
|
-
isCorrect = false
|
|
1708
|
-
queryResultErrors.nationality.in = {
|
|
1709
|
-
message: "Nationality inclusion is not set in the query result",
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
return { isCorrect, queryResultErrors }
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
private checkIssuingCountryInclusionPublicInputs(
|
|
1716
|
-
queryResult: QueryResult,
|
|
1717
|
-
countryList: string[],
|
|
1718
|
-
) {
|
|
1719
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1720
|
-
sig_check_dsc: {},
|
|
1721
|
-
sig_check_id_data: {},
|
|
1722
|
-
data_check_integrity: {},
|
|
1723
|
-
disclose: {},
|
|
1724
|
-
age: {},
|
|
1725
|
-
birthdate: {},
|
|
1726
|
-
expiry_date: {},
|
|
1727
|
-
document_type: {},
|
|
1728
|
-
issuing_country: {},
|
|
1729
|
-
gender: {},
|
|
1730
|
-
nationality: {},
|
|
1731
|
-
firstname: {},
|
|
1732
|
-
lastname: {},
|
|
1733
|
-
fullname: {},
|
|
1734
|
-
document_number: {},
|
|
1735
|
-
outer: {},
|
|
1736
|
-
bind: {},
|
|
1737
|
-
}
|
|
1738
|
-
let isCorrect = true
|
|
1739
|
-
|
|
1740
|
-
if (
|
|
1741
|
-
queryResult.issuing_country &&
|
|
1742
|
-
queryResult.issuing_country.in &&
|
|
1743
|
-
queryResult.issuing_country.in.result
|
|
1744
|
-
) {
|
|
1745
|
-
if (
|
|
1746
|
-
!queryResult.issuing_country.in.expected?.every((country) => countryList.includes(country))
|
|
1747
|
-
) {
|
|
1748
|
-
console.warn("Issuing country inclusion list does not match the one from the query results")
|
|
1749
|
-
isCorrect = false
|
|
1750
|
-
queryResultErrors.issuing_country.in = {
|
|
1751
|
-
expected: queryResult.issuing_country.in.expected,
|
|
1752
|
-
received: countryList,
|
|
1753
|
-
message: "Issuing country inclusion list does not match the one from the query results",
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
} else if (!queryResult.issuing_country || !queryResult.issuing_country.in) {
|
|
1757
|
-
console.warn("Issuing country inclusion is not set in the query result")
|
|
1758
|
-
isCorrect = false
|
|
1759
|
-
queryResultErrors.issuing_country.in = {
|
|
1760
|
-
message: "Issuing country inclusion is not set in the query result",
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
return { isCorrect, queryResultErrors }
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
private checkScopeFromDisclosureProof(
|
|
1767
|
-
proofData: ProofData,
|
|
1768
|
-
queryResultErrors: QueryResultErrors,
|
|
1769
|
-
key: string,
|
|
1770
|
-
scope?: string,
|
|
1771
|
-
chainId?: number,
|
|
1772
|
-
) {
|
|
1773
|
-
let isCorrect = true
|
|
1774
|
-
if (
|
|
1775
|
-
this.domain &&
|
|
1776
|
-
getServiceScopeHash(this.domain, chainId) !== BigInt(proofData.publicInputs[1])
|
|
1777
|
-
) {
|
|
1778
|
-
console.warn("The proof comes from a different domain than the one expected")
|
|
1779
|
-
isCorrect = false
|
|
1780
|
-
queryResultErrors[key as keyof QueryResultErrors].scope = {
|
|
1781
|
-
expected: `Scope: ${getServiceScopeHash(this.domain, chainId).toString()}`,
|
|
1782
|
-
received: `Scope: ${BigInt(proofData.publicInputs[1]).toString()}`,
|
|
1783
|
-
message: "The proof comes from a different domain than the one expected",
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
if (scope && getScopeHash(scope) !== BigInt(proofData.publicInputs[2])) {
|
|
1787
|
-
console.warn("The proof uses a different scope than the one expected")
|
|
1788
|
-
isCorrect = false
|
|
1789
|
-
queryResultErrors[key as keyof QueryResultErrors].scope = {
|
|
1790
|
-
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
1791
|
-
received: `Scope: ${BigInt(proofData.publicInputs[2]).toString()}`,
|
|
1792
|
-
message: "The proof uses a different scope than the one expected",
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
return { isCorrect, queryResultErrors }
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
private async checkCertificateRegistryRoot(
|
|
1799
|
-
root: string,
|
|
1800
|
-
queryResultErrors: any,
|
|
1801
|
-
outer?: boolean,
|
|
1802
|
-
) {
|
|
1803
|
-
let isCorrect = true
|
|
1804
|
-
try {
|
|
1805
|
-
// Maintained certificate registry settled onchain
|
|
1806
|
-
// Here we use Ethereum Sepolia
|
|
1807
|
-
const registryClient = new RegistryClient({ chainId: 11155111 })
|
|
1808
|
-
const isValid = await registryClient.isCertificateRootValid(root)
|
|
1809
|
-
if (!isValid) {
|
|
1810
|
-
console.warn("The ID was signed by an unrecognized root certificate")
|
|
1811
|
-
isCorrect = false
|
|
1812
|
-
queryResultErrors[outer ? "outer" : "sig_check_dsc"].certificate = {
|
|
1813
|
-
expected: `A valid root from ZKPassport Registry`,
|
|
1814
|
-
received: `Got invalid certificate registry root: ${root}`,
|
|
1815
|
-
message: "The ID was signed by an unrecognized root certificate",
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
} catch (error) {
|
|
1819
|
-
console.warn(error)
|
|
1820
|
-
console.warn("The ID was signed by an unrecognized root certificate")
|
|
1821
|
-
isCorrect = false
|
|
1822
|
-
queryResultErrors[outer ? "outer" : "sig_check_dsc"].certificate = {
|
|
1823
|
-
expected: `A valid root from ZKPassport Registry`,
|
|
1824
|
-
received: `Got invalid certificate registry root: ${root}`,
|
|
1825
|
-
message: "The ID was signed by an unrecognized root certificate",
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
return { isCorrect, queryResultErrors }
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
private async checkCircuitRegistryRoot(root: string, queryResultErrors: any) {
|
|
1832
|
-
let isCorrect = true
|
|
1833
|
-
try {
|
|
1834
|
-
const registryClient = new RegistryClient({ chainId: 11155111 })
|
|
1835
|
-
const isValid = await registryClient.isCircuitRootValid(root)
|
|
1836
|
-
if (!isValid) {
|
|
1837
|
-
console.warn("The proof uses unrecognized circuits")
|
|
1838
|
-
isCorrect = false
|
|
1839
|
-
queryResultErrors.outer.circuit = {
|
|
1840
|
-
expected: `A valid circuit from ZKPassport Registry`,
|
|
1841
|
-
received: `Got invalid circuit registry root: ${root}`,
|
|
1842
|
-
message: "The proof uses an unrecognized circuit",
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
} catch (error) {
|
|
1846
|
-
console.warn(error)
|
|
1847
|
-
console.warn("The proof uses unrecognized circuits")
|
|
1848
|
-
isCorrect = false
|
|
1849
|
-
queryResultErrors.outer.circuit = {
|
|
1850
|
-
expected: `A valid circuit from ZKPassport Registry`,
|
|
1851
|
-
received: `Got invalid circuit registry root: ${root}`,
|
|
1852
|
-
message: "The proof uses an unrecognized circuit",
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
return { isCorrect, queryResultErrors }
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
private checkBindPublicInputs(queryResult: QueryResult, boundData: BoundData) {
|
|
1859
|
-
const queryResultErrors: QueryResultErrors = {
|
|
1860
|
-
sig_check_dsc: {},
|
|
1861
|
-
sig_check_id_data: {},
|
|
1862
|
-
data_check_integrity: {},
|
|
1863
|
-
disclose: {},
|
|
1864
|
-
age: {},
|
|
1865
|
-
birthdate: {},
|
|
1866
|
-
expiry_date: {},
|
|
1867
|
-
document_type: {},
|
|
1868
|
-
issuing_country: {},
|
|
1869
|
-
gender: {},
|
|
1870
|
-
nationality: {},
|
|
1871
|
-
firstname: {},
|
|
1872
|
-
lastname: {},
|
|
1873
|
-
fullname: {},
|
|
1874
|
-
document_number: {},
|
|
1875
|
-
outer: {},
|
|
1876
|
-
bind: {},
|
|
1877
|
-
}
|
|
1878
|
-
let isCorrect = true
|
|
1879
|
-
|
|
1880
|
-
if (queryResult.bind) {
|
|
1881
|
-
if (
|
|
1882
|
-
queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
|
|
1883
|
-
boundData.user_address?.toLowerCase().replace("0x", "")
|
|
1884
|
-
) {
|
|
1885
|
-
console.warn("Bound user address does not match the one from the query results")
|
|
1886
|
-
isCorrect = false
|
|
1887
|
-
queryResultErrors.bind.eq = {
|
|
1888
|
-
expected: queryResult.bind.user_address,
|
|
1889
|
-
received: boundData.user_address,
|
|
1890
|
-
message: "Bound user address does not match the one from the query results",
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
if (
|
|
1894
|
-
queryResult.bind.custom_data?.trim().toLowerCase() !==
|
|
1895
|
-
boundData.custom_data?.trim().toLowerCase()
|
|
1896
|
-
) {
|
|
1897
|
-
console.warn("Bound custom data does not match the one from the query results")
|
|
1898
|
-
isCorrect = false
|
|
1899
|
-
queryResultErrors.bind.eq = {
|
|
1900
|
-
expected: queryResult.bind.custom_data,
|
|
1901
|
-
received: boundData.custom_data,
|
|
1902
|
-
message: "Bound custom data does not match the one from the query results",
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
return { isCorrect, queryResultErrors }
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
private async checkPublicInputs(
|
|
1910
|
-
proofs: Array<ProofResult>,
|
|
1911
|
-
queryResult: QueryResult,
|
|
1912
|
-
validity?: number,
|
|
1913
|
-
scope?: string,
|
|
1914
|
-
chainId?: number,
|
|
1915
|
-
) {
|
|
1916
|
-
let commitmentIn: bigint | undefined
|
|
1917
|
-
let commitmentOut: bigint | undefined
|
|
1918
|
-
let isCorrect = true
|
|
1919
|
-
let uniqueIdentifier: string | undefined
|
|
1920
|
-
const currentTime = new Date()
|
|
1921
|
-
const today = new Date(
|
|
1922
|
-
currentTime.getFullYear(),
|
|
1923
|
-
currentTime.getMonth(),
|
|
1924
|
-
currentTime.getDate(),
|
|
1925
|
-
0,
|
|
1926
|
-
0,
|
|
1927
|
-
0,
|
|
1928
|
-
0,
|
|
1929
|
-
)
|
|
1930
|
-
let queryResultErrors: QueryResultErrors = {
|
|
1931
|
-
sig_check_dsc: {},
|
|
1932
|
-
sig_check_id_data: {},
|
|
1933
|
-
data_check_integrity: {},
|
|
1934
|
-
disclose: {},
|
|
1935
|
-
age: {},
|
|
1936
|
-
birthdate: {},
|
|
1937
|
-
expiry_date: {},
|
|
1938
|
-
document_type: {},
|
|
1939
|
-
issuing_country: {},
|
|
1940
|
-
gender: {},
|
|
1941
|
-
nationality: {},
|
|
1942
|
-
firstname: {},
|
|
1943
|
-
lastname: {},
|
|
1944
|
-
fullname: {},
|
|
1945
|
-
document_number: {},
|
|
1946
|
-
outer: {},
|
|
1947
|
-
bind: {},
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
// Since the order is important for the commitments, we need to sort the proofs
|
|
1951
|
-
// by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
|
|
1952
|
-
const sortedProofs = proofs.sort((a, b) => {
|
|
1953
|
-
const proofOrder = [
|
|
1954
|
-
"sig_check_dsc",
|
|
1955
|
-
"sig_check_id_data",
|
|
1956
|
-
"data_check_integrity",
|
|
1957
|
-
"disclose_bytes",
|
|
1958
|
-
"compare_age",
|
|
1959
|
-
"compare_birthdate",
|
|
1960
|
-
"compare_expiry",
|
|
1961
|
-
"exclusion_check_nationality",
|
|
1962
|
-
"inclusion_check_nationality",
|
|
1963
|
-
"exclusion_check_issuing_country",
|
|
1964
|
-
"inclusion_check_issuing_country",
|
|
1965
|
-
"bind",
|
|
1966
|
-
]
|
|
1967
|
-
const getIndex = (proof: ProofResult) => {
|
|
1968
|
-
const name = proof.name || ""
|
|
1969
|
-
return proofOrder.findIndex((p) => name.startsWith(p))
|
|
1970
|
-
}
|
|
1971
|
-
return getIndex(a) - getIndex(b)
|
|
1972
|
-
})
|
|
1973
|
-
|
|
1974
|
-
for (const proof of sortedProofs!) {
|
|
1975
|
-
const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
|
|
1976
|
-
if (proof.name?.startsWith("outer")) {
|
|
1977
|
-
const isForEVM = proof.name?.startsWith("outer_evm")
|
|
1978
|
-
const certificateRegistryRoot = getCertificateRegistryRootFromOuterProof(proofData)
|
|
1979
|
-
const {
|
|
1980
|
-
isCorrect: isCorrectCertificateRegistryRoot,
|
|
1981
|
-
queryResultErrors: queryResultErrorsCertificateRegistryRoot,
|
|
1982
|
-
} = await this.checkCertificateRegistryRoot(
|
|
1983
|
-
certificateRegistryRoot.toString(16),
|
|
1984
|
-
queryResultErrors,
|
|
1985
|
-
true,
|
|
1986
|
-
)
|
|
1987
|
-
isCorrect = isCorrect && isCorrectCertificateRegistryRoot
|
|
1988
|
-
queryResultErrors = {
|
|
1989
|
-
...queryResultErrors,
|
|
1990
|
-
...queryResultErrorsCertificateRegistryRoot,
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
const circuitRegistryRoot = getCircuitRegistryRootFromOuterProof(proofData)
|
|
1994
|
-
const {
|
|
1995
|
-
isCorrect: isCorrectCircuitRegistryRoot,
|
|
1996
|
-
queryResultErrors: queryResultErrorsCircuitRegistryRoot,
|
|
1997
|
-
} = await this.checkCircuitRegistryRoot(circuitRegistryRoot.toString(16), queryResultErrors)
|
|
1998
|
-
isCorrect = isCorrect && isCorrectCircuitRegistryRoot
|
|
1999
|
-
queryResultErrors = {
|
|
2000
|
-
...queryResultErrors,
|
|
2001
|
-
...queryResultErrorsCircuitRegistryRoot,
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
const currentDate = getCurrentDateFromOuterProof(proofData)
|
|
2005
|
-
const todayToCurrentDate = today.getTime() - currentDate.getTime()
|
|
2006
|
-
const differenceInDays = validity ?? 180
|
|
2007
|
-
const expectedDifference = differenceInDays * 86400000
|
|
2008
|
-
const actualDifference = today.getTime() - (today.getTime() - expectedDifference)
|
|
2009
|
-
// The ID should not expire within the next 6 months (or whatever the custom value is)
|
|
2010
|
-
if (todayToCurrentDate >= actualDifference) {
|
|
2011
|
-
console.warn(
|
|
2012
|
-
`The date used to check the validity of the ID is older than ${differenceInDays} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
|
|
2013
|
-
)
|
|
2014
|
-
isCorrect = false
|
|
2015
|
-
queryResultErrors.outer.date = {
|
|
2016
|
-
expected: `Difference: ${differenceInDays} days`,
|
|
2017
|
-
received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
|
|
2018
|
-
message:
|
|
2019
|
-
"The date used to check the validity of the ID is older than the validity period",
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
const paramCommitments = getParamCommitmentsFromOuterProof(proofData)
|
|
2023
|
-
const committedInputs = proof.committedInputs
|
|
2024
|
-
const keysInCommittedInputs = Object.keys(committedInputs || {})
|
|
2025
|
-
if (keysInCommittedInputs.length !== paramCommitments.length) {
|
|
2026
|
-
console.warn("The proof does not verify all the requested conditions and information")
|
|
2027
|
-
isCorrect = false
|
|
2028
|
-
queryResultErrors.outer.commitment = {
|
|
2029
|
-
expected: `Number of parameter commitments: ${paramCommitments.length}`,
|
|
2030
|
-
received: `Number of disclosure proofs provided: ${keysInCommittedInputs.length}`,
|
|
2031
|
-
message: "The proof does not verify all the requested conditions and information",
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
if (
|
|
2035
|
-
this.domain &&
|
|
2036
|
-
getServiceScopeHash(this.domain, chainId) !== getScopeFromOuterProof(proofData)
|
|
2037
|
-
) {
|
|
2038
|
-
console.warn("The proof comes from a different domain than the one expected")
|
|
2039
|
-
isCorrect = false
|
|
2040
|
-
queryResultErrors.outer.scope = {
|
|
2041
|
-
expected: `Scope: ${getServiceScopeHash(this.domain, chainId).toString()}`,
|
|
2042
|
-
received: `Scope: ${getScopeFromOuterProof(proofData).toString()}`,
|
|
2043
|
-
message: "The proof comes from a different domain than the one expected",
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
if (scope && getScopeHash(scope) !== getSubscopeFromOuterProof(proofData)) {
|
|
2047
|
-
console.warn("The proof uses a different scope than the one expected")
|
|
2048
|
-
isCorrect = false
|
|
2049
|
-
queryResultErrors.outer.scope = {
|
|
2050
|
-
expected: `Scope: ${getScopeHash(scope).toString()}`,
|
|
2051
|
-
received: `Scope: ${getSubscopeFromOuterProof(proofData).toString()}`,
|
|
2052
|
-
message: "The proof uses a different scope than the one expected",
|
|
2053
|
-
}
|
|
2054
|
-
}
|
|
2055
|
-
if (!!committedInputs?.compare_age) {
|
|
2056
|
-
const ageCommittedInputs = committedInputs?.compare_age as AgeCommittedInputs
|
|
2057
|
-
const ageParameterCommitment = isForEVM
|
|
2058
|
-
? await getAgeEVMParameterCommitment(
|
|
2059
|
-
ageCommittedInputs.currentDate,
|
|
2060
|
-
ageCommittedInputs.minAge,
|
|
2061
|
-
ageCommittedInputs.maxAge,
|
|
2062
|
-
)
|
|
2063
|
-
: await getAgeParameterCommitment(
|
|
2064
|
-
ageCommittedInputs.currentDate,
|
|
2065
|
-
ageCommittedInputs.minAge,
|
|
2066
|
-
ageCommittedInputs.maxAge,
|
|
2067
|
-
)
|
|
2068
|
-
if (!paramCommitments.includes(ageParameterCommitment)) {
|
|
2069
|
-
console.warn("This proof does not verify the age")
|
|
2070
|
-
isCorrect = false
|
|
2071
|
-
queryResultErrors.age.commitment = {
|
|
2072
|
-
expected: `Age parameter commitment: ${ageParameterCommitment.toString()}`,
|
|
2073
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2074
|
-
message: "This proof does not verify the age",
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
|
|
2078
|
-
this.checkAgePublicInputs(proof, queryResult)
|
|
2079
|
-
isCorrect = isCorrect && isCorrectAge
|
|
2080
|
-
queryResultErrors = {
|
|
2081
|
-
...queryResultErrors,
|
|
2082
|
-
...queryResultErrorsAge,
|
|
2083
|
-
}
|
|
2084
|
-
} else if (!!committedInputs?.compare_birthdate) {
|
|
2085
|
-
const birthdateCommittedInputs = committedInputs?.compare_birthdate as DateCommittedInputs
|
|
2086
|
-
const birthdateParameterCommitment = isForEVM
|
|
2087
|
-
? await getDateEVMParameterCommitment(
|
|
2088
|
-
ProofType.BIRTHDATE,
|
|
2089
|
-
birthdateCommittedInputs.currentDate,
|
|
2090
|
-
birthdateCommittedInputs.minDate,
|
|
2091
|
-
birthdateCommittedInputs.maxDate,
|
|
2092
|
-
)
|
|
2093
|
-
: await getDateParameterCommitment(
|
|
2094
|
-
ProofType.BIRTHDATE,
|
|
2095
|
-
birthdateCommittedInputs.currentDate,
|
|
2096
|
-
birthdateCommittedInputs.minDate,
|
|
2097
|
-
birthdateCommittedInputs.maxDate,
|
|
2098
|
-
)
|
|
2099
|
-
if (!paramCommitments.includes(birthdateParameterCommitment)) {
|
|
2100
|
-
console.warn("This proof does not verify the birthdate")
|
|
2101
|
-
isCorrect = false
|
|
2102
|
-
queryResultErrors.birthdate.commitment = {
|
|
2103
|
-
expected: `Birthdate parameter commitment: ${birthdateParameterCommitment.toString()}`,
|
|
2104
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2105
|
-
message: "This proof does not verify the birthdate",
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
|
|
2109
|
-
this.checkBirthdatePublicInputs(proof, queryResult)
|
|
2110
|
-
isCorrect = isCorrect && isCorrectBirthdate
|
|
2111
|
-
queryResultErrors = {
|
|
2112
|
-
...queryResultErrors,
|
|
2113
|
-
...queryResultErrorsBirthdate,
|
|
2114
|
-
}
|
|
2115
|
-
} else if (!!committedInputs?.compare_expiry) {
|
|
2116
|
-
const expiryCommittedInputs = committedInputs?.compare_expiry as DateCommittedInputs
|
|
2117
|
-
const expiryParameterCommitment = isForEVM
|
|
2118
|
-
? await getDateEVMParameterCommitment(
|
|
2119
|
-
ProofType.EXPIRY_DATE,
|
|
2120
|
-
expiryCommittedInputs.currentDate,
|
|
2121
|
-
expiryCommittedInputs.minDate,
|
|
2122
|
-
expiryCommittedInputs.maxDate,
|
|
2123
|
-
)
|
|
2124
|
-
: await getDateParameterCommitment(
|
|
2125
|
-
ProofType.EXPIRY_DATE,
|
|
2126
|
-
expiryCommittedInputs.currentDate,
|
|
2127
|
-
expiryCommittedInputs.minDate,
|
|
2128
|
-
expiryCommittedInputs.maxDate,
|
|
2129
|
-
)
|
|
2130
|
-
if (!paramCommitments.includes(expiryParameterCommitment)) {
|
|
2131
|
-
console.warn("This proof does not verify the expiry date")
|
|
2132
|
-
isCorrect = false
|
|
2133
|
-
queryResultErrors.expiry_date.commitment = {
|
|
2134
|
-
expected: `Expiry date parameter commitment: ${expiryParameterCommitment.toString()}`,
|
|
2135
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2136
|
-
message: "This proof does not verify the expiry date",
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
|
|
2140
|
-
this.checkExpiryDatePublicInputs(proof, queryResult)
|
|
2141
|
-
isCorrect = isCorrect && isCorrectExpiryDate
|
|
2142
|
-
queryResultErrors = {
|
|
2143
|
-
...queryResultErrors,
|
|
2144
|
-
...queryResultErrorsExpiryDate,
|
|
2145
|
-
}
|
|
2146
|
-
} else if (!!committedInputs?.disclose_bytes) {
|
|
2147
|
-
const discloseCommittedInputs = committedInputs?.disclose_bytes as DiscloseCommittedInputs
|
|
2148
|
-
const discloseParameterCommitment = isForEVM
|
|
2149
|
-
? await getDiscloseEVMParameterCommitment(
|
|
2150
|
-
discloseCommittedInputs.discloseMask,
|
|
2151
|
-
discloseCommittedInputs.disclosedBytes,
|
|
2152
|
-
)
|
|
2153
|
-
: await getDiscloseParameterCommitment(
|
|
2154
|
-
discloseCommittedInputs.discloseMask,
|
|
2155
|
-
discloseCommittedInputs.disclosedBytes,
|
|
2156
|
-
)
|
|
2157
|
-
if (!paramCommitments.includes(discloseParameterCommitment)) {
|
|
2158
|
-
console.warn("This proof does not verify any of the data disclosed")
|
|
2159
|
-
isCorrect = false
|
|
2160
|
-
queryResultErrors.disclose.commitment = {
|
|
2161
|
-
expected: `Disclosure parameter commitment: ${discloseParameterCommitment.toString()}`,
|
|
2162
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2163
|
-
message: "This proof does not verify any of the data disclosed",
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
|
|
2167
|
-
this.checkDiscloseBytesPublicInputs(proof, queryResult)
|
|
2168
|
-
isCorrect = isCorrect && isCorrectDisclose
|
|
2169
|
-
queryResultErrors = {
|
|
2170
|
-
...queryResultErrors,
|
|
2171
|
-
...queryResultErrorsDisclose,
|
|
2172
|
-
}
|
|
2173
|
-
} else if (!!committedInputs?.inclusion_check_nationality) {
|
|
2174
|
-
const inclusionCheckNationalityCommittedInputs =
|
|
2175
|
-
committedInputs?.inclusion_check_nationality as CountryCommittedInputs
|
|
2176
|
-
const inclusionCheckNationalityParameterCommitment = isForEVM
|
|
2177
|
-
? await getCountryEVMParameterCommitment(
|
|
2178
|
-
ProofType.NATIONALITY_INCLUSION,
|
|
2179
|
-
inclusionCheckNationalityCommittedInputs.countries,
|
|
2180
|
-
)
|
|
2181
|
-
: await getCountryParameterCommitment(
|
|
2182
|
-
ProofType.NATIONALITY_INCLUSION,
|
|
2183
|
-
inclusionCheckNationalityCommittedInputs.countries,
|
|
2184
|
-
)
|
|
2185
|
-
if (!paramCommitments.includes(inclusionCheckNationalityParameterCommitment)) {
|
|
2186
|
-
console.warn("This proof does not verify the inclusion of the nationality")
|
|
2187
|
-
isCorrect = false
|
|
2188
|
-
queryResultErrors.nationality.commitment = {
|
|
2189
|
-
expected: `Nationality parameter commitment: ${inclusionCheckNationalityParameterCommitment.toString()}`,
|
|
2190
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2191
|
-
message: "This proof does not verify the inclusion of the nationality",
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
const countryList = inclusionCheckNationalityCommittedInputs.countries
|
|
2195
|
-
const {
|
|
2196
|
-
isCorrect: isCorrectNationalityInclusion,
|
|
2197
|
-
queryResultErrors: queryResultErrorsNationalityInclusion,
|
|
2198
|
-
} = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
|
|
2199
|
-
isCorrect = isCorrect && isCorrectNationalityInclusion
|
|
2200
|
-
queryResultErrors = {
|
|
2201
|
-
...queryResultErrors,
|
|
2202
|
-
...queryResultErrorsNationalityInclusion,
|
|
2203
|
-
}
|
|
2204
|
-
} else if (!!committedInputs?.inclusion_check_issuing_country) {
|
|
2205
|
-
const inclusionCheckIssuingCountryCommittedInputs =
|
|
2206
|
-
committedInputs?.inclusion_check_issuing_country as CountryCommittedInputs
|
|
2207
|
-
const inclusionCheckIssuingCountryParameterCommitment = isForEVM
|
|
2208
|
-
? await getCountryEVMParameterCommitment(
|
|
2209
|
-
ProofType.ISSUING_COUNTRY_INCLUSION,
|
|
2210
|
-
inclusionCheckIssuingCountryCommittedInputs.countries,
|
|
2211
|
-
)
|
|
2212
|
-
: await getCountryParameterCommitment(
|
|
2213
|
-
ProofType.ISSUING_COUNTRY_INCLUSION,
|
|
2214
|
-
inclusionCheckIssuingCountryCommittedInputs.countries,
|
|
2215
|
-
)
|
|
2216
|
-
if (!paramCommitments.includes(inclusionCheckIssuingCountryParameterCommitment)) {
|
|
2217
|
-
console.warn("This proof does not verify the inclusion of the issuing country")
|
|
2218
|
-
isCorrect = false
|
|
2219
|
-
queryResultErrors.issuing_country.commitment = {
|
|
2220
|
-
expected: `Issuing country parameter commitment: ${inclusionCheckIssuingCountryParameterCommitment.toString()}`,
|
|
2221
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2222
|
-
message: "This proof does not verify the inclusion of the issuing country",
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
const countryList = inclusionCheckIssuingCountryCommittedInputs.countries
|
|
2226
|
-
const {
|
|
2227
|
-
isCorrect: isCorrectIssuingCountryInclusion,
|
|
2228
|
-
queryResultErrors: queryResultErrorsIssuingCountryInclusion,
|
|
2229
|
-
} = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
|
|
2230
|
-
isCorrect = isCorrect && isCorrectIssuingCountryInclusion
|
|
2231
|
-
queryResultErrors = {
|
|
2232
|
-
...queryResultErrors,
|
|
2233
|
-
...queryResultErrorsIssuingCountryInclusion,
|
|
2234
|
-
}
|
|
2235
|
-
} else if (!!committedInputs?.exclusion_check_nationality) {
|
|
2236
|
-
const exclusionCheckNationalityCommittedInputs =
|
|
2237
|
-
committedInputs?.exclusion_check_nationality as CountryCommittedInputs
|
|
2238
|
-
const exclusionCheckNationalityParameterCommitment = isForEVM
|
|
2239
|
-
? await getCountryEVMParameterCommitment(
|
|
2240
|
-
ProofType.NATIONALITY_EXCLUSION,
|
|
2241
|
-
exclusionCheckNationalityCommittedInputs.countries,
|
|
2242
|
-
)
|
|
2243
|
-
: await getCountryParameterCommitment(
|
|
2244
|
-
ProofType.NATIONALITY_EXCLUSION,
|
|
2245
|
-
exclusionCheckNationalityCommittedInputs.countries,
|
|
2246
|
-
)
|
|
2247
|
-
if (!paramCommitments.includes(exclusionCheckNationalityParameterCommitment)) {
|
|
2248
|
-
console.warn("This proof does not verify the exclusion of the nationality")
|
|
2249
|
-
isCorrect = false
|
|
2250
|
-
queryResultErrors.nationality.commitment = {
|
|
2251
|
-
expected: `Nationality parameter commitment: ${exclusionCheckNationalityParameterCommitment.toString()}`,
|
|
2252
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2253
|
-
message: "This proof does not verify the exclusion of the nationality",
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
const countryList = exclusionCheckNationalityCommittedInputs.countries
|
|
2257
|
-
const {
|
|
2258
|
-
isCorrect: isCorrectNationalityExclusion,
|
|
2259
|
-
queryResultErrors: queryResultErrorsNationalityExclusion,
|
|
2260
|
-
} = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
|
|
2261
|
-
isCorrect = isCorrect && isCorrectNationalityExclusion
|
|
2262
|
-
queryResultErrors = {
|
|
2263
|
-
...queryResultErrors,
|
|
2264
|
-
...queryResultErrorsNationalityExclusion,
|
|
2265
|
-
}
|
|
2266
|
-
} else if (!!committedInputs?.exclusion_check_issuing_country) {
|
|
2267
|
-
const exclusionCheckIssuingCountryCommittedInputs =
|
|
2268
|
-
committedInputs?.exclusion_check_issuing_country as CountryCommittedInputs
|
|
2269
|
-
const exclusionCheckIssuingCountryParameterCommitment = isForEVM
|
|
2270
|
-
? await getCountryEVMParameterCommitment(
|
|
2271
|
-
ProofType.ISSUING_COUNTRY_EXCLUSION,
|
|
2272
|
-
exclusionCheckIssuingCountryCommittedInputs.countries,
|
|
2273
|
-
)
|
|
2274
|
-
: await getCountryParameterCommitment(
|
|
2275
|
-
ProofType.ISSUING_COUNTRY_EXCLUSION,
|
|
2276
|
-
exclusionCheckIssuingCountryCommittedInputs.countries,
|
|
2277
|
-
)
|
|
2278
|
-
if (!paramCommitments.includes(exclusionCheckIssuingCountryParameterCommitment)) {
|
|
2279
|
-
console.warn("This proof does not verify the exclusion of the issuing country")
|
|
2280
|
-
isCorrect = false
|
|
2281
|
-
queryResultErrors.issuing_country.commitment = {
|
|
2282
|
-
expected: `Issuing country parameter commitment: ${exclusionCheckIssuingCountryParameterCommitment.toString()}`,
|
|
2283
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2284
|
-
message: "This proof does not verify the exclusion of the issuing country",
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
const countryList = exclusionCheckIssuingCountryCommittedInputs.countries
|
|
2288
|
-
const {
|
|
2289
|
-
isCorrect: isCorrectIssuingCountryExclusion,
|
|
2290
|
-
queryResultErrors: queryResultErrorsIssuingCountryExclusion,
|
|
2291
|
-
} = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
|
|
2292
|
-
isCorrect = isCorrect && isCorrectIssuingCountryExclusion
|
|
2293
|
-
queryResultErrors = {
|
|
2294
|
-
...queryResultErrors,
|
|
2295
|
-
...queryResultErrorsIssuingCountryExclusion,
|
|
2296
|
-
}
|
|
2297
|
-
} else if (!!committedInputs?.bind) {
|
|
2298
|
-
const bindCommittedInputs = committedInputs?.bind as BindCommittedInputs
|
|
2299
|
-
const bindParameterCommitment = isForEVM
|
|
2300
|
-
? await getBindEVMParameterCommitment(formatBoundData(bindCommittedInputs.data))
|
|
2301
|
-
: await getBindParameterCommitment(formatBoundData(bindCommittedInputs.data))
|
|
2302
|
-
if (!paramCommitments.includes(bindParameterCommitment)) {
|
|
2303
|
-
console.warn("This proof does not verify the bound data")
|
|
2304
|
-
isCorrect = false
|
|
2305
|
-
queryResultErrors.bind.commitment = {
|
|
2306
|
-
expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
|
|
2307
|
-
received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
|
|
2308
|
-
message: "This proof does not verify the bound data",
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
|
|
2312
|
-
this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
|
|
2313
|
-
isCorrect = isCorrect && isCorrectBind
|
|
2314
|
-
queryResultErrors = {
|
|
2315
|
-
...queryResultErrors,
|
|
2316
|
-
...queryResultErrorsBind,
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
uniqueIdentifier = getNullifierFromOuterProof(proofData).toString(10)
|
|
2320
|
-
} else if (proof.name?.startsWith("sig_check_dsc")) {
|
|
2321
|
-
commitmentOut = getCommitmentFromDSCProof(proofData)
|
|
2322
|
-
const merkleRoot = getMerkleRootFromDSCProof(proofData)
|
|
2323
|
-
const {
|
|
2324
|
-
isCorrect: isCorrectCertificateRegistryRoot,
|
|
2325
|
-
queryResultErrors: queryResultErrorsCertificateRegistryRoot,
|
|
2326
|
-
} = await this.checkCertificateRegistryRoot(
|
|
2327
|
-
merkleRoot.toString(16),
|
|
2328
|
-
queryResultErrors,
|
|
2329
|
-
false,
|
|
2330
|
-
)
|
|
2331
|
-
isCorrect = isCorrect && isCorrectCertificateRegistryRoot
|
|
2332
|
-
queryResultErrors = {
|
|
2333
|
-
...queryResultErrors,
|
|
2334
|
-
...queryResultErrorsCertificateRegistryRoot,
|
|
2335
|
-
}
|
|
2336
|
-
} else if (proof.name?.startsWith("sig_check_id_data")) {
|
|
2337
|
-
commitmentIn = getCommitmentInFromIDDataProof(proofData)
|
|
2338
|
-
if (commitmentIn !== commitmentOut) {
|
|
2339
|
-
console.warn(
|
|
2340
|
-
"Failed to check the link between the certificate signature and ID signature",
|
|
2341
|
-
)
|
|
2342
|
-
isCorrect = false
|
|
2343
|
-
queryResultErrors.sig_check_id_data.commitment = {
|
|
2344
|
-
expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
|
|
2345
|
-
received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
|
|
2346
|
-
message: "Failed to check the link between the certificate signature and ID signature",
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2349
|
-
commitmentOut = getCommitmentOutFromIDDataProof(proofData)
|
|
2350
|
-
} else if (proof.name?.startsWith("data_check_integrity")) {
|
|
2351
|
-
commitmentIn = getCommitmentInFromIntegrityProof(proofData)
|
|
2352
|
-
if (commitmentIn !== commitmentOut) {
|
|
2353
|
-
console.warn("Failed to check the link between the ID signature and the data signed")
|
|
2354
|
-
isCorrect = false
|
|
2355
|
-
queryResultErrors.data_check_integrity.commitment = {
|
|
2356
|
-
expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
|
|
2357
|
-
received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
|
|
2358
|
-
message: "Failed to check the link between the ID signature and the data signed",
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
commitmentOut = getCommitmentOutFromIntegrityProof(proofData)
|
|
2362
|
-
const currentDate = getCurrentDateFromIntegrityProof(proofData)
|
|
2363
|
-
const todayToCurrentDate = today.getTime() - currentDate.getTime()
|
|
2364
|
-
const differenceInDays = validity ?? 180
|
|
2365
|
-
const expectedDifference = differenceInDays * 86400000
|
|
2366
|
-
const actualDifference = today.getTime() - (today.getTime() - expectedDifference)
|
|
2367
|
-
// The ID should not expire within the next 6 months (or whatever the custom value is)
|
|
2368
|
-
if (todayToCurrentDate >= actualDifference) {
|
|
2369
|
-
console.warn(
|
|
2370
|
-
`The date used to check the validity of the ID is older than ${differenceInDays} days. You can ask the user to rescan their ID or ask them to disclose their expiry date`,
|
|
2371
|
-
)
|
|
2372
|
-
isCorrect = false
|
|
2373
|
-
queryResultErrors.data_check_integrity.date = {
|
|
2374
|
-
expected: `Difference: ${differenceInDays} days`,
|
|
2375
|
-
received: `Difference: ${Math.round(todayToCurrentDate / 86400000)} days`,
|
|
2376
|
-
message:
|
|
2377
|
-
"The date used to check the validity of the ID is older than the validity period",
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
} else if (proof.name === "disclose_bytes") {
|
|
2381
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2382
|
-
if (commitmentIn !== commitmentOut) {
|
|
2383
|
-
console.warn(
|
|
2384
|
-
"Failed to check the link between the validity of the ID and the data to disclose",
|
|
2385
|
-
)
|
|
2386
|
-
isCorrect = false
|
|
2387
|
-
queryResultErrors.disclose.commitment = {
|
|
2388
|
-
expected: `Commitment: ${commitmentOut?.toString() || "undefined"}`,
|
|
2389
|
-
received: `Commitment: ${commitmentIn?.toString() || "undefined"}`,
|
|
2390
|
-
message:
|
|
2391
|
-
"Failed to check the link between the validity of the ID and the data to disclose",
|
|
2392
|
-
}
|
|
2393
|
-
}
|
|
2394
|
-
const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2395
|
-
const calculatedParamCommitment = await getDiscloseParameterCommitment(
|
|
2396
|
-
(proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).discloseMask!,
|
|
2397
|
-
(proof.committedInputs?.disclose_bytes as DiscloseCommittedInputs).disclosedBytes!,
|
|
2398
|
-
)
|
|
2399
|
-
if (paramCommitment !== calculatedParamCommitment) {
|
|
2400
|
-
console.warn("The disclosed data does not match the data committed by the proof")
|
|
2401
|
-
isCorrect = false
|
|
2402
|
-
queryResultErrors.disclose.commitment = {
|
|
2403
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2404
|
-
received: `Commitment: ${paramCommitment}`,
|
|
2405
|
-
message: "The disclosed data does not match the data committed by the proof",
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2409
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "disclose", scope)
|
|
2410
|
-
isCorrect = isCorrect && isCorrectScope
|
|
2411
|
-
queryResultErrors = {
|
|
2412
|
-
...queryResultErrors,
|
|
2413
|
-
...queryResultErrorsScope,
|
|
2414
|
-
}
|
|
2415
|
-
const { isCorrect: isCorrectDisclose, queryResultErrors: queryResultErrorsDisclose } =
|
|
2416
|
-
this.checkDiscloseBytesPublicInputs(proof, queryResult)
|
|
2417
|
-
isCorrect = isCorrect && isCorrectDisclose && isCorrectScope
|
|
2418
|
-
queryResultErrors = {
|
|
2419
|
-
...queryResultErrors,
|
|
2420
|
-
...queryResultErrorsDisclose,
|
|
2421
|
-
...queryResultErrorsScope,
|
|
2422
|
-
}
|
|
2423
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2424
|
-
} else if (proof.name === "compare_age") {
|
|
2425
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2426
|
-
if (commitmentIn !== commitmentOut) {
|
|
2427
|
-
console.warn(
|
|
2428
|
-
"Failed to check the link between the validity of the ID and the age derived from it",
|
|
2429
|
-
)
|
|
2430
|
-
isCorrect = false
|
|
2431
|
-
queryResultErrors.age.commitment = {
|
|
2432
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2433
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2434
|
-
message:
|
|
2435
|
-
"Failed to check the link between the validity of the ID and the age derived from it",
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2439
|
-
const committedInputs = proof.committedInputs?.compare_age as AgeCommittedInputs
|
|
2440
|
-
const calculatedParamCommitment = await getAgeParameterCommitment(
|
|
2441
|
-
committedInputs.currentDate,
|
|
2442
|
-
committedInputs.minAge,
|
|
2443
|
-
committedInputs.maxAge,
|
|
2444
|
-
)
|
|
2445
|
-
if (paramCommitment !== calculatedParamCommitment) {
|
|
2446
|
-
console.warn(
|
|
2447
|
-
"The conditions for the age check do not match the conditions checked by the proof",
|
|
2448
|
-
)
|
|
2449
|
-
isCorrect = false
|
|
2450
|
-
queryResultErrors.age.commitment = {
|
|
2451
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2452
|
-
received: `Commitment: ${paramCommitment}`,
|
|
2453
|
-
message:
|
|
2454
|
-
"The conditions for the age check do not match the conditions checked by the proof",
|
|
2455
|
-
}
|
|
2456
|
-
}
|
|
2457
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2458
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "age", scope)
|
|
2459
|
-
const { isCorrect: isCorrectAge, queryResultErrors: queryResultErrorsAge } =
|
|
2460
|
-
this.checkAgePublicInputs(proof, queryResult)
|
|
2461
|
-
isCorrect = isCorrect && isCorrectAge && isCorrectScope
|
|
2462
|
-
queryResultErrors = {
|
|
2463
|
-
...queryResultErrors,
|
|
2464
|
-
...queryResultErrorsAge,
|
|
2465
|
-
...queryResultErrorsScope,
|
|
2466
|
-
}
|
|
2467
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2468
|
-
} else if (proof.name === "compare_birthdate") {
|
|
2469
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2470
|
-
if (commitmentIn !== commitmentOut) {
|
|
2471
|
-
console.warn(
|
|
2472
|
-
"Failed to check the link between the validity of the ID and the birthdate derived from it",
|
|
2473
|
-
)
|
|
2474
|
-
isCorrect = false
|
|
2475
|
-
queryResultErrors.birthdate.commitment = {
|
|
2476
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2477
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2478
|
-
message:
|
|
2479
|
-
"Failed to check the link between the validity of the ID and the birthdate derived from it",
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2483
|
-
const committedInputs = proof.committedInputs?.compare_birthdate as DateCommittedInputs
|
|
2484
|
-
const calculatedParamCommitment = await getDateParameterCommitment(
|
|
2485
|
-
ProofType.BIRTHDATE,
|
|
2486
|
-
committedInputs.currentDate,
|
|
2487
|
-
committedInputs.minDate,
|
|
2488
|
-
committedInputs.maxDate,
|
|
2489
|
-
)
|
|
2490
|
-
if (paramCommitment !== calculatedParamCommitment) {
|
|
2491
|
-
console.warn(
|
|
2492
|
-
"The conditions for the birthdate check do not match the conditions checked by the proof",
|
|
2493
|
-
)
|
|
2494
|
-
isCorrect = false
|
|
2495
|
-
queryResultErrors.birthdate.commitment = {
|
|
2496
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2497
|
-
received: `Commitment: ${paramCommitment}`,
|
|
2498
|
-
message:
|
|
2499
|
-
"The conditions for the birthdate check do not match the conditions checked by the proof",
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2503
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "birthdate", scope)
|
|
2504
|
-
const { isCorrect: isCorrectBirthdate, queryResultErrors: queryResultErrorsBirthdate } =
|
|
2505
|
-
this.checkBirthdatePublicInputs(proof, queryResult)
|
|
2506
|
-
isCorrect = isCorrect && isCorrectBirthdate && isCorrectScope
|
|
2507
|
-
queryResultErrors = {
|
|
2508
|
-
...queryResultErrors,
|
|
2509
|
-
...queryResultErrorsBirthdate,
|
|
2510
|
-
...queryResultErrorsScope,
|
|
2511
|
-
}
|
|
2512
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2513
|
-
} else if (proof.name === "compare_expiry") {
|
|
2514
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2515
|
-
if (commitmentIn !== commitmentOut) {
|
|
2516
|
-
console.warn(
|
|
2517
|
-
"Failed to check the link between the validity of the ID and its expiry date",
|
|
2518
|
-
)
|
|
2519
|
-
isCorrect = false
|
|
2520
|
-
queryResultErrors.expiry_date.commitment = {
|
|
2521
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2522
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2523
|
-
message: "Failed to check the link between the validity of the ID and its expiry date",
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
const paramCommitment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2527
|
-
const committedInputs = proof.committedInputs?.compare_expiry as DateCommittedInputs
|
|
2528
|
-
const calculatedParamCommitment = await getDateParameterCommitment(
|
|
2529
|
-
ProofType.EXPIRY_DATE,
|
|
2530
|
-
committedInputs.currentDate,
|
|
2531
|
-
committedInputs.minDate,
|
|
2532
|
-
committedInputs.maxDate,
|
|
2533
|
-
)
|
|
2534
|
-
if (paramCommitment !== calculatedParamCommitment) {
|
|
2535
|
-
console.warn(
|
|
2536
|
-
"The conditions for the expiry date check do not match the conditions checked by the proof",
|
|
2537
|
-
)
|
|
2538
|
-
isCorrect = false
|
|
2539
|
-
queryResultErrors.expiry_date.commitment = {
|
|
2540
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2541
|
-
received: `Commitment: ${paramCommitment}`,
|
|
2542
|
-
message:
|
|
2543
|
-
"The conditions for the expiry date check do not match the conditions checked by the proof",
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2547
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "expiry_date", scope)
|
|
2548
|
-
const { isCorrect: isCorrectExpiryDate, queryResultErrors: queryResultErrorsExpiryDate } =
|
|
2549
|
-
this.checkExpiryDatePublicInputs(proof, queryResult)
|
|
2550
|
-
isCorrect = isCorrect && isCorrectExpiryDate && isCorrectScope
|
|
2551
|
-
queryResultErrors = {
|
|
2552
|
-
...queryResultErrors,
|
|
2553
|
-
...queryResultErrorsExpiryDate,
|
|
2554
|
-
...queryResultErrorsScope,
|
|
2555
|
-
}
|
|
2556
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2557
|
-
} else if (proof.name === "exclusion_check_nationality") {
|
|
2558
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2559
|
-
if (commitmentIn !== commitmentOut) {
|
|
2560
|
-
console.warn(
|
|
2561
|
-
"Failed to check the link between the validity of the ID and the nationality exclusion check",
|
|
2562
|
-
)
|
|
2563
|
-
isCorrect = false
|
|
2564
|
-
queryResultErrors.nationality.commitment = {
|
|
2565
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2566
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2567
|
-
message:
|
|
2568
|
-
"Failed to check the link between the validity of the ID and the nationality exclusion check",
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
const countryList = (
|
|
2572
|
-
proof.committedInputs?.exclusion_check_nationality as CountryCommittedInputs
|
|
2573
|
-
).countries
|
|
2574
|
-
const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2575
|
-
const calculatedParamCommitment = await getCountryParameterCommitment(
|
|
2576
|
-
ProofType.NATIONALITY_EXCLUSION,
|
|
2577
|
-
countryList,
|
|
2578
|
-
true,
|
|
2579
|
-
)
|
|
2580
|
-
if (paramCommittment !== calculatedParamCommitment) {
|
|
2581
|
-
console.warn(
|
|
2582
|
-
"The committed country list for the exclusion check does not match the one from the proof",
|
|
2583
|
-
)
|
|
2584
|
-
isCorrect = false
|
|
2585
|
-
queryResultErrors.nationality.commitment = {
|
|
2586
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2587
|
-
received: `Commitment: ${paramCommittment}`,
|
|
2588
|
-
message:
|
|
2589
|
-
"The committed country list for the exclusion check does not match the one from the proof",
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2593
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2594
|
-
const {
|
|
2595
|
-
isCorrect: isCorrectNationalityExclusion,
|
|
2596
|
-
queryResultErrors: queryResultErrorsNationalityExclusion,
|
|
2597
|
-
} = this.checkNationalityExclusionPublicInputs(queryResult, countryList)
|
|
2598
|
-
isCorrect = isCorrect && isCorrectNationalityExclusion && isCorrectScope
|
|
2599
|
-
queryResultErrors = {
|
|
2600
|
-
...queryResultErrors,
|
|
2601
|
-
...queryResultErrorsNationalityExclusion,
|
|
2602
|
-
...queryResultErrorsScope,
|
|
2603
|
-
}
|
|
2604
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2605
|
-
} else if (proof.name === "exclusion_check_issuing_country") {
|
|
2606
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2607
|
-
if (commitmentIn !== commitmentOut) {
|
|
2608
|
-
console.warn(
|
|
2609
|
-
"Failed to check the link between the validity of the ID and the issuing country exclusion check",
|
|
2610
|
-
)
|
|
2611
|
-
isCorrect = false
|
|
2612
|
-
queryResultErrors.nationality.commitment = {
|
|
2613
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2614
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2615
|
-
message:
|
|
2616
|
-
"Failed to check the link between the validity of the ID and the issuing country exclusion check",
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
const countryList = (
|
|
2620
|
-
proof.committedInputs?.exclusion_check_issuing_country as CountryCommittedInputs
|
|
2621
|
-
).countries
|
|
2622
|
-
const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2623
|
-
const calculatedParamCommitment = await getCountryParameterCommitment(
|
|
2624
|
-
ProofType.ISSUING_COUNTRY_EXCLUSION,
|
|
2625
|
-
countryList,
|
|
2626
|
-
true,
|
|
2627
|
-
)
|
|
2628
|
-
if (paramCommittment !== calculatedParamCommitment) {
|
|
2629
|
-
console.warn(
|
|
2630
|
-
"The committed country list for the issuing country exclusion check does not match the one from the proof",
|
|
2631
|
-
)
|
|
2632
|
-
isCorrect = false
|
|
2633
|
-
queryResultErrors.issuing_country.commitment = {
|
|
2634
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2635
|
-
received: `Commitment: ${paramCommittment}`,
|
|
2636
|
-
message:
|
|
2637
|
-
"The committed country list for the issuing country exclusion check does not match the one from the proof",
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
2640
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2641
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2642
|
-
const {
|
|
2643
|
-
isCorrect: isCorrectIssuingCountryExclusion,
|
|
2644
|
-
queryResultErrors: queryResultErrorsIssuingCountryExclusion,
|
|
2645
|
-
} = this.checkIssuingCountryExclusionPublicInputs(queryResult, countryList)
|
|
2646
|
-
isCorrect = isCorrect && isCorrectIssuingCountryExclusion && isCorrectScope
|
|
2647
|
-
queryResultErrors = {
|
|
2648
|
-
...queryResultErrors,
|
|
2649
|
-
...queryResultErrorsIssuingCountryExclusion,
|
|
2650
|
-
...queryResultErrorsScope,
|
|
2651
|
-
}
|
|
2652
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2653
|
-
} else if (proof.name === "inclusion_check_nationality") {
|
|
2654
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2655
|
-
if (commitmentIn !== commitmentOut) {
|
|
2656
|
-
console.warn(
|
|
2657
|
-
"Failed to check the link between the validity of the ID and the nationality inclusion check",
|
|
2658
|
-
)
|
|
2659
|
-
isCorrect = false
|
|
2660
|
-
queryResultErrors.nationality.commitment = {
|
|
2661
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2662
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2663
|
-
message:
|
|
2664
|
-
"Failed to check the link between the validity of the ID and the nationality inclusion check",
|
|
2665
|
-
}
|
|
2666
|
-
}
|
|
2667
|
-
const countryList = (
|
|
2668
|
-
proof.committedInputs?.inclusion_check_nationality as CountryCommittedInputs
|
|
2669
|
-
).countries
|
|
2670
|
-
const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2671
|
-
const calculatedParamCommitment = await getCountryParameterCommitment(
|
|
2672
|
-
ProofType.NATIONALITY_INCLUSION,
|
|
2673
|
-
countryList,
|
|
2674
|
-
false,
|
|
2675
|
-
)
|
|
2676
|
-
if (paramCommittment !== calculatedParamCommitment) {
|
|
2677
|
-
console.warn(
|
|
2678
|
-
"The committed country list for the nationality inclusion check does not match the one from the proof",
|
|
2679
|
-
)
|
|
2680
|
-
isCorrect = false
|
|
2681
|
-
queryResultErrors.nationality.commitment = {
|
|
2682
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2683
|
-
received: `Commitment: ${paramCommittment}`,
|
|
2684
|
-
message:
|
|
2685
|
-
"The committed country list for the nationality inclusion check does not match the one from the proof",
|
|
2686
|
-
}
|
|
2687
|
-
}
|
|
2688
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2689
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2690
|
-
const {
|
|
2691
|
-
isCorrect: isCorrectNationalityInclusion,
|
|
2692
|
-
queryResultErrors: queryResultErrorsNationalityInclusion,
|
|
2693
|
-
} = this.checkNationalityInclusionPublicInputs(queryResult, countryList)
|
|
2694
|
-
isCorrect = isCorrect && isCorrectNationalityInclusion && isCorrectScope
|
|
2695
|
-
queryResultErrors = {
|
|
2696
|
-
...queryResultErrors,
|
|
2697
|
-
...queryResultErrorsNationalityInclusion,
|
|
2698
|
-
...queryResultErrorsScope,
|
|
2699
|
-
}
|
|
2700
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2701
|
-
} else if (proof.name === "inclusion_check_issuing_country") {
|
|
2702
|
-
commitmentIn = getCommitmentInFromDisclosureProof(proofData)
|
|
2703
|
-
if (commitmentIn !== commitmentOut) {
|
|
2704
|
-
console.warn(
|
|
2705
|
-
"Failed to check the link between the validity of the ID and the issuing country inclusion check",
|
|
2706
|
-
)
|
|
2707
|
-
isCorrect = false
|
|
2708
|
-
queryResultErrors.nationality.commitment = {
|
|
2709
|
-
expected: `Commitment: ${commitmentOut}`,
|
|
2710
|
-
received: `Commitment: ${commitmentIn}`,
|
|
2711
|
-
message:
|
|
2712
|
-
"Failed to check the link between the validity of the ID and the issuing country inclusion check",
|
|
2713
|
-
}
|
|
2714
|
-
}
|
|
2715
|
-
const countryList = (
|
|
2716
|
-
proof.committedInputs?.inclusion_check_issuing_country as CountryCommittedInputs
|
|
2717
|
-
).countries
|
|
2718
|
-
const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2719
|
-
const calculatedParamCommitment = await getCountryParameterCommitment(
|
|
2720
|
-
ProofType.ISSUING_COUNTRY_INCLUSION,
|
|
2721
|
-
countryList,
|
|
2722
|
-
false,
|
|
2723
|
-
)
|
|
2724
|
-
if (paramCommittment !== calculatedParamCommitment) {
|
|
2725
|
-
console.warn(
|
|
2726
|
-
"The committed country list for the issuing country inclusion check does not match the one from the proof",
|
|
2727
|
-
)
|
|
2728
|
-
isCorrect = false
|
|
2729
|
-
queryResultErrors.issuing_country.commitment = {
|
|
2730
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2731
|
-
received: `Commitment: ${paramCommittment}`,
|
|
2732
|
-
message:
|
|
2733
|
-
"The committed country list for the issuing country inclusion check does not match the one from the proof",
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
const { isCorrect: isCorrectScope, queryResultErrors: queryResultErrorsScope } =
|
|
2737
|
-
this.checkScopeFromDisclosureProof(proofData, queryResultErrors, "nationality", scope)
|
|
2738
|
-
const {
|
|
2739
|
-
isCorrect: isCorrectIssuingCountryInclusion,
|
|
2740
|
-
queryResultErrors: queryResultErrorsIssuingCountryInclusion,
|
|
2741
|
-
} = this.checkIssuingCountryInclusionPublicInputs(queryResult, countryList)
|
|
2742
|
-
isCorrect = isCorrect && isCorrectIssuingCountryInclusion && isCorrectScope
|
|
2743
|
-
queryResultErrors = {
|
|
2744
|
-
...queryResultErrors,
|
|
2745
|
-
...queryResultErrorsIssuingCountryInclusion,
|
|
2746
|
-
...queryResultErrorsScope,
|
|
2747
|
-
}
|
|
2748
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2749
|
-
} else if (proof.name === "bind") {
|
|
2750
|
-
const bindCommittedInputs = proof.committedInputs?.bind as BindCommittedInputs
|
|
2751
|
-
const paramCommittment = getParameterCommitmentFromDisclosureProof(proofData)
|
|
2752
|
-
const calculatedParamCommitment = await getBindParameterCommitment(
|
|
2753
|
-
formatBoundData(bindCommittedInputs.data),
|
|
2754
|
-
)
|
|
2755
|
-
if (paramCommittment !== calculatedParamCommitment) {
|
|
2756
|
-
console.warn("The bound data does not match the one from the proof")
|
|
2757
|
-
isCorrect = false
|
|
2758
|
-
queryResultErrors.bind.commitment = {
|
|
2759
|
-
expected: `Commitment: ${calculatedParamCommitment}`,
|
|
2760
|
-
received: `Commitment: ${paramCommittment}`,
|
|
2761
|
-
message: "The bound data does not match the one from the proof",
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } =
|
|
2765
|
-
this.checkBindPublicInputs(queryResult, bindCommittedInputs.data)
|
|
2766
|
-
isCorrect = isCorrect && isCorrectBind
|
|
2767
|
-
queryResultErrors = {
|
|
2768
|
-
...queryResultErrors,
|
|
2769
|
-
...queryResultErrorsBind,
|
|
2770
|
-
}
|
|
2771
|
-
uniqueIdentifier = getNullifierFromDisclosureProof(proofData).toString(10)
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
return { isCorrect, uniqueIdentifier, queryResultErrors }
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
|
-
/**
|
|
2778
|
-
* @notice Verify the proofs received from the mobile app.
|
|
2779
|
-
* @param proofs The proofs to verify.
|
|
2780
|
-
* @param queryResult The query result to verify against
|
|
2781
|
-
* @param validity How many days ago should have the ID been last scanned by the user?
|
|
2782
|
-
* @param scope Scope this request to a specific use case
|
|
2783
|
-
* @param evmChain The EVM chain to use for the verification (if using the proof onchain)
|
|
2784
|
-
* @param devMode Whether to enable dev mode. This will allow you to verify mock proofs (i.e. from ZKR)
|
|
2785
|
-
* @param writingDirectory The directory (e.g. `./tmp`) where the necessary temporary artifacts for verification are written to.
|
|
2786
|
-
* It should only be needed when running the `verify` function on a server with restricted write access (e.g. Vercel)
|
|
2787
|
-
* @returns An object containing the unique identifier associated to the user
|
|
2788
|
-
* and a boolean indicating whether the proofs were successfully verified.
|
|
2789
|
-
*/
|
|
2790
|
-
public async verify({
|
|
2791
|
-
proofs,
|
|
2792
|
-
queryResult,
|
|
2793
|
-
validity,
|
|
2794
|
-
scope,
|
|
2795
|
-
evmChain,
|
|
2796
|
-
devMode = false,
|
|
2797
|
-
writingDirectory,
|
|
2798
|
-
}: {
|
|
2799
|
-
proofs: Array<ProofResult>
|
|
2800
|
-
queryResult: QueryResult
|
|
2801
|
-
validity?: number
|
|
2802
|
-
scope?: string
|
|
2803
|
-
evmChain?: EVMChain
|
|
2804
|
-
devMode?: boolean
|
|
2805
|
-
writingDirectory?: string
|
|
2806
|
-
}): Promise<{
|
|
2807
|
-
uniqueIdentifier: string | undefined
|
|
2808
|
-
verified: boolean
|
|
2809
|
-
queryResultErrors?: QueryResultErrors
|
|
2810
|
-
}> {
|
|
2811
|
-
// If no proofs were generated, the results can't be trusted.
|
|
2812
|
-
// We still return it but verified will be false
|
|
2813
|
-
if (!proofs || proofs.length === 0) {
|
|
2814
|
-
return {
|
|
2815
|
-
uniqueIdentifier: undefined,
|
|
2816
|
-
verified: false,
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
const formattedResult: QueryResult = queryResult
|
|
2820
|
-
// Make sure to reconvert the dates to Date objects
|
|
2821
|
-
if (formattedResult.birthdate && formattedResult.birthdate.disclose) {
|
|
2822
|
-
formattedResult.birthdate.disclose.result = new Date(
|
|
2823
|
-
formattedResult.birthdate.disclose.result,
|
|
2824
|
-
)
|
|
2825
|
-
}
|
|
2826
|
-
if (formattedResult.expiry_date && formattedResult.expiry_date.disclose) {
|
|
2827
|
-
formattedResult.expiry_date.disclose.result = new Date(
|
|
2828
|
-
formattedResult.expiry_date.disclose.result,
|
|
2829
|
-
)
|
|
2830
|
-
}
|
|
2831
|
-
|
|
2832
|
-
const { BarretenbergVerifier } = await import("@aztec/bb.js")
|
|
2833
|
-
// Automatically set the writing directory to `/tmp` if it is not provided
|
|
2834
|
-
// and the code is not running in the browser
|
|
2835
|
-
if (typeof window === "undefined" && !writingDirectory) {
|
|
2836
|
-
writingDirectory = "/tmp"
|
|
2837
|
-
}
|
|
2838
|
-
const verifier = new BarretenbergVerifier({
|
|
2839
|
-
crsPath: writingDirectory ? writingDirectory + "/.bb-crs" : undefined,
|
|
2840
|
-
})
|
|
2841
|
-
let verified = true
|
|
2842
|
-
let uniqueIdentifier: string | undefined
|
|
2843
|
-
let queryResultErrors: QueryResultErrors | undefined
|
|
2844
|
-
const chainId = evmChain ? getChainIdFromEVMChain(evmChain) : undefined
|
|
2845
|
-
const {
|
|
2846
|
-
isCorrect,
|
|
2847
|
-
uniqueIdentifier: uniqueIdentifierFromPublicInputs,
|
|
2848
|
-
queryResultErrors: queryResultErrorsFromPublicInputs,
|
|
2849
|
-
} = await this.checkPublicInputs(proofs, formattedResult, validity, scope, chainId)
|
|
2850
|
-
uniqueIdentifier = uniqueIdentifierFromPublicInputs
|
|
2851
|
-
verified = isCorrect
|
|
2852
|
-
queryResultErrors = isCorrect ? undefined : queryResultErrorsFromPublicInputs
|
|
2853
|
-
if (
|
|
2854
|
-
uniqueIdentifier &&
|
|
2855
|
-
(BigInt(uniqueIdentifier) === BigInt(1) || BigInt(uniqueIdentifier) === BigInt(0)) &&
|
|
2856
|
-
!devMode
|
|
2857
|
-
) {
|
|
2858
|
-
// If the unique identifier is 0 (old ZKR proofs) or 1 (new ZKR proofs) and it is not in dev mode,
|
|
2859
|
-
// the proofs are considered invalid as these are mock proofs only meant
|
|
2860
|
-
// for testing purposes
|
|
2861
|
-
verified = false
|
|
2862
|
-
console.warn(
|
|
2863
|
-
"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.",
|
|
2864
|
-
)
|
|
2865
|
-
}
|
|
2866
|
-
// Only proceed with the proof verification if the public inputs are correct
|
|
2867
|
-
if (verified) {
|
|
2868
|
-
const registryClient = new RegistryClient({ chainId: 11155111 })
|
|
2869
|
-
const circuitManifest = await registryClient.getCircuitManifest(undefined, {
|
|
2870
|
-
// We assume all proofs have the same version
|
|
2871
|
-
version: proofs[0].version,
|
|
2872
|
-
})
|
|
2873
|
-
for (const proof of proofs) {
|
|
2874
|
-
const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
|
|
2875
|
-
const hostedPackagedCircuit = await registryClient.getPackagedCircuit(
|
|
2876
|
-
proof.name!,
|
|
2877
|
-
circuitManifest,
|
|
2878
|
-
)
|
|
2879
|
-
if (proof.name?.startsWith("outer_evm")) {
|
|
2880
|
-
try {
|
|
2881
|
-
const { createPublicClient, http } = await import("viem")
|
|
2882
|
-
const { sepolia } = await import("viem/chains")
|
|
2883
|
-
const { address, abi, functionName } =
|
|
2884
|
-
this.getSolidityVerifierDetails("ethereum_sepolia")
|
|
2885
|
-
const client = createPublicClient({
|
|
2886
|
-
chain: sepolia,
|
|
2887
|
-
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
|
|
2888
|
-
})
|
|
2889
|
-
const params = this.getSolidityVerifierParameters({
|
|
2890
|
-
proof,
|
|
2891
|
-
validityPeriodInDays: validity,
|
|
2892
|
-
domain: this.domain,
|
|
2893
|
-
scope,
|
|
2894
|
-
devMode,
|
|
2895
|
-
})
|
|
2896
|
-
const result = await client.readContract({
|
|
2897
|
-
address,
|
|
2898
|
-
abi,
|
|
2899
|
-
functionName,
|
|
2900
|
-
args: [params],
|
|
2901
|
-
})
|
|
2902
|
-
const isVerified = Array.isArray(result) ? Boolean(result[0]) : false
|
|
2903
|
-
verified = isVerified
|
|
2904
|
-
} catch (error) {
|
|
2905
|
-
console.warn("Error verifying proof", error)
|
|
2906
|
-
verified = false
|
|
2907
|
-
}
|
|
2908
|
-
} else {
|
|
2909
|
-
const vkeyBytes = Buffer.from(hostedPackagedCircuit.vkey, "base64")
|
|
2910
|
-
try {
|
|
2911
|
-
verified = await verifier.verifyUltraHonkProof(
|
|
2912
|
-
{
|
|
2913
|
-
proof: Buffer.from(proofData.proof.join(""), "hex"),
|
|
2914
|
-
publicInputs: proofData.publicInputs,
|
|
2915
|
-
},
|
|
2916
|
-
new Uint8Array(vkeyBytes),
|
|
2917
|
-
)
|
|
2918
|
-
} catch (e) {
|
|
2919
|
-
console.warn("Error verifying proof", e)
|
|
2920
|
-
verified = false
|
|
2921
|
-
}
|
|
2922
|
-
}
|
|
2923
|
-
if (!verified) {
|
|
2924
|
-
// Break the loop if the proof is not valid
|
|
2925
|
-
// and don't bother checking the other proofs
|
|
2926
|
-
break
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
// If the proofs are not verified, we don't return the unique identifier
|
|
2931
|
-
uniqueIdentifier = verified ? uniqueIdentifier : undefined
|
|
2932
|
-
return { uniqueIdentifier, verified, queryResultErrors }
|
|
2933
|
-
}
|
|
2934
|
-
|
|
2935
|
-
public getSolidityVerifierDetails(network: EVMChain): {
|
|
2936
|
-
address: `0x${string}`
|
|
2937
|
-
functionName: string
|
|
2938
|
-
abi: {
|
|
2939
|
-
type: "function" | "event" | "constructor"
|
|
2940
|
-
name: string
|
|
2941
|
-
inputs: { name: string; type: string; internalType: string }[]
|
|
2942
|
-
outputs: { name: string; type: string; internalType: string }[]
|
|
2943
|
-
}[]
|
|
2944
|
-
} {
|
|
2945
|
-
const baseConfig = {
|
|
2946
|
-
functionName: "verifyProof",
|
|
2947
|
-
abi: ZKPassportVerifierAbi.abi as any,
|
|
2948
|
-
}
|
|
2949
|
-
if (network === "ethereum_sepolia") {
|
|
2950
|
-
return {
|
|
2951
|
-
...baseConfig,
|
|
2952
|
-
address: "0xDDeFf76024052D26B78A7Fac66FFbd6fbc5bd9Ad",
|
|
2953
|
-
}
|
|
2954
|
-
} else if (network === "local_anvil") {
|
|
2955
|
-
return {
|
|
2956
|
-
...baseConfig,
|
|
2957
|
-
address: "0x0",
|
|
2958
|
-
}
|
|
2959
|
-
}
|
|
2960
|
-
throw new Error(`Unsupported network: ${network}`)
|
|
2961
|
-
}
|
|
2962
|
-
|
|
2963
|
-
public getSolidityVerifierParameters({
|
|
2964
|
-
proof,
|
|
2965
|
-
validityPeriodInDays = 7,
|
|
2966
|
-
domain,
|
|
2967
|
-
scope,
|
|
2968
|
-
devMode = false,
|
|
2969
|
-
}: {
|
|
2970
|
-
proof: ProofResult
|
|
2971
|
-
validityPeriodInDays?: number
|
|
2972
|
-
domain?: string
|
|
2973
|
-
scope?: string
|
|
2974
|
-
devMode?: boolean
|
|
2975
|
-
}) {
|
|
2976
|
-
if (!proof.name?.startsWith("outer_evm")) {
|
|
2977
|
-
throw new Error(
|
|
2978
|
-
"This proof cannot be verified on an EVM chain. Please make sure to use the `compressed-evm` mode.",
|
|
2979
|
-
)
|
|
2980
|
-
}
|
|
2981
|
-
const proofData = getProofData(proof.proof as string, getNumberOfPublicInputs(proof.name!))
|
|
2982
|
-
// For EVM optimised proofs, the first 16 bytes of the proof are the aggregation object
|
|
2983
|
-
// and should be moved at the end of the public inputs
|
|
2984
|
-
const actualProof = proofData.proof.slice(16)
|
|
2985
|
-
const actualPublicInputs = proofData.publicInputs.concat(
|
|
2986
|
-
proofData.proof.slice(0, 16).map((x) => `0x${x}`),
|
|
2987
|
-
)
|
|
2988
|
-
let committedInputCounts: { circuitName: DisclosureCircuitName; count: number }[] = []
|
|
2989
|
-
let committedInputs: { circuitName: DisclosureCircuitName; inputs: string }[] = []
|
|
2990
|
-
for (const key in proof.committedInputs) {
|
|
2991
|
-
const committedInputCount = getCommittedInputCount(key as DisclosureCircuitName)
|
|
2992
|
-
const circuitName = key as DisclosureCircuitName
|
|
2993
|
-
committedInputCounts.push({ circuitName, count: committedInputCount })
|
|
2994
|
-
let compressedCommittedInputs = ""
|
|
2995
|
-
if (
|
|
2996
|
-
circuitName === "inclusion_check_issuing_country_evm" ||
|
|
2997
|
-
circuitName === "inclusion_check_nationality_evm" ||
|
|
2998
|
-
circuitName === "exclusion_check_issuing_country_evm" ||
|
|
2999
|
-
circuitName === "exclusion_check_nationality_evm"
|
|
3000
|
-
) {
|
|
3001
|
-
const value = proof.committedInputs[circuitName] as CountryCommittedInputs
|
|
3002
|
-
const formattedCountries = value.countries
|
|
3003
|
-
if (
|
|
3004
|
-
circuitName === "exclusion_check_issuing_country_evm" ||
|
|
3005
|
-
circuitName === "exclusion_check_nationality_evm"
|
|
3006
|
-
) {
|
|
3007
|
-
formattedCountries.sort((a, b) => a.localeCompare(b))
|
|
3008
|
-
}
|
|
3009
|
-
const proofType = (() => {
|
|
3010
|
-
switch (circuitName) {
|
|
3011
|
-
case "exclusion_check_issuing_country_evm":
|
|
3012
|
-
return ProofType.ISSUING_COUNTRY_EXCLUSION
|
|
3013
|
-
case "exclusion_check_nationality_evm":
|
|
3014
|
-
return ProofType.NATIONALITY_EXCLUSION
|
|
3015
|
-
case "inclusion_check_issuing_country_evm":
|
|
3016
|
-
return ProofType.ISSUING_COUNTRY_INCLUSION
|
|
3017
|
-
case "inclusion_check_nationality_evm":
|
|
3018
|
-
return ProofType.NATIONALITY_INCLUSION
|
|
3019
|
-
}
|
|
3020
|
-
})()
|
|
3021
|
-
compressedCommittedInputs =
|
|
3022
|
-
proofType.toString(16).padStart(2, "0") +
|
|
3023
|
-
rightPadArrayWithZeros(
|
|
3024
|
-
formattedCountries.map((c) => Array.from(new TextEncoder().encode(c))).flat(),
|
|
3025
|
-
600,
|
|
3026
|
-
)
|
|
3027
|
-
.map((x) => x.toString(16).padStart(2, "0"))
|
|
3028
|
-
.join("")
|
|
3029
|
-
} else if (circuitName === "compare_age_evm") {
|
|
3030
|
-
const value = proof.committedInputs[circuitName] as AgeCommittedInputs
|
|
3031
|
-
const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
|
|
3032
|
-
compressedCommittedInputs =
|
|
3033
|
-
ProofType.AGE.toString(16).padStart(2, "0") +
|
|
3034
|
-
currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3035
|
-
value.minAge.toString(16).padStart(2, "0") +
|
|
3036
|
-
value.maxAge.toString(16).padStart(2, "0")
|
|
3037
|
-
} else if (circuitName === "compare_birthdate_evm") {
|
|
3038
|
-
const value = proof.committedInputs[circuitName] as DateCommittedInputs
|
|
3039
|
-
const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
|
|
3040
|
-
const minDateBytes = Array.from(new TextEncoder().encode(value.minDate))
|
|
3041
|
-
const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate))
|
|
3042
|
-
compressedCommittedInputs =
|
|
3043
|
-
ProofType.BIRTHDATE.toString(16).padStart(2, "0") +
|
|
3044
|
-
currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3045
|
-
minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3046
|
-
maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
|
|
3047
|
-
} else if (circuitName === "compare_expiry_evm") {
|
|
3048
|
-
const value = proof.committedInputs[circuitName] as DateCommittedInputs
|
|
3049
|
-
const currentDateBytes = Array.from(new TextEncoder().encode(value.currentDate))
|
|
3050
|
-
const minDateBytes = Array.from(new TextEncoder().encode(value.minDate))
|
|
3051
|
-
const maxDateBytes = Array.from(new TextEncoder().encode(value.maxDate))
|
|
3052
|
-
compressedCommittedInputs =
|
|
3053
|
-
ProofType.EXPIRY_DATE.toString(16).padStart(2, "0") +
|
|
3054
|
-
currentDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3055
|
-
minDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3056
|
-
maxDateBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
|
|
3057
|
-
} else if (circuitName === "disclose_bytes_evm") {
|
|
3058
|
-
const value = proof.committedInputs[circuitName] as DiscloseCommittedInputs
|
|
3059
|
-
compressedCommittedInputs =
|
|
3060
|
-
ProofType.DISCLOSE.toString(16).padStart(2, "0") +
|
|
3061
|
-
value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
|
|
3062
|
-
value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("")
|
|
3063
|
-
} else if (circuitName === "bind_evm") {
|
|
3064
|
-
const value = proof.committedInputs[circuitName] as BindCommittedInputs
|
|
3065
|
-
compressedCommittedInputs =
|
|
3066
|
-
ProofType.BIND.toString(16).padStart(2, "0") +
|
|
3067
|
-
rightPadArrayWithZeros(formatBoundData(value.data), 500)
|
|
3068
|
-
.map((x) => x.toString(16).padStart(2, "0"))
|
|
3069
|
-
.join("")
|
|
3070
|
-
} else {
|
|
3071
|
-
throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`)
|
|
3072
|
-
}
|
|
3073
|
-
committedInputs.push({ circuitName, inputs: compressedCommittedInputs })
|
|
3074
|
-
}
|
|
3075
|
-
const parameterCommitments = proofData.publicInputs.slice(12, proofData.publicInputs.length - 1)
|
|
3076
|
-
let compressedCommittedInputs = ""
|
|
3077
|
-
let committedInputCountsArray = []
|
|
3078
|
-
for (const commitment of parameterCommitments) {
|
|
3079
|
-
const committedInput = committedInputs.find((x) => {
|
|
3080
|
-
const rawHashedInputs = sha256(hexToBytes(x.inputs))
|
|
3081
|
-
// Shift the hash 8 bits to the right (1 byte)
|
|
3082
|
-
// as one byte is dropped in the circuit to fit in the 254-bit field size
|
|
3083
|
-
const hashedInputs = new Uint8Array(rawHashedInputs.length)
|
|
3084
|
-
// Move each byte 1 position to the right (shifting 8 bits)
|
|
3085
|
-
for (let i = 0; i < rawHashedInputs.length - 1; i++) {
|
|
3086
|
-
hashedInputs[i + 1] = rawHashedInputs[i]
|
|
3087
|
-
}
|
|
3088
|
-
// First byte becomes 0 (since we're shifting right)
|
|
3089
|
-
hashedInputs[0] = 0
|
|
3090
|
-
|
|
3091
|
-
return bytesToHex(hashedInputs) === commitment.replace("0x", "")
|
|
3092
|
-
})
|
|
3093
|
-
if (committedInput) {
|
|
3094
|
-
const count = committedInputCounts.find(
|
|
3095
|
-
(x) => x.circuitName === committedInput.circuitName,
|
|
3096
|
-
)?.count
|
|
3097
|
-
if (count) {
|
|
3098
|
-
committedInputCountsArray.push(count)
|
|
3099
|
-
compressedCommittedInputs += committedInput.inputs
|
|
3100
|
-
} else {
|
|
3101
|
-
throw new Error(`Unknown circuit name: ${committedInput.circuitName}`)
|
|
3102
|
-
}
|
|
3103
|
-
} else {
|
|
3104
|
-
throw new Error(`Invalid commitment: ${commitment}`)
|
|
3105
|
-
}
|
|
3106
|
-
}
|
|
3107
|
-
const params: SolidityVerifierParameters = {
|
|
3108
|
-
// Make sure the vkeyHash is 32 bytes
|
|
3109
|
-
vkeyHash: `0x${proof.vkeyHash!.replace("0x", "").padStart(64, "0")}`,
|
|
3110
|
-
proof: `0x${actualProof.join("")}`,
|
|
3111
|
-
publicInputs: actualPublicInputs,
|
|
3112
|
-
committedInputs: `0x${compressedCommittedInputs}`,
|
|
3113
|
-
committedInputCounts: committedInputCountsArray,
|
|
3114
|
-
validityPeriodInDays,
|
|
3115
|
-
domain: domain ?? this.domain,
|
|
3116
|
-
scope: scope ?? "",
|
|
3117
|
-
devMode,
|
|
3118
|
-
}
|
|
3119
|
-
return params
|
|
3120
|
-
}
|
|
3121
|
-
|
|
3122
|
-
private _getUrl(requestId: string) {
|
|
3123
|
-
const base64Config = Buffer.from(JSON.stringify(this.topicToConfig[requestId])).toString(
|
|
3124
|
-
"base64",
|
|
3125
|
-
)
|
|
3126
|
-
const base64Service = Buffer.from(JSON.stringify(this.topicToService[requestId])).toString(
|
|
3127
|
-
"base64",
|
|
3128
|
-
)
|
|
3129
|
-
const pubkey = this.topicToPublicKey[requestId]
|
|
3130
|
-
return `https://zkpassport.id/r?d=${this.domain}&t=${requestId}&c=${base64Config}&s=${base64Service}&p=${pubkey}&m=${this.topicToLocalConfig[requestId].mode}&v=${VERSION}`
|
|
3131
|
-
}
|
|
3132
|
-
|
|
3133
|
-
/**
|
|
3134
|
-
* @notice Returns the URL of the request.
|
|
3135
|
-
* @param requestId The request ID.
|
|
3136
|
-
* @returns The URL of the request.
|
|
3137
|
-
*/
|
|
3138
|
-
public getUrl(requestId: string) {
|
|
3139
|
-
return this._getUrl(requestId)
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
/**
|
|
3143
|
-
* @notice Cancels a request by closing the WebSocket connection and deleting the associated data.
|
|
3144
|
-
* @param requestId The request ID.
|
|
3145
|
-
*/
|
|
3146
|
-
public cancelRequest(requestId: string) {
|
|
3147
|
-
if (this.topicToBridge[requestId]) {
|
|
3148
|
-
this.topicToBridge[requestId].close()
|
|
3149
|
-
delete this.topicToBridge[requestId]
|
|
3150
|
-
}
|
|
3151
|
-
delete this.topicToPublicKey[requestId]
|
|
3152
|
-
delete this.topicToConfig[requestId]
|
|
3153
|
-
delete this.topicToLocalConfig[requestId]
|
|
3154
|
-
delete this.topicToProofs[requestId]
|
|
3155
|
-
delete this.topicToExpectedProofCount[requestId]
|
|
3156
|
-
delete this.topicToFailedProofCount[requestId]
|
|
3157
|
-
delete this.topicToResults[requestId]
|
|
3158
|
-
this.onRequestReceivedCallbacks[requestId] = []
|
|
3159
|
-
this.onGeneratingProofCallbacks[requestId] = []
|
|
3160
|
-
this.onBridgeConnectCallbacks[requestId] = []
|
|
3161
|
-
this.onProofGeneratedCallbacks[requestId] = []
|
|
3162
|
-
this.onRejectCallbacks[requestId] = []
|
|
3163
|
-
this.onErrorCallbacks[requestId] = []
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
|
-
/**
|
|
3167
|
-
* @notice Clears all requests.
|
|
3168
|
-
*/
|
|
3169
|
-
public clearAllRequests() {
|
|
3170
|
-
for (const requestId in this.topicToBridge) {
|
|
3171
|
-
this.cancelRequest(requestId)
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
}
|