@zkproofport-app/sdk 0.2.6 → 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/LICENSE +1 -1
- package/README.md +140 -3
- package/dist/ProofportSDK.d.ts +35 -0
- package/dist/constants.d.ts +37 -3
- package/dist/deeplink.d.ts +16 -15
- package/dist/index.d.ts +3 -3
- package/dist/index.esm.js +255 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +256 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +255 -5
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +64 -2
- package/dist/verifier.d.ts +17 -0
- package/package.json +2 -2
package/dist/index.esm.js
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).
|
|
@@ -226,14 +276,18 @@ const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
|
|
|
226
276
|
const OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT = {
|
|
227
277
|
PUBKEY_MODULUS_START: 0,
|
|
228
278
|
PUBKEY_MODULUS_END: 17,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
279
|
+
DOMAIN_STORAGE_START: 18,
|
|
280
|
+
DOMAIN_STORAGE_END: 81,
|
|
281
|
+
DOMAIN_LEN: 82,
|
|
232
282
|
SCOPE_START: 83,
|
|
233
283
|
SCOPE_END: 114,
|
|
234
284
|
NULLIFIER_START: 115,
|
|
235
285
|
NULLIFIER_END: 146,
|
|
236
286
|
PROVIDER: 147,
|
|
287
|
+
/** @deprecated Use DOMAIN_STORAGE_START */
|
|
288
|
+
DOMAIN_START: 18,
|
|
289
|
+
/** @deprecated Use DOMAIN_LEN */
|
|
290
|
+
DOMAIN_END: 82,
|
|
237
291
|
};
|
|
238
292
|
|
|
239
293
|
/**
|
|
@@ -480,6 +534,78 @@ function isProofportDeepLink(url, scheme = DEFAULT_SCHEME) {
|
|
|
480
534
|
* @param request - Proof request to validate
|
|
481
535
|
* @returns Validation result with `valid` flag and optional `error` message
|
|
482
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
|
+
}
|
|
483
609
|
function validateProofRequest(request) {
|
|
484
610
|
if (!request.requestId) {
|
|
485
611
|
return { valid: false, error: 'Missing requestId' };
|
|
@@ -487,7 +613,14 @@ function validateProofRequest(request) {
|
|
|
487
613
|
if (!request.circuit) {
|
|
488
614
|
return { valid: false, error: 'Missing circuit type' };
|
|
489
615
|
}
|
|
490
|
-
if (![
|
|
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)) {
|
|
491
624
|
return { valid: false, error: `Invalid circuit type: ${request.circuit}` };
|
|
492
625
|
}
|
|
493
626
|
if (!request.callbackUrl) {
|
|
@@ -529,6 +662,14 @@ function validateProofRequest(request) {
|
|
|
529
662
|
return { valid: false, error: 'provider must be a non-empty string when specified' };
|
|
530
663
|
}
|
|
531
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
|
+
}
|
|
532
673
|
// Check expiry
|
|
533
674
|
if (request.expiresAt && Date.now() > request.expiresAt) {
|
|
534
675
|
return { valid: false, error: 'Request has expired' };
|
|
@@ -3834,6 +3975,11 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
|
|
|
3834
3975
|
start = 83;
|
|
3835
3976
|
end = 114;
|
|
3836
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
|
+
}
|
|
3837
3983
|
else {
|
|
3838
3984
|
start = 64;
|
|
3839
3985
|
end = 95;
|
|
@@ -3871,6 +4017,11 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
|
|
|
3871
4017
|
start = 115;
|
|
3872
4018
|
end = 146;
|
|
3873
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
|
+
}
|
|
3874
4025
|
else {
|
|
3875
4026
|
start = 96;
|
|
3876
4027
|
end = 127;
|
|
@@ -3880,6 +4031,38 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
|
|
|
3880
4031
|
const nullifierFields = publicInputsHex.slice(start, end + 1);
|
|
3881
4032
|
return reconstructBytes32FromFields(nullifierFields);
|
|
3882
4033
|
}
|
|
4034
|
+
/**
|
|
4035
|
+
* Extract domain string from OIDC Domain Attestation public inputs.
|
|
4036
|
+
*
|
|
4037
|
+
* The domain is stored as a Noir BoundedVec<u8, 64>, which serializes as
|
|
4038
|
+
* [storage[0..64], len]. Each storage element is a u8 value in a field element.
|
|
4039
|
+
*
|
|
4040
|
+
* @param publicInputsHex - Array of public input hex strings
|
|
4041
|
+
* @param circuit - Circuit identifier (must be 'oidc_domain_attestation')
|
|
4042
|
+
* @returns Domain as ASCII string, or null if circuit doesn't match or inputs are insufficient
|
|
4043
|
+
*
|
|
4044
|
+
* @example
|
|
4045
|
+
* ```typescript
|
|
4046
|
+
* const domain = extractDomainFromPublicInputs(publicInputs, 'oidc_domain_attestation');
|
|
4047
|
+
* console.log(domain); // 'example.com'
|
|
4048
|
+
* ```
|
|
4049
|
+
*/
|
|
4050
|
+
function extractDomainFromPublicInputs(publicInputsHex, circuit) {
|
|
4051
|
+
if (circuit !== 'oidc_domain_attestation')
|
|
4052
|
+
return null;
|
|
4053
|
+
const layout = OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT;
|
|
4054
|
+
if (publicInputsHex.length <= layout.DOMAIN_LEN)
|
|
4055
|
+
return null;
|
|
4056
|
+
const len = Number(BigInt(publicInputsHex[layout.DOMAIN_LEN]) & 0xffn);
|
|
4057
|
+
if (len === 0 || len > 64)
|
|
4058
|
+
return null;
|
|
4059
|
+
const storageFields = publicInputsHex.slice(layout.DOMAIN_STORAGE_START, layout.DOMAIN_STORAGE_START + len);
|
|
4060
|
+
const chars = storageFields.map(f => {
|
|
4061
|
+
const byte = Number(BigInt(f) & 0xffn);
|
|
4062
|
+
return String.fromCharCode(byte);
|
|
4063
|
+
});
|
|
4064
|
+
return chars.join('');
|
|
4065
|
+
}
|
|
3883
4066
|
/** @internal Reconstruct a bytes32 value from 32 individual field elements */
|
|
3884
4067
|
function reconstructBytes32FromFields(fields) {
|
|
3885
4068
|
if (fields.length !== 32) {
|
|
@@ -4106,10 +4289,48 @@ class ProofportSDK {
|
|
|
4106
4289
|
if (circuit === 'coinbase_country_attestation') {
|
|
4107
4290
|
return this.createCoinbaseCountryRequest(inputs, options);
|
|
4108
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
|
+
}
|
|
4109
4297
|
else {
|
|
4110
4298
|
return this.createCoinbaseKycRequest(inputs, options);
|
|
4111
4299
|
}
|
|
4112
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
|
+
}
|
|
4113
4334
|
// ============ Deep Link Generation ============
|
|
4114
4335
|
/**
|
|
4115
4336
|
* @internal
|
|
@@ -4770,6 +4991,14 @@ class ProofportSDK {
|
|
|
4770
4991
|
if (needsSignature && !this.signer) {
|
|
4771
4992
|
throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
|
|
4772
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
|
+
}
|
|
4773
5002
|
// Get challenge + requestId from relay
|
|
4774
5003
|
const { requestId, challenge } = await this.getChallenge();
|
|
4775
5004
|
const body = {
|
|
@@ -5057,6 +5286,27 @@ class ProofportSDK {
|
|
|
5057
5286
|
extractNullifier(publicInputs, circuit) {
|
|
5058
5287
|
return extractNullifierFromPublicInputs(publicInputs, circuit);
|
|
5059
5288
|
}
|
|
5289
|
+
/**
|
|
5290
|
+
* Extracts the domain string from OIDC Domain Attestation proof public inputs.
|
|
5291
|
+
*
|
|
5292
|
+
* Only works with 'oidc_domain_attestation' circuit. Returns null for other circuits.
|
|
5293
|
+
*
|
|
5294
|
+
* @param publicInputs - Array of hex-encoded field elements from proof result
|
|
5295
|
+
* @param circuit - Circuit type that produced the public inputs
|
|
5296
|
+
* @returns Domain as ASCII string (e.g., 'example.com'), or null if not applicable
|
|
5297
|
+
*
|
|
5298
|
+
* @example
|
|
5299
|
+
* ```typescript
|
|
5300
|
+
* const result = await sdk.waitForProof(relay.requestId);
|
|
5301
|
+
* if (result.status === 'completed') {
|
|
5302
|
+
* const domain = sdk.extractDomain(result.publicInputs, result.circuit);
|
|
5303
|
+
* console.log('Domain:', domain); // 'example.com'
|
|
5304
|
+
* }
|
|
5305
|
+
* ```
|
|
5306
|
+
*/
|
|
5307
|
+
extractDomain(publicInputs, circuit) {
|
|
5308
|
+
return extractDomainFromPublicInputs(publicInputs, circuit);
|
|
5309
|
+
}
|
|
5060
5310
|
}
|
|
5061
5311
|
/**
|
|
5062
5312
|
* Creates a proof request through the relay server.
|
|
@@ -5095,5 +5345,5 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
|
|
|
5095
5345
|
'coinbase_country_attestation',
|
|
5096
5346
|
];
|
|
5097
5347
|
|
|
5098
|
-
export { COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT, COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT, OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT, ProofportSDK, ProofportSDK as default, 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 };
|
|
5099
5349
|
//# sourceMappingURL=index.esm.js.map
|