@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.js
CHANGED
|
@@ -78,6 +78,26 @@ const CIRCUIT_METADATA = {
|
|
|
78
78
|
publicInputsCount: 148,
|
|
79
79
|
publicInputNames: ['pubkey_modulus_limbs', 'domain', 'scope', 'nullifier', 'provider'],
|
|
80
80
|
},
|
|
81
|
+
// Korea Mobile ID (mDL) circuits. Counts are byte-flattened field totals
|
|
82
|
+
// from the compiled circuit ABIs: [u8; 32] arrays contribute 32 inputs each.
|
|
83
|
+
mdl_kr_ownership: {
|
|
84
|
+
name: 'Korea Mobile ID — Ownership',
|
|
85
|
+
description: "Prove valid Korean mobile driver's license ownership with selective disclosure",
|
|
86
|
+
publicInputsCount: 97,
|
|
87
|
+
publicInputNames: ['scope', 'nullifier_value', 'disclose_flags', 'owner_commit'],
|
|
88
|
+
},
|
|
89
|
+
mdl_kr_age: {
|
|
90
|
+
name: 'Korea Mobile ID — Age',
|
|
91
|
+
description: "Prove minimum age from Korean mobile driver's license without revealing birth date",
|
|
92
|
+
publicInputsCount: 66,
|
|
93
|
+
publicInputNames: ['scope', 'nullifier_value', 'age_threshold', 'current_year'],
|
|
94
|
+
},
|
|
95
|
+
mdl_kr_region: {
|
|
96
|
+
name: 'Korea Mobile ID — Region',
|
|
97
|
+
description: "Prove si/do residency from Korean mobile driver's license without revealing address",
|
|
98
|
+
publicInputsCount: 96,
|
|
99
|
+
publicInputNames: ['scope', 'nullifier_value', 'region_code'],
|
|
100
|
+
},
|
|
81
101
|
};
|
|
82
102
|
/**
|
|
83
103
|
* Standard verifier contract ABI shared across all Barretenberg-generated verifiers.
|
|
@@ -209,6 +229,36 @@ const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
|
|
|
209
229
|
NULLIFIER_START: 118,
|
|
210
230
|
NULLIFIER_END: 149,
|
|
211
231
|
};
|
|
232
|
+
/**
|
|
233
|
+
* Korea Mobile ID (mDL) circuit public input layout (field offsets).
|
|
234
|
+
* All three mDL circuits share the same prefix: scope [0..31] and
|
|
235
|
+
* nullifier_value [32..63], each stored one byte per field element.
|
|
236
|
+
* The suffix differs per predicate circuit:
|
|
237
|
+
* - mdl_kr_ownership (97 inputs): disclose_flags [64], owner_commit [65..96]
|
|
238
|
+
* - mdl_kr_age (66 inputs): age_threshold [64], current_year [65]
|
|
239
|
+
* - mdl_kr_region (96 inputs): region_code [64..95]
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const nullifier = publicInputs.slice(
|
|
244
|
+
* MDL_KR_PUBLIC_INPUT_LAYOUT.NULLIFIER_START,
|
|
245
|
+
* MDL_KR_PUBLIC_INPUT_LAYOUT.NULLIFIER_END + 1
|
|
246
|
+
* );
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
const MDL_KR_PUBLIC_INPUT_LAYOUT = {
|
|
250
|
+
SCOPE_START: 0,
|
|
251
|
+
SCOPE_END: 31,
|
|
252
|
+
NULLIFIER_START: 32,
|
|
253
|
+
NULLIFIER_END: 63,
|
|
254
|
+
OWNERSHIP_DISCLOSE_FLAGS: 64,
|
|
255
|
+
OWNERSHIP_OWNER_COMMIT_START: 65,
|
|
256
|
+
OWNERSHIP_OWNER_COMMIT_END: 96,
|
|
257
|
+
AGE_THRESHOLD: 64,
|
|
258
|
+
AGE_CURRENT_YEAR: 65,
|
|
259
|
+
REGION_CODE_START: 64,
|
|
260
|
+
REGION_CODE_END: 95,
|
|
261
|
+
};
|
|
212
262
|
/**
|
|
213
263
|
* OIDC Domain Attestation circuit public input layout (field offsets).
|
|
214
264
|
* Defines the field positions in the flattened public inputs array (148 fields total).
|
|
@@ -231,14 +281,18 @@ const COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = {
|
|
|
231
281
|
const OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT = {
|
|
232
282
|
PUBKEY_MODULUS_START: 0,
|
|
233
283
|
PUBKEY_MODULUS_END: 17,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
284
|
+
DOMAIN_STORAGE_START: 18,
|
|
285
|
+
DOMAIN_STORAGE_END: 81,
|
|
286
|
+
DOMAIN_LEN: 82,
|
|
237
287
|
SCOPE_START: 83,
|
|
238
288
|
SCOPE_END: 114,
|
|
239
289
|
NULLIFIER_START: 115,
|
|
240
290
|
NULLIFIER_END: 146,
|
|
241
291
|
PROVIDER: 147,
|
|
292
|
+
/** @deprecated Use DOMAIN_STORAGE_START */
|
|
293
|
+
DOMAIN_START: 18,
|
|
294
|
+
/** @deprecated Use DOMAIN_LEN */
|
|
295
|
+
DOMAIN_END: 82,
|
|
242
296
|
};
|
|
243
297
|
|
|
244
298
|
/**
|
|
@@ -485,6 +539,78 @@ function isProofportDeepLink(url, scheme = DEFAULT_SCHEME) {
|
|
|
485
539
|
* @param request - Proof request to validate
|
|
486
540
|
* @returns Validation result with `valid` flag and optional `error` message
|
|
487
541
|
*/
|
|
542
|
+
/**
|
|
543
|
+
* Validates a free-text mDL input (scope / targetRegion).
|
|
544
|
+
* Rejects empty or whitespace-only values, control characters (which break
|
|
545
|
+
* deep-link query encoding and downstream logging), and values over 256 chars
|
|
546
|
+
* (server-side length cap mirror). UTF-8 text such as Korean region names is
|
|
547
|
+
* allowed.
|
|
548
|
+
*
|
|
549
|
+
* @returns Error message string, or null when valid.
|
|
550
|
+
*/
|
|
551
|
+
function validateMdlTextInput(value, fieldName) {
|
|
552
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
553
|
+
return `${fieldName} is required and must be a non-empty string`;
|
|
554
|
+
}
|
|
555
|
+
if (value.length > 256) {
|
|
556
|
+
return `${fieldName} must be 256 characters or fewer`;
|
|
557
|
+
}
|
|
558
|
+
// eslint-disable-next-line no-control-regex
|
|
559
|
+
if (/[\u0000-\u001F\u007F]/.test(value)) {
|
|
560
|
+
return `${fieldName} must not contain control characters`;
|
|
561
|
+
}
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Validates dapp-supplied inputs for the Korea Mobile ID (mDL) circuits.
|
|
566
|
+
* Shared by deep-link request validation and relay request creation so the
|
|
567
|
+
* same guards apply on every request path.
|
|
568
|
+
*
|
|
569
|
+
* Rules:
|
|
570
|
+
* - `scope` — required non-empty string, ≤256 chars, no control characters
|
|
571
|
+
* - `mdl_kr_ownership.discloseFlags` — optional integer 0–15 (0x0F bitmask)
|
|
572
|
+
* - `mdl_kr_age.ageThreshold` — required integer 1–150
|
|
573
|
+
* - `mdl_kr_region.targetRegion` — required non-empty string (Korean si/do),
|
|
574
|
+
* ≤256 chars, no control characters
|
|
575
|
+
*
|
|
576
|
+
* @param circuit - One of the three mDL circuit identifiers
|
|
577
|
+
* @param inputs - Dapp-supplied circuit inputs
|
|
578
|
+
* @returns Error message string, or null when the inputs are valid
|
|
579
|
+
*/
|
|
580
|
+
function validateMdlInputs(circuit, inputs) {
|
|
581
|
+
const scopeError = validateMdlTextInput(inputs.scope, 'scope');
|
|
582
|
+
if (scopeError) {
|
|
583
|
+
return scopeError;
|
|
584
|
+
}
|
|
585
|
+
if (circuit === 'mdl_kr_ownership') {
|
|
586
|
+
const { discloseFlags } = inputs;
|
|
587
|
+
if (discloseFlags !== undefined) {
|
|
588
|
+
if (typeof discloseFlags !== 'number' ||
|
|
589
|
+
!Number.isInteger(discloseFlags) ||
|
|
590
|
+
discloseFlags < 0 ||
|
|
591
|
+
discloseFlags > 0x0f) {
|
|
592
|
+
return 'discloseFlags must be an integer between 0 and 15';
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
else if (circuit === 'mdl_kr_age') {
|
|
597
|
+
const { ageThreshold } = inputs;
|
|
598
|
+
if (typeof ageThreshold !== 'number' ||
|
|
599
|
+
!Number.isInteger(ageThreshold) ||
|
|
600
|
+
ageThreshold < 1 ||
|
|
601
|
+
ageThreshold > 150) {
|
|
602
|
+
return 'ageThreshold is required and must be an integer between 1 and 150';
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const { targetRegion } = inputs;
|
|
607
|
+
const regionError = validateMdlTextInput(targetRegion, 'targetRegion');
|
|
608
|
+
if (regionError) {
|
|
609
|
+
return regionError;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
488
614
|
function validateProofRequest(request) {
|
|
489
615
|
if (!request.requestId) {
|
|
490
616
|
return { valid: false, error: 'Missing requestId' };
|
|
@@ -492,7 +618,14 @@ function validateProofRequest(request) {
|
|
|
492
618
|
if (!request.circuit) {
|
|
493
619
|
return { valid: false, error: 'Missing circuit type' };
|
|
494
620
|
}
|
|
495
|
-
if (![
|
|
621
|
+
if (![
|
|
622
|
+
'coinbase_attestation',
|
|
623
|
+
'coinbase_country_attestation',
|
|
624
|
+
'oidc_domain_attestation',
|
|
625
|
+
'mdl_kr_ownership',
|
|
626
|
+
'mdl_kr_age',
|
|
627
|
+
'mdl_kr_region',
|
|
628
|
+
].includes(request.circuit)) {
|
|
496
629
|
return { valid: false, error: `Invalid circuit type: ${request.circuit}` };
|
|
497
630
|
}
|
|
498
631
|
if (!request.callbackUrl) {
|
|
@@ -534,6 +667,14 @@ function validateProofRequest(request) {
|
|
|
534
667
|
return { valid: false, error: 'provider must be a non-empty string when specified' };
|
|
535
668
|
}
|
|
536
669
|
}
|
|
670
|
+
else if (request.circuit === 'mdl_kr_ownership' ||
|
|
671
|
+
request.circuit === 'mdl_kr_age' ||
|
|
672
|
+
request.circuit === 'mdl_kr_region') {
|
|
673
|
+
const mdlError = validateMdlInputs(request.circuit, request.inputs);
|
|
674
|
+
if (mdlError) {
|
|
675
|
+
return { valid: false, error: mdlError };
|
|
676
|
+
}
|
|
677
|
+
}
|
|
537
678
|
// Check expiry
|
|
538
679
|
if (request.expiresAt && Date.now() > request.expiresAt) {
|
|
539
680
|
return { valid: false, error: 'Request has expired' };
|
|
@@ -972,6 +1113,11 @@ function extractScopeFromPublicInputs(publicInputsHex, circuit) {
|
|
|
972
1113
|
start = 83;
|
|
973
1114
|
end = 114;
|
|
974
1115
|
}
|
|
1116
|
+
else if (circuit !== undefined && circuit.startsWith('mdl_kr_')) {
|
|
1117
|
+
// All Korea mDL circuits: scope [0..31], nullifier_value [32..63]
|
|
1118
|
+
start = 0;
|
|
1119
|
+
end = 31;
|
|
1120
|
+
}
|
|
975
1121
|
else {
|
|
976
1122
|
start = 64;
|
|
977
1123
|
end = 95;
|
|
@@ -1009,6 +1155,11 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
|
|
|
1009
1155
|
start = 115;
|
|
1010
1156
|
end = 146;
|
|
1011
1157
|
}
|
|
1158
|
+
else if (circuit !== undefined && circuit.startsWith('mdl_kr_')) {
|
|
1159
|
+
// All Korea mDL circuits: scope [0..31], nullifier_value [32..63]
|
|
1160
|
+
start = 32;
|
|
1161
|
+
end = 63;
|
|
1162
|
+
}
|
|
1012
1163
|
else {
|
|
1013
1164
|
start = 96;
|
|
1014
1165
|
end = 127;
|
|
@@ -1018,6 +1169,38 @@ function extractNullifierFromPublicInputs(publicInputsHex, circuit) {
|
|
|
1018
1169
|
const nullifierFields = publicInputsHex.slice(start, end + 1);
|
|
1019
1170
|
return reconstructBytes32FromFields(nullifierFields);
|
|
1020
1171
|
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Extract domain string from OIDC Domain Attestation public inputs.
|
|
1174
|
+
*
|
|
1175
|
+
* The domain is stored as a Noir BoundedVec<u8, 64>, which serializes as
|
|
1176
|
+
* [storage[0..64], len]. Each storage element is a u8 value in a field element.
|
|
1177
|
+
*
|
|
1178
|
+
* @param publicInputsHex - Array of public input hex strings
|
|
1179
|
+
* @param circuit - Circuit identifier (must be 'oidc_domain_attestation')
|
|
1180
|
+
* @returns Domain as ASCII string, or null if circuit doesn't match or inputs are insufficient
|
|
1181
|
+
*
|
|
1182
|
+
* @example
|
|
1183
|
+
* ```typescript
|
|
1184
|
+
* const domain = extractDomainFromPublicInputs(publicInputs, 'oidc_domain_attestation');
|
|
1185
|
+
* console.log(domain); // 'example.com'
|
|
1186
|
+
* ```
|
|
1187
|
+
*/
|
|
1188
|
+
function extractDomainFromPublicInputs(publicInputsHex, circuit) {
|
|
1189
|
+
if (circuit !== 'oidc_domain_attestation')
|
|
1190
|
+
return null;
|
|
1191
|
+
const layout = OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT;
|
|
1192
|
+
if (publicInputsHex.length <= layout.DOMAIN_LEN)
|
|
1193
|
+
return null;
|
|
1194
|
+
const len = Number(BigInt(publicInputsHex[layout.DOMAIN_LEN]) & 0xffn);
|
|
1195
|
+
if (len === 0 || len > 64)
|
|
1196
|
+
return null;
|
|
1197
|
+
const storageFields = publicInputsHex.slice(layout.DOMAIN_STORAGE_START, layout.DOMAIN_STORAGE_START + len);
|
|
1198
|
+
const chars = storageFields.map(f => {
|
|
1199
|
+
const byte = Number(BigInt(f) & 0xffn);
|
|
1200
|
+
return String.fromCharCode(byte);
|
|
1201
|
+
});
|
|
1202
|
+
return chars.join('');
|
|
1203
|
+
}
|
|
1021
1204
|
/** @internal Reconstruct a bytes32 value from 32 individual field elements */
|
|
1022
1205
|
function reconstructBytes32FromFields(fields) {
|
|
1023
1206
|
if (fields.length !== 32) {
|
|
@@ -1244,10 +1427,48 @@ class ProofportSDK {
|
|
|
1244
1427
|
if (circuit === 'coinbase_country_attestation') {
|
|
1245
1428
|
return this.createCoinbaseCountryRequest(inputs, options);
|
|
1246
1429
|
}
|
|
1430
|
+
else if (circuit === 'mdl_kr_ownership' ||
|
|
1431
|
+
circuit === 'mdl_kr_age' ||
|
|
1432
|
+
circuit === 'mdl_kr_region') {
|
|
1433
|
+
return this.createMdlKrRequest(circuit, inputs, options);
|
|
1434
|
+
}
|
|
1247
1435
|
else {
|
|
1248
1436
|
return this.createCoinbaseKycRequest(inputs, options);
|
|
1249
1437
|
}
|
|
1250
1438
|
}
|
|
1439
|
+
/**
|
|
1440
|
+
* @internal
|
|
1441
|
+
* Creates a Korea Mobile ID (mDL) proof request for any of the three
|
|
1442
|
+
* mDL predicate circuits (ownership / age / region).
|
|
1443
|
+
*
|
|
1444
|
+
* The mobile app collects the license data on-device; the dapp only
|
|
1445
|
+
* supplies the predicate parameters validated here. No wallet signature
|
|
1446
|
+
* is required for mDL circuits.
|
|
1447
|
+
*
|
|
1448
|
+
* @param circuit - 'mdl_kr_ownership' | 'mdl_kr_age' | 'mdl_kr_region'
|
|
1449
|
+
* @param inputs - Predicate inputs (see MdlKr*Inputs types)
|
|
1450
|
+
* @param options - Request configuration options
|
|
1451
|
+
*
|
|
1452
|
+
* @throws Error if the predicate inputs fail validation
|
|
1453
|
+
*/
|
|
1454
|
+
createMdlKrRequest(circuit, inputs, options = {}) {
|
|
1455
|
+
const mdlError = validateMdlInputs(circuit, inputs);
|
|
1456
|
+
if (mdlError) {
|
|
1457
|
+
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
1458
|
+
}
|
|
1459
|
+
const request = {
|
|
1460
|
+
requestId: generateRequestId(),
|
|
1461
|
+
circuit,
|
|
1462
|
+
inputs,
|
|
1463
|
+
message: options.message,
|
|
1464
|
+
dappName: options.dappName,
|
|
1465
|
+
dappIcon: options.dappIcon,
|
|
1466
|
+
createdAt: Date.now(),
|
|
1467
|
+
expiresAt: Date.now() + (options.expiresInMs || DEFAULT_REQUEST_EXPIRY_MS),
|
|
1468
|
+
};
|
|
1469
|
+
this.pendingRequests.set(request.requestId, request);
|
|
1470
|
+
return request;
|
|
1471
|
+
}
|
|
1251
1472
|
// ============ Deep Link Generation ============
|
|
1252
1473
|
/**
|
|
1253
1474
|
* @internal
|
|
@@ -1908,6 +2129,14 @@ class ProofportSDK {
|
|
|
1908
2129
|
if (needsSignature && !this.signer) {
|
|
1909
2130
|
throw new Error('Signer not set. Call setSigner() first. Wallet signature is required for this circuit.');
|
|
1910
2131
|
}
|
|
2132
|
+
// mDL circuits: validate predicate inputs client-side before hitting the
|
|
2133
|
+
// relay, so a bad ageThreshold / targetRegion fails fast with a clear error.
|
|
2134
|
+
if (circuit === 'mdl_kr_ownership' || circuit === 'mdl_kr_age' || circuit === 'mdl_kr_region') {
|
|
2135
|
+
const mdlError = validateMdlInputs(circuit, inputs);
|
|
2136
|
+
if (mdlError) {
|
|
2137
|
+
throw new Error(`${mdlError} (circuit: ${circuit})`);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
1911
2140
|
// Get challenge + requestId from relay
|
|
1912
2141
|
const { requestId, challenge } = await this.getChallenge();
|
|
1913
2142
|
const body = {
|
|
@@ -2195,6 +2424,27 @@ class ProofportSDK {
|
|
|
2195
2424
|
extractNullifier(publicInputs, circuit) {
|
|
2196
2425
|
return extractNullifierFromPublicInputs(publicInputs, circuit);
|
|
2197
2426
|
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Extracts the domain string from OIDC Domain Attestation proof public inputs.
|
|
2429
|
+
*
|
|
2430
|
+
* Only works with 'oidc_domain_attestation' circuit. Returns null for other circuits.
|
|
2431
|
+
*
|
|
2432
|
+
* @param publicInputs - Array of hex-encoded field elements from proof result
|
|
2433
|
+
* @param circuit - Circuit type that produced the public inputs
|
|
2434
|
+
* @returns Domain as ASCII string (e.g., 'example.com'), or null if not applicable
|
|
2435
|
+
*
|
|
2436
|
+
* @example
|
|
2437
|
+
* ```typescript
|
|
2438
|
+
* const result = await sdk.waitForProof(relay.requestId);
|
|
2439
|
+
* if (result.status === 'completed') {
|
|
2440
|
+
* const domain = sdk.extractDomain(result.publicInputs, result.circuit);
|
|
2441
|
+
* console.log('Domain:', domain); // 'example.com'
|
|
2442
|
+
* }
|
|
2443
|
+
* ```
|
|
2444
|
+
*/
|
|
2445
|
+
extractDomain(publicInputs, circuit) {
|
|
2446
|
+
return extractDomainFromPublicInputs(publicInputs, circuit);
|
|
2447
|
+
}
|
|
2198
2448
|
}
|
|
2199
2449
|
/**
|
|
2200
2450
|
* Creates a proof request through the relay server.
|
|
@@ -2235,9 +2485,11 @@ ProofportSDK.WALLET_SIGNATURE_CIRCUITS = [
|
|
|
2235
2485
|
|
|
2236
2486
|
exports.COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT = COINBASE_ATTESTATION_PUBLIC_INPUT_LAYOUT;
|
|
2237
2487
|
exports.COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT = COINBASE_COUNTRY_PUBLIC_INPUT_LAYOUT;
|
|
2488
|
+
exports.MDL_KR_PUBLIC_INPUT_LAYOUT = MDL_KR_PUBLIC_INPUT_LAYOUT;
|
|
2238
2489
|
exports.OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT = OIDC_DOMAIN_ATTESTATION_PUBLIC_INPUT_LAYOUT;
|
|
2239
2490
|
exports.ProofportSDK = ProofportSDK;
|
|
2240
2491
|
exports.default = ProofportSDK;
|
|
2492
|
+
exports.extractDomainFromPublicInputs = extractDomainFromPublicInputs;
|
|
2241
2493
|
exports.extractNullifierFromPublicInputs = extractNullifierFromPublicInputs;
|
|
2242
2494
|
exports.extractScopeFromPublicInputs = extractScopeFromPublicInputs;
|
|
2243
2495
|
//# sourceMappingURL=index.js.map
|