@zkpassport/sdk 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
- import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode } from "@zkpassport/utils";
1
+ import { type DisclosableIDCredential, type IDCredential, type IDCredentialValue, type NumericalIDCredential, type ProofResult, type QueryResult, ProofMode, BoundData } from "@zkpassport/utils";
2
2
  export type QueryResultError<T> = {
3
3
  expected?: T;
4
4
  received?: T;
5
5
  message: string;
6
6
  };
7
7
  export type QueryResultErrors = {
8
- [key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose"]: {
8
+ [key in IDCredential | "sig_check_dsc" | "sig_check_id_data" | "data_check_integrity" | "outer" | "disclose" | "bind"]: {
9
9
  disclose?: QueryResultError<string | number | Date>;
10
10
  gte?: QueryResultError<number | Date>;
11
11
  lte?: QueryResultError<number | Date>;
@@ -150,6 +150,12 @@ export type QueryBuilder = {
150
150
  * @param key The attribute to disclose.
151
151
  */
152
152
  disclose: (key: DisclosableIDCredential) => QueryBuilder;
153
+ /**
154
+ * Binds a value to the request.
155
+ * @param key The key of the value to bind.
156
+ * @param value The value to bind the request to.
157
+ */
158
+ bind: (key: keyof BoundData, value: BoundData[keyof BoundData]) => QueryBuilder;
153
159
  /**
154
160
  * Builds the request.
155
161
  *
@@ -224,6 +230,7 @@ export declare class ZKPassport {
224
230
  private checkIssuingCountryInclusionPublicInputs;
225
231
  private checkScopeFromDisclosureProof;
226
232
  private checkCertificateRegistryRoot;
233
+ private checkBindPublicInputs;
227
234
  private checkPublicInputs;
228
235
  /**
229
236
  * @notice Verify the proofs received from the mobile app.
package/dist/cjs/index.js CHANGED
@@ -201,6 +201,9 @@ class ZKPassport {
201
201
  }
202
202
  }
203
203
  }
204
+ if (this.topicToConfig[topic].bind) {
205
+ neededCircuits.push("bind");
206
+ }
204
207
  // From the circuits needed, determine the expected proof count
205
208
  // There are at least 4 proofs, 3 base proofs and 1 disclosure proof minimum
206
209
  // Each separate needed circuit adds 1 disclosure proof
@@ -323,6 +326,13 @@ class ZKPassport {
323
326
  };
324
327
  return this.getZkPassportRequest(topic);
325
328
  },
329
+ bind: (key, value) => {
330
+ this.topicToConfig[topic].bind = {
331
+ ...this.topicToConfig[topic].bind,
332
+ [key]: value,
333
+ };
334
+ return this.getZkPassportRequest(topic);
335
+ },
326
336
  done: () => {
327
337
  const base64Config = buffer_1.Buffer.from(JSON.stringify(this.topicToConfig[topic])).toString("base64");
328
338
  const base64Service = buffer_1.Buffer.from(JSON.stringify(this.topicToService[topic])).toString("base64");
@@ -419,6 +429,7 @@ class ZKPassport {
419
429
  fullname: {},
420
430
  document_number: {},
421
431
  outer: {},
432
+ bind: {},
422
433
  };
423
434
  let isCorrect = true;
424
435
  // We can't be certain that the disclosed data is for a passport or an ID card
@@ -731,6 +742,7 @@ class ZKPassport {
731
742
  fullname: {},
732
743
  document_number: {},
733
744
  outer: {},
745
+ bind: {},
734
746
  };
735
747
  let isCorrect = true;
736
748
  const currentTime = new Date();
@@ -840,6 +852,7 @@ class ZKPassport {
840
852
  fullname: {},
841
853
  document_number: {},
842
854
  outer: {},
855
+ bind: {},
843
856
  };
844
857
  let isCorrect = true;
845
858
  const currentTime = new Date();
@@ -943,6 +956,7 @@ class ZKPassport {
943
956
  fullname: {},
944
957
  document_number: {},
945
958
  outer: {},
959
+ bind: {},
946
960
  };
947
961
  let isCorrect = true;
948
962
  const currentTime = new Date();
@@ -1046,6 +1060,7 @@ class ZKPassport {
1046
1060
  fullname: {},
1047
1061
  document_number: {},
1048
1062
  outer: {},
1063
+ bind: {},
1049
1064
  };
1050
1065
  let isCorrect = true;
1051
1066
  if (queryResult.nationality &&
@@ -1100,6 +1115,7 @@ class ZKPassport {
1100
1115
  fullname: {},
1101
1116
  document_number: {},
1102
1117
  outer: {},
1118
+ bind: {},
1103
1119
  };
1104
1120
  let isCorrect = true;
1105
1121
  if (queryResult.issuing_country &&
@@ -1154,6 +1170,7 @@ class ZKPassport {
1154
1170
  fullname: {},
1155
1171
  document_number: {},
1156
1172
  outer: {},
1173
+ bind: {},
1157
1174
  };
1158
1175
  let isCorrect = true;
1159
1176
  if (queryResult.nationality &&
@@ -1196,6 +1213,7 @@ class ZKPassport {
1196
1213
  fullname: {},
1197
1214
  document_number: {},
1198
1215
  outer: {},
1216
+ bind: {},
1199
1217
  };
1200
1218
  let isCorrect = true;
1201
1219
  if (queryResult.issuing_country &&
@@ -1271,6 +1289,51 @@ class ZKPassport {
1271
1289
  }
1272
1290
  return { isCorrect, queryResultErrors };
1273
1291
  }
1292
+ checkBindPublicInputs(queryResult, boundData) {
1293
+ const queryResultErrors = {
1294
+ sig_check_dsc: {},
1295
+ sig_check_id_data: {},
1296
+ data_check_integrity: {},
1297
+ disclose: {},
1298
+ age: {},
1299
+ birthdate: {},
1300
+ expiry_date: {},
1301
+ document_type: {},
1302
+ issuing_country: {},
1303
+ gender: {},
1304
+ nationality: {},
1305
+ firstname: {},
1306
+ lastname: {},
1307
+ fullname: {},
1308
+ document_number: {},
1309
+ outer: {},
1310
+ bind: {},
1311
+ };
1312
+ let isCorrect = true;
1313
+ if (queryResult.bind) {
1314
+ if (queryResult.bind.user_address?.toLowerCase().replace("0x", "") !==
1315
+ boundData.user_address?.toLowerCase().replace("0x", "")) {
1316
+ console.warn("Bound user address does not match the one from the query results");
1317
+ isCorrect = false;
1318
+ queryResultErrors.bind.eq = {
1319
+ expected: queryResult.bind.user_address,
1320
+ received: boundData.user_address,
1321
+ message: "Bound user address does not match the one from the query results",
1322
+ };
1323
+ }
1324
+ if (queryResult.bind.custom_data?.trim().toLowerCase() !==
1325
+ boundData.custom_data?.trim().toLowerCase()) {
1326
+ console.warn("Bound custom data does not match the one from the query results");
1327
+ isCorrect = false;
1328
+ queryResultErrors.bind.eq = {
1329
+ expected: queryResult.bind.custom_data,
1330
+ received: boundData.custom_data,
1331
+ message: "Bound custom data does not match the one from the query results",
1332
+ };
1333
+ }
1334
+ }
1335
+ return { isCorrect, queryResultErrors };
1336
+ }
1274
1337
  async checkPublicInputs(proofs, queryResult, validity, scope, chainId) {
1275
1338
  let commitmentIn;
1276
1339
  let commitmentOut;
@@ -1295,6 +1358,7 @@ class ZKPassport {
1295
1358
  fullname: {},
1296
1359
  document_number: {},
1297
1360
  outer: {},
1361
+ bind: {},
1298
1362
  };
1299
1363
  // Since the order is important for the commitments, we need to sort the proofs
1300
1364
  // by their expected order: root signature check -> ID signature check -> integrity check -> disclosure
@@ -1311,6 +1375,7 @@ class ZKPassport {
1311
1375
  "inclusion_check_nationality",
1312
1376
  "exclusion_check_issuing_country",
1313
1377
  "inclusion_check_issuing_country",
1378
+ "bind",
1314
1379
  ];
1315
1380
  const getIndex = (proof) => {
1316
1381
  const name = proof.name || "";
@@ -1547,6 +1612,27 @@ class ZKPassport {
1547
1612
  ...queryResultErrorsIssuingCountryExclusion,
1548
1613
  };
1549
1614
  }
1615
+ else if (!!committedInputs?.bind) {
1616
+ const bindCommittedInputs = committedInputs?.bind;
1617
+ const bindParameterCommitment = isForEVM
1618
+ ? await (0, utils_1.getBindEVMParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data))
1619
+ : await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1620
+ if (!paramCommitments.includes(bindParameterCommitment)) {
1621
+ console.warn("This proof does not verify the bound data");
1622
+ isCorrect = false;
1623
+ queryResultErrors.bind.commitment = {
1624
+ expected: `Bind parameter commitment: ${bindParameterCommitment.toString()}`,
1625
+ received: `Parameter commitments included: ${paramCommitments.join(", ")}`,
1626
+ message: "This proof does not verify the bound data",
1627
+ };
1628
+ }
1629
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1630
+ isCorrect = isCorrect && isCorrectBind;
1631
+ queryResultErrors = {
1632
+ ...queryResultErrors,
1633
+ ...queryResultErrorsBind,
1634
+ };
1635
+ }
1550
1636
  uniqueIdentifier = (0, utils_1.getNullifierFromOuterProof)(proofData).toString(10);
1551
1637
  }
1552
1638
  else if (proof.name?.startsWith("sig_check_dsc")) {
@@ -1868,6 +1954,27 @@ class ZKPassport {
1868
1954
  };
1869
1955
  uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1870
1956
  }
1957
+ else if (proof.name === "bind") {
1958
+ const bindCommittedInputs = proof.committedInputs?.bind;
1959
+ const paramCommittment = (0, utils_1.getParameterCommitmentFromDisclosureProof)(proofData);
1960
+ const calculatedParamCommitment = await (0, utils_1.getBindParameterCommitment)((0, utils_1.formatBoundData)(bindCommittedInputs.data));
1961
+ if (paramCommittment !== calculatedParamCommitment) {
1962
+ console.warn("The bound data does not match the one from the proof");
1963
+ isCorrect = false;
1964
+ queryResultErrors.bind.commitment = {
1965
+ expected: `Commitment: ${calculatedParamCommitment}`,
1966
+ received: `Commitment: ${paramCommittment}`,
1967
+ message: "The bound data does not match the one from the proof",
1968
+ };
1969
+ }
1970
+ const { isCorrect: isCorrectBind, queryResultErrors: queryResultErrorsBind } = this.checkBindPublicInputs(queryResult, bindCommittedInputs.data);
1971
+ isCorrect = isCorrect && isCorrectBind;
1972
+ queryResultErrors = {
1973
+ ...queryResultErrors,
1974
+ ...queryResultErrorsBind,
1975
+ };
1976
+ uniqueIdentifier = (0, utils_1.getNullifierFromDisclosureProof)(proofData).toString(10);
1977
+ }
1871
1978
  }
1872
1979
  return { isCorrect, uniqueIdentifier, queryResultErrors };
1873
1980
  }
@@ -1983,7 +2090,7 @@ class ZKPassport {
1983
2090
  if (network === "ethereum_sepolia") {
1984
2091
  return {
1985
2092
  ...baseConfig,
1986
- address: "0xDfE02DFd5c208854884B58bFf6522De5c42F73E3",
2093
+ address: "0x5e4B11F7B7995F5Cee0134692a422b045091112F",
1987
2094
  };
1988
2095
  }
1989
2096
  else if (network === "local_anvil") {
@@ -2076,6 +2183,14 @@ class ZKPassport {
2076
2183
  value.discloseMask.map((x) => x.toString(16).padStart(2, "0")).join("") +
2077
2184
  value.disclosedBytes.map((x) => x.toString(16).padStart(2, "0")).join("");
2078
2185
  }
2186
+ else if (circuitName === "bind_evm") {
2187
+ const value = proof.committedInputs[circuitName];
2188
+ compressedCommittedInputs =
2189
+ utils_1.ProofType.BIND.toString(16).padStart(2, "0") +
2190
+ (0, utils_1.rightPadArrayWithZeros)((0, utils_1.formatBoundData)(value.data), 500)
2191
+ .map((x) => x.toString(16).padStart(2, "0"))
2192
+ .join("");
2193
+ }
2079
2194
  else {
2080
2195
  throw new Error(`Unsupported circuit for EVM verification: ${circuitName}`);
2081
2196
  }