@zkproofport-app/sdk 0.2.7 → 0.2.8

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/index.mjs CHANGED
@@ -73,6 +73,26 @@ const CIRCUIT_METADATA = {
73
73
  publicInputsCount: 148,
74
74
  publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
75
75
  },
76
+ // Korea Mobile ID (mDL) circuits. Counts are byte-flattened field totals
77
+ // from the compiled circuit ABIs: [u8; 32] arrays contribute 32 inputs each.
78
+ mdl_kr_ownership: {
79
+ name: 'Korea Mobile ID — Ownership',
80
+ description: "Prove valid Korean mobile driver's license ownership with selective disclosure",
81
+ publicInputsCount: 97,
82
+ publicInputNames: ['scope', 'nullifier_value', 'disclose_flags', 'owner_commit'],
83
+ },
84
+ mdl_kr_age: {
85
+ name: 'Korea Mobile ID — Age',
86
+ description: "Prove minimum age from Korean mobile driver's license without revealing birth date",
87
+ publicInputsCount: 66,
88
+ publicInputNames: ['scope', 'nullifier_value', 'age_threshold', 'current_year'],
89
+ },
90
+ mdl_kr_region: {
91
+ name: 'Korea Mobile ID — Region',
92
+ description: "Prove si/do residency from Korean mobile driver's license without revealing address",
93
+ publicInputsCount: 96,
94
+ publicInputNames: ['scope', 'nullifier_value', 'region_code'],
95
+ },
76
96
  };
77
97
  /**
78
98
  * Standard verifier contract ABI shared across all Barretenberg-generated verifiers.
@@ -204,6 +224,36 @@ const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
204
224
  NULLIFIER_START: 118,
205
225
  NULLIFIER_END: 149,
206
226
  };
227
+ /**
228
+ * Korea Mobile ID (mDL) circuit public input layout (field offsets).
229
+ * All three mDL circuits share the same prefix: scope [0..31] and
230
+ * nullifier_value [32..63], each stored one byte per field element.
231
+ * The suffix differs per predicate circuit:
232
+ * - mdl_kr_ownership (97 inputs): disclose_flags [64], owner_commit [65..96]
233
+ * - mdl_kr_age (66 inputs): age_threshold [64], current_year [65]
234
+ * - mdl_kr_region (96 inputs): region_code [64..95]
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const nullifier = publicInputs.slice(
239
+ * MDL_KR_PUBLIC_INPUT_LAYOUT.NULLIFIER_START,
240
+ * MDL_KR_PUBLIC_INPUT_LAYOUT.NULLIFIER_END + 1
241
+ * );
242
+ * ```
243
+ */
244
+ const MDL_KR_PUBLIC_INPUT_LAYOUT = {
245
+ SCOPE_START: 0,
246
+ SCOPE_END: 31,
247
+ NULLIFIER_START: 32,
248
+ NULLIFIER_END: 63,
249
+ OWNERSHIP_DISCLOSE_FLAGS: 64,
250
+ OWNERSHIP_OWNER_COMMIT_START: 65,
251
+ OWNERSHIP_OWNER_COMMIT_END: 96,
252
+ AGE_THRESHOLD: 64,
253
+ AGE_CURRENT_YEAR: 65,
254
+ REGION_CODE_START: 64,
255
+ REGION_CODE_END: 95,
256
+ };
207
257
  /**
208
258
  * OIDC Domain Attestation circuit public input layout (field offsets).
209
259
  * Defines the field positions in the flattened public inputs array (148 fields total).
@@ -484,6 +534,78 @@ function isProofportDeepLink(url, scheme = DEFAULT_SCHEME) {
484
534
  * @param request - Proof request to validate
485
535
  * @returns Validation result with `valid` flag and optional `error` message
486
536
  */
537
+ /**
538
+ * Validates a free-text mDL input (scope / targetRegion).
539
+ * Rejects empty or whitespace-only values, control characters (which break
540
+ * deep-link query encoding and downstream logging), and values over 256 chars
541
+ * (server-side length cap mirror). UTF-8 text such as Korean region names is
542
+ * allowed.
543
+ *
544
+ * @returns Error message string, or null when valid.
545
+ */
546
+ function validateMdlTextInput(value, fieldName) {
547
+ if (typeof value !== 'string' || value.trim() === '') {
548
+ return `${fieldName} is required and must be a non-empty string`;
549
+ }
550
+ if (value.length > 256) {
551
+ return `${fieldName} must be 256 characters or fewer`;
552
+ }
553
+ // eslint-disable-next-line no-control-regex
554
+ if (/[\u0000-\u001F\u007F]/.test(value)) {
555
+ return `${fieldName} must not contain control characters`;
556
+ }
557
+ return null;
558
+ }
559
+ /**
560
+ * Validates dapp-supplied inputs for the Korea Mobile ID (mDL) circuits.
561
+ * Shared by deep-link request validation and relay request creation so the
562
+ * same guards apply on every request path.
563
+ *
564
+ * Rules:
565
+ * - `scope` — required non-empty string, ≤256 chars, no control characters
566
+ * - `mdl_kr_ownership.discloseFlags` — optional integer 0–15 (0x0F bitmask)
567
+ * - `mdl_kr_age.ageThreshold` — required integer 1–150
568
+ * - `mdl_kr_region.targetRegion` — required non-empty string (Korean si/do),
569
+ * ≤256 chars, no control characters
570
+ *
571
+ * @param circuit - One of the three mDL circuit identifiers
572
+ * @param inputs - Dapp-supplied circuit inputs
573
+ * @returns Error message string, or null when the inputs are valid
574
+ */
575
+ function validateMdlInputs(circuit, inputs) {
576
+ const scopeError = validateMdlTextInput(inputs.scope, 'scope');
577
+ if (scopeError) {
578
+ return scopeError;
579
+ }
580
+ if (circuit === 'mdl_kr_ownership') {
581
+ const { discloseFlags } = inputs;
582
+ if (discloseFlags !== undefined) {
583
+ if (typeof discloseFlags !== 'number' ||
584
+ !Number.isInteger(discloseFlags) ||
585
+ discloseFlags < 0 ||
586
+ discloseFlags > 0x0f) {
587
+ return 'discloseFlags must be an integer between 0 and 15';
588
+ }
589
+ }
590
+ }
591
+ else if (circuit === 'mdl_kr_age') {
592
+ const { ageThreshold } = inputs;
593
+ if (typeof ageThreshold !== 'number' ||
594
+ !Number.isInteger(ageThreshold) ||
595
+ ageThreshold < 1 ||
596
+ ageThreshold > 150) {
597
+ return 'ageThreshold is required and must be an integer between 1 and 150';
598
+ }
599
+ }
600
+ else {
601
+ const { targetRegion } = inputs;
602
+ const regionError = validateMdlTextInput(targetRegion, 'targetRegion');
603
+ if (regionError) {
604
+ return regionError;
605
+ }
606
+ }
607
+ return null;
608
+ }
487
609
  function validateProofRequest(request) {
488
610
  if (!request.requestId) {
489
611
  return { valid: false, error: 'Missing requestId' };
@@ -491,7 +613,14 @@ function validateProofRequest(request) {
491
613
  if (!request.circuit) {
492
614
  return { valid: false, error: 'Missing circuit type' };
493
615
  }
494
- if (!['coinbase_attestation', 'coinbase_country_attestation', 'oidc_domain_attestation'].includes(request.circuit)) {
616
+ if (![
617
+ 'coinbase_attestation',
618
+ 'coinbase_country_attestation',
619
+ 'oidc_domain_attestation',
620
+ 'mdl_kr_ownership',
621
+ 'mdl_kr_age',
622
+ 'mdl_kr_region',
623
+ ].includes(request.circuit)) {
495
624
  return { valid: false, error: `Invalid circuit type: ${request.circuit}` };
496
625
  }
497
626
  if (!request.callbackUrl) {
@@ -533,6 +662,14 @@ function validateProofRequest(request) {
533
662
  return { valid: false, error: 'provider must be a non-empty string when specified' };
534
663
  }
535
664
  }
665
+ else if (request.circuit === 'mdl_kr_ownership' ||
666
+ request.circuit === 'mdl_kr_age' ||
667
+ request.circuit === 'mdl_kr_region') {
668
+ const mdlError = validateMdlInputs(request.circuit, request.inputs);
669
+ if (mdlError) {
670
+ return { valid: false, error: mdlError };
671
+ }
672
+ }
536
673
  // Check expiry
537
674
  if (request.expiresAt && Date.now() > request.expiresAt) {
538
675
  return { valid: false, error: 'Request has expired' };
@@ -3838,6 +3975,11 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
3838
3975
  start = 83;
3839
3976
  end = 114;
3840
3977
  }
3978
+ else if (circuit !== undefined && circuit.startsWith('mdl_kr_')) {
3979
+ // All Korea mDL circuits: scope [0..31], nullifier_value [32..63]
3980
+ start = 0;
3981
+ end = 31;
3982
+ }
3841
3983
  else {
3842
3984
  start = 64;
3843
3985
  end = 95;
@@ -3875,6 +4017,11 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
3875
4017
  start = 115;
3876
4018
  end = 146;
3877
4019
  }
4020
+ else if (circuit !== undefined && circuit.startsWith('mdl_kr_')) {
4021
+ // All Korea mDL circuits: scope [0..31], nullifier_value [32..63]
4022
+ start = 32;
4023
+ end = 63;
4024
+ }
3878
4025
  else {
3879
4026
  start = 96;
3880
4027
  end = 127;
@@ -4142,10 +4289,48 @@ class ProofportSDK {
4142
4289
  if (circuit === 'coinbase_country_attestation') {
4143
4290
  return this.createCoinbaseCountryRequest(inputs, options);
4144
4291
  }
4292
+ else if (circuit === 'mdl_kr_ownership' ||
4293
+ circuit === 'mdl_kr_age' ||
4294
+ circuit === 'mdl_kr_region') {
4295
+ return this.createMdlKrRequest(circuit, inputs, options);
4296
+ }
4145
4297
  else {
4146
4298
  return this.createCoinbaseKycRequest(inputs, options);
4147
4299
  }
4148
4300
  }
4301
+ /**
4302
+ * @internal
4303
+ * Creates a Korea Mobile ID (mDL) proof request for any of the three
4304
+ * mDL predicate circuits (ownership / age / region).
4305
+ *
4306
+ * The mobile app collects the license data on-device; the dapp only
4307
+ * supplies the predicate parameters validated here. No wallet signature
4308
+ * is required for mDL circuits.
4309
+ *
4310
+ * @param circuit - 'mdl_kr_ownership' | 'mdl_kr_age' | 'mdl_kr_region'
4311
+ * @param inputs - Predicate inputs (see MdlKr*Inputs types)
4312
+ * @param options - Request configuration options
4313
+ *
4314
+ * @throws Error if the predicate inputs fail validation
4315
+ */
4316
+ createMdlKrRequest(circuit, inputs, options = {}) {
4317
+ const mdlError = validateMdlInputs(circuit, inputs);
4318
+ if (mdlError) {
4319
+ throw new Error(`${mdlError} (circuit: ${circuit})`);
4320
+ }
4321
+ const request = {
4322
+ requestId: generateRequestId(),
4323
+ circuit,
4324
+ inputs,
4325
+ message: options.message,
4326
+ dappName: options.dappName,
4327
+ dappIcon: options.dappIcon,
4328
+ createdAt: Date.now(),
4329
+ expiresAt: Date.now() + (options.expiresInMs || DEFAULT_REQUEST_EXPIRY_MS),
4330
+ };
4331
+ this.pendingRequests.set(request.requestId, request);
4332
+ return request;
4333
+ }
4149
4334
  // ============ Deep Link Generation ============
4150
4335
  /**
4151
4336
  * @internal
@@ -4806,6 +4991,14 @@ class ProofportSDK {
4806
4991
  if (needsSignature && !this.signer) {
4807
4992
  throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
4808
4993
  }
4994
+ // mDL circuits: validate predicate inputs client-side before hitting the
4995
+ // relay, so a bad ageThreshold / targetRegion fails fast with a clear error.
4996
+ if (circuit === 'mdl_kr_ownership' || circuit === 'mdl_kr_age' || circuit === 'mdl_kr_region') {
4997
+ const mdlError = validateMdlInputs(circuit, inputs);
4998
+ if (mdlError) {
4999
+ throw new Error(`${mdlError} (circuit: ${circuit})`);
5000
+ }
5001
+ }
4809
5002
  // Get challenge + requestId from relay
4810
5003
  const { requestId, challenge } = await this.getChallenge();
4811
5004
  const body = {
@@ -5152,5 +5345,5 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
5152
5345
  'coinbase_country_attestation',
5153
5346
  ];
5154
5347
 
5155
- export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs };
5348
+ export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, MDL_KR_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, extractDomainFromPublicInputs, extractNullifierFromPublicInputs, extractScopeFromPublicInputs };
5156
5349
  //# sourceMappingURL=index.mjs.map