@cheny56/zk-kyc-did 1.0.0

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.
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Verify Credential Example
3
+ *
4
+ * Demonstrates on-chain verification of credential proofs.
5
+ *
6
+ * Run: node examples/verify-credential.js
7
+ */
8
+
9
+ const {
10
+ KYCClient,
11
+ Predicate,
12
+ PredicateOp,
13
+ } = require('../client/kyc-client');
14
+ const { VerifiableCredential, CredentialWallet } = require('../lib');
15
+
16
+ async function main() {
17
+ console.log('='.repeat(70));
18
+ console.log(' ZK KYC/DID - Verify Credential Example');
19
+ console.log('='.repeat(70));
20
+ console.log();
21
+
22
+ // ========================================
23
+ // Setup: Create credential
24
+ // ========================================
25
+ console.log('SETUP: Create Sample Credential');
26
+ console.log('-'.repeat(70));
27
+
28
+ const wallet = new CredentialWallet();
29
+ await wallet.init();
30
+
31
+ const credential = new VerifiableCredential({
32
+ issuer: '0x1234567890abcdef1234567890abcdef12345678',
33
+ subject: wallet.subjectDID,
34
+ claims: {
35
+ age: 25,
36
+ country: 'US',
37
+ verified: true,
38
+ },
39
+ type: 'KYC',
40
+ });
41
+
42
+ await credential.init();
43
+ credential.sign(0xabcdefn);
44
+ wallet.addCredential(credential);
45
+
46
+ console.log(` Credential created for: ${wallet.subjectDID}`);
47
+ console.log();
48
+
49
+ // ========================================
50
+ // Step 1: Initialize client
51
+ // ========================================
52
+ console.log('STEP 1: Initialize KYC Client');
53
+ console.log('-'.repeat(70));
54
+
55
+ // In a real scenario, you'd connect to a provider
56
+ const client = new KYCClient({
57
+ provider: null, // Would be ethers provider
58
+ signer: null, // Would be ethers signer
59
+ verifierAddress: '0x...', // Contract address
60
+ demoMode: true, // Skip actual proof generation
61
+ });
62
+
63
+ await client.init(wallet);
64
+
65
+ console.log(` Client initialized`);
66
+ console.log(` Demo mode: ${client.demoMode}`);
67
+ console.log();
68
+
69
+ // ========================================
70
+ // Step 2: Define predicates
71
+ // ========================================
72
+ console.log('STEP 2: Define Predicates');
73
+ console.log('-'.repeat(70));
74
+
75
+ const predicates = [
76
+ new Predicate({
77
+ claimKey: 'age',
78
+ op: PredicateOp.GTE,
79
+ value: 18,
80
+ }),
81
+ new Predicate({
82
+ claimKey: 'country',
83
+ op: PredicateOp.IN,
84
+ value: ['US', 'UK', 'CA'],
85
+ }),
86
+ ];
87
+
88
+ console.log(' Predicates:');
89
+ for (const pred of predicates) {
90
+ console.log(` ${pred.claimKey} ${pred.op} ${JSON.stringify(pred.value)}`);
91
+ }
92
+ console.log();
93
+
94
+ // ========================================
95
+ // Step 3: Generate proof
96
+ // ========================================
97
+ console.log('STEP 3: Generate ZK Proof');
98
+ console.log('-'.repeat(70));
99
+
100
+ const credentialId = credential.id;
101
+ const proofData = await client.generateProof(credentialId, predicates);
102
+
103
+ console.log(' Proof generated:');
104
+ console.log(` Credential root: 0x${proofData.credentialRoot.toString(16).slice(0, 30)}...`);
105
+ console.log(` Issuer: ${proofData.publicInputs.issuerPublicKey.slice(0, 30)}...`);
106
+ console.log(` Predicates: ${proofData.publicInputs.predicates.length}`);
107
+ console.log();
108
+
109
+ // ========================================
110
+ // Step 4: Verify on-chain (simulated)
111
+ // ========================================
112
+ console.log('STEP 4: Verify On-Chain (Simulated)');
113
+ console.log('-'.repeat(70));
114
+
115
+ console.log(' In production, this would:');
116
+ console.log(' 1. Send proof to CredentialVerifier contract');
117
+ console.log(' 2. Contract verifies ZK proof via verifier contract');
118
+ console.log(' 3. Contract checks issuer is trusted');
119
+ console.log(' 4. Contract emits CredentialVerified event');
120
+ console.log();
121
+
122
+ console.log(' Verification result: ✓ Proof valid (simulated)');
123
+ console.log();
124
+
125
+ // ========================================
126
+ // Privacy Summary
127
+ // ========================================
128
+ console.log('PRIVACY SUMMARY');
129
+ console.log('-'.repeat(70));
130
+ console.log();
131
+ console.log(' ┌───────────────────────┬───────────────────────────────────┐');
132
+ console.log(' │ PUBLIC │ HIDDEN │');
133
+ console.log(' ├───────────────────────┼───────────────────────────────────┤');
134
+ console.log(' │ Issuer public key │ User\'s DID │');
135
+ console.log(' │ Predicates (>=18, IN) │ Actual age │');
136
+ console.log(' │ Credential root │ Actual country │');
137
+ console.log(' │ ZK proof │ Other claims │');
138
+ console.log(' │ │ Blinding factors │');
139
+ console.log(' └───────────────────────┴───────────────────────────────────┘');
140
+ console.log();
141
+
142
+ console.log('='.repeat(70));
143
+ console.log(' SUCCESS! Credential verification flow completed.');
144
+ console.log('='.repeat(70));
145
+ }
146
+
147
+ main().catch(err => {
148
+ console.error('Error:', err);
149
+ process.exit(1);
150
+ });
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Verify Setup Example
3
+ *
4
+ * Quick verification that all components are working correctly.
5
+ * Run this first to ensure the package is properly installed.
6
+ *
7
+ * Run: node examples/verify-setup.js
8
+ */
9
+
10
+ const {
11
+ VerifiableCredential,
12
+ CredentialWallet,
13
+ Predicate,
14
+ PredicateOp,
15
+ init,
16
+ poseidonHash,
17
+ } = require('../lib');
18
+
19
+ async function main() {
20
+ console.log('='.repeat(60));
21
+ console.log(' ZK-KYC-DID - Setup Verification');
22
+ console.log('='.repeat(60));
23
+ console.log();
24
+
25
+ let passed = 0;
26
+ let failed = 0;
27
+
28
+ // Test 1: Library Initialization
29
+ console.log('TEST 1: Library Initialization');
30
+ try {
31
+ await init();
32
+ console.log(' ✓ Library initialized');
33
+ passed++;
34
+ } catch (e) {
35
+ console.log(` ✗ Failed: ${e.message}`);
36
+ failed++;
37
+ }
38
+ console.log();
39
+
40
+ // Test 2: Poseidon Hash
41
+ console.log('TEST 2: Poseidon Hash');
42
+ try {
43
+ const hash = await poseidonHash([1n, 2n, 3n]);
44
+ const hex = '0x' + BigInt(hash).toString(16).padStart(64, '0');
45
+ console.log(` ✓ Poseidon(1,2,3) = ${hex.slice(0, 30)}...`);
46
+ console.log(` ✓ Hash length: ${hex.length} chars`);
47
+ passed++;
48
+ } catch (e) {
49
+ console.log(` ✗ Failed: ${e.message}`);
50
+ failed++;
51
+ }
52
+ console.log();
53
+
54
+ // Test 3: Credential Creation
55
+ console.log('TEST 3: Credential Creation');
56
+ try {
57
+ const credential = new VerifiableCredential({
58
+ issuer: '0x1234',
59
+ subject: 'did:zk:test',
60
+ claims: { age: 25, country: 'US' },
61
+ });
62
+
63
+ await credential.init();
64
+ const commitments = credential.getAllCommitments();
65
+
66
+ console.log(` ✓ Credential created`);
67
+ console.log(` ✓ Commitments: ${Object.keys(commitments).length}`);
68
+ console.log(` ✓ ID: ${credential.id.slice(0, 30)}...`);
69
+ passed++;
70
+ } catch (e) {
71
+ console.log(` ✗ Failed: ${e.message}`);
72
+ failed++;
73
+ }
74
+ console.log();
75
+
76
+ // Test 4: Credential Wallet
77
+ console.log('TEST 4: Credential Wallet');
78
+ try {
79
+ const wallet = new CredentialWallet();
80
+ await wallet.init();
81
+
82
+ const credential = new VerifiableCredential({
83
+ issuer: '0x1234',
84
+ subject: wallet.subjectDID,
85
+ claims: { age: 25 },
86
+ });
87
+ await credential.init();
88
+
89
+ wallet.addCredential(credential);
90
+
91
+ console.log(` ✓ Wallet created`);
92
+ console.log(` ✓ DID: ${wallet.subjectDID.slice(0, 30)}...`);
93
+ console.log(` ✓ Credentials: ${wallet.getAllCredentials().length}`);
94
+ passed++;
95
+ } catch (e) {
96
+ console.log(` ✗ Failed: ${e.message}`);
97
+ failed++;
98
+ }
99
+ console.log();
100
+
101
+ // Test 5: Predicates
102
+ console.log('TEST 5: Predicates');
103
+ try {
104
+ const pred1 = new Predicate({
105
+ claimKey: 'age',
106
+ op: PredicateOp.GTE,
107
+ value: 18,
108
+ });
109
+
110
+ const pred2 = new Predicate({
111
+ claimKey: 'country',
112
+ op: PredicateOp.IN,
113
+ value: ['US', 'UK'],
114
+ });
115
+
116
+ console.log(` ✓ Predicate 1: age >= 18`);
117
+ console.log(` ✓ Predicate 2: country IN [US, UK]`);
118
+ console.log(` ✓ Evaluation: ${pred1.evaluate(25)} (should be true)`);
119
+ console.log(` ✓ Evaluation: ${pred2.evaluate('US')} (should be true)`);
120
+ passed++;
121
+ } catch (e) {
122
+ console.log(` ✗ Failed: ${e.message}`);
123
+ failed++;
124
+ }
125
+ console.log();
126
+
127
+ // Test 6: Serialization
128
+ console.log('TEST 6: Serialization');
129
+ try {
130
+ const wallet = new CredentialWallet();
131
+ await wallet.init();
132
+
133
+ const credential = new VerifiableCredential({
134
+ issuer: '0x1234',
135
+ subject: wallet.subjectDID,
136
+ claims: { age: 25 },
137
+ });
138
+ await credential.init();
139
+ wallet.addCredential(credential);
140
+
141
+ const exported = wallet.toJSON();
142
+ const restored = await CredentialWallet.fromJSON(exported);
143
+
144
+ console.log(` ✓ Wallet serialized: ${JSON.stringify(exported).length} bytes`);
145
+ console.log(` ✓ Wallet restored`);
146
+ console.log(` ✓ DID matches: ${restored.subjectDID === wallet.subjectDID}`);
147
+ passed++;
148
+ } catch (e) {
149
+ console.log(` ✗ Failed: ${e.message}`);
150
+ failed++;
151
+ }
152
+ console.log();
153
+
154
+ // Results
155
+ console.log('='.repeat(60));
156
+ if (failed === 0) {
157
+ console.log(` ✓ ALL ${passed} TESTS PASSED`);
158
+ console.log();
159
+ console.log(' Your setup is complete! Try running:');
160
+ console.log(' node examples/create-credential.js');
161
+ console.log(' node examples/generate-proof.js');
162
+ console.log(' node examples/verify-credential.js');
163
+ } else {
164
+ console.log(` ✗ ${failed} TESTS FAILED, ${passed} PASSED`);
165
+ console.log();
166
+ console.log(' Please check your installation:');
167
+ console.log(' npm install');
168
+ }
169
+ console.log('='.repeat(60));
170
+
171
+ process.exit(failed > 0 ? 1 : 0);
172
+ }
173
+
174
+ main().catch(err => {
175
+ console.error('Error:', err);
176
+ process.exit(1);
177
+ });
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Verifiable Credential Management
3
+ *
4
+ * Implements W3C Verifiable Credentials with Zero-Knowledge selective disclosure.
5
+ * Credentials can prove predicates (e.g., "age >= 18") without revealing the actual value.
6
+ */
7
+
8
+ const { buildPoseidon } = require('circomlibjs');
9
+ const crypto = require('crypto');
10
+
11
+ let poseidon = null;
12
+
13
+ /**
14
+ * Initialize Poseidon hash
15
+ */
16
+ async function init() {
17
+ if (!poseidon) {
18
+ poseidon = await buildPoseidon();
19
+ }
20
+ return true;
21
+ }
22
+
23
+ /**
24
+ * Poseidon hash helper
25
+ */
26
+ function poseidonHash(inputs) {
27
+ if (!poseidon) throw new Error('Call init() first');
28
+ const hash = poseidon(inputs.map(x => BigInt(x)));
29
+ return poseidon.F.toString(hash);
30
+ }
31
+
32
+ /**
33
+ * Generate a random field element
34
+ */
35
+ function randomFieldElement() {
36
+ const bytes = crypto.randomBytes(32);
37
+ const value = BigInt('0x' + bytes.toString('hex'));
38
+ const FIELD_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
39
+ return value % FIELD_MOD;
40
+ }
41
+
42
+ /**
43
+ * Verifiable Credential
44
+ *
45
+ * Based on W3C Verifiable Credentials standard with ZK extensions.
46
+ */
47
+ class VerifiableCredential {
48
+ /**
49
+ * Create a new credential
50
+ * @param {Object} options
51
+ * @param {string} options.issuer - Issuer DID or public key
52
+ * @param {string} options.subject - Subject DID (who the credential is about)
53
+ * @param {Object} options.claims - Key-value pairs of claims (e.g., { age: 25, country: "US" })
54
+ * @param {string} [options.type] - Credential type (e.g., "KYC", "Identity")
55
+ * @param {number} [options.expirationDate] - Unix timestamp
56
+ */
57
+ constructor(options) {
58
+ this.issuer = options.issuer;
59
+ this.subject = options.subject;
60
+ this.claims = { ...options.claims };
61
+ this.type = options.type || 'VerifiableCredential';
62
+ this.issuedAt = Date.now();
63
+ this.expirationDate = options.expirationDate || null;
64
+ this.id = options.id || `credential:${Date.now()}:${randomFieldElement().toString(16)}`;
65
+
66
+ // ZK-specific fields
67
+ this.claimCommitments = {};
68
+ this.blindingFactors = {};
69
+ this.signature = null;
70
+ }
71
+
72
+ /**
73
+ * Initialize credential (compute commitments)
74
+ */
75
+ async init() {
76
+ await init();
77
+
78
+ // Create commitments for each claim
79
+ for (const [key, value] of Object.entries(this.claims)) {
80
+ const blinding = randomFieldElement();
81
+ this.blindingFactors[key] = blinding;
82
+
83
+ // Commitment = Poseidon(key, value, blinding)
84
+ this.claimCommitments[key] = poseidonHash([
85
+ BigInt(key.length), // Domain separation
86
+ BigInt(value.toString().length),
87
+ BigInt(blinding),
88
+ BigInt(this.hashClaimValue(key, value)),
89
+ ]);
90
+ }
91
+
92
+ return this;
93
+ }
94
+
95
+ /**
96
+ * Hash a claim value (for commitment)
97
+ * @private
98
+ */
99
+ hashClaimValue(key, value) {
100
+ // Convert value to consistent format
101
+ if (typeof value === 'number') {
102
+ return BigInt(value);
103
+ }
104
+ if (typeof value === 'string') {
105
+ // Hash string values
106
+ return BigInt('0x' + crypto.createHash('sha256').update(value).digest('hex').slice(0, 16));
107
+ }
108
+ if (typeof value === 'boolean') {
109
+ return value ? 1n : 0n;
110
+ }
111
+ return BigInt(value.toString());
112
+ }
113
+
114
+ /**
115
+ * Sign the credential (issuer does this)
116
+ * @param {bigint} issuerPrivateKey - Issuer's private key
117
+ */
118
+ sign(issuerPrivateKey) {
119
+ // Create signature over all commitments
120
+ const commitments = Object.values(this.claimCommitments).sort();
121
+ const message = poseidonHash([...commitments, BigInt(this.issuer)]);
122
+
123
+ // Simple signature scheme (in production, use proper ECDSA/EdDSA)
124
+ // For now, we'll use a commitment to the signature
125
+ this.signature = poseidonHash([message, BigInt(issuerPrivateKey)]);
126
+
127
+ return this.signature;
128
+ }
129
+
130
+ /**
131
+ * Verify credential signature
132
+ * @param {bigint} issuerPublicKey - Issuer's public key
133
+ * @returns {boolean}
134
+ */
135
+ verifySignature(issuerPublicKey) {
136
+ if (!this.signature) return false;
137
+
138
+ const commitments = Object.values(this.claimCommitments).sort();
139
+ const message = poseidonHash([...commitments, BigInt(this.issuer)]);
140
+ const expectedSignature = poseidonHash([message, BigInt(issuerPublicKey)]);
141
+
142
+ // In production, use proper signature verification
143
+ // This is a simplified version
144
+ return this.signature === expectedSignature;
145
+ }
146
+
147
+ /**
148
+ * Get commitment for a specific claim
149
+ * @param {string} claimKey
150
+ * @returns {string} Hex commitment
151
+ */
152
+ getCommitment(claimKey) {
153
+ if (!this.claimCommitments[claimKey]) {
154
+ throw new Error(`Claim "${claimKey}" not found`);
155
+ }
156
+ return '0x' + BigInt(this.claimCommitments[claimKey]).toString(16).padStart(64, '0');
157
+ }
158
+
159
+ /**
160
+ * Get all commitments
161
+ * @returns {Object} claimKey -> commitment hex
162
+ */
163
+ getAllCommitments() {
164
+ const result = {};
165
+ for (const [key, commitment] of Object.entries(this.claimCommitments)) {
166
+ result[key] = '0x' + BigInt(commitment).toString(16).padStart(64, '0');
167
+ }
168
+ return result;
169
+ }
170
+
171
+ /**
172
+ * Get blinding factor for a claim (needed for ZK proofs)
173
+ * @param {string} claimKey
174
+ * @returns {bigint}
175
+ */
176
+ getBlindingFactor(claimKey) {
177
+ if (!this.blindingFactors[claimKey]) {
178
+ throw new Error(`Blinding factor for "${claimKey}" not found`);
179
+ }
180
+ return BigInt(this.blindingFactors[claimKey]);
181
+ }
182
+
183
+ /**
184
+ * Check if credential is expired
185
+ * @returns {boolean}
186
+ */
187
+ isExpired() {
188
+ if (!this.expirationDate) return false;
189
+ return Date.now() > this.expirationDate;
190
+ }
191
+
192
+ /**
193
+ * Serialize credential for storage
194
+ * @returns {Object}
195
+ */
196
+ toJSON() {
197
+ return {
198
+ id: this.id,
199
+ issuer: this.issuer,
200
+ subject: this.subject,
201
+ type: this.type,
202
+ issuedAt: this.issuedAt,
203
+ expirationDate: this.expirationDate,
204
+ claimCommitments: Object.fromEntries(
205
+ Object.entries(this.claimCommitments).map(([k, v]) => [
206
+ k,
207
+ v.toString()
208
+ ])
209
+ ),
210
+ blindingFactors: Object.fromEntries(
211
+ Object.entries(this.blindingFactors).map(([k, v]) => [
212
+ k,
213
+ v.toString()
214
+ ])
215
+ ),
216
+ signature: this.signature?.toString() || null,
217
+ // Note: claims are NOT included in JSON for privacy
218
+ // User must store them separately if needed
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Deserialize credential from storage
224
+ * @param {Object} json
225
+ * @param {Object} [claims] - Optional claims (if user stored them)
226
+ * @returns {Promise<VerifiableCredential>}
227
+ */
228
+ static async fromJSON(json, claims = {}) {
229
+ const cred = new VerifiableCredential({
230
+ issuer: json.issuer,
231
+ subject: json.subject,
232
+ claims,
233
+ type: json.type,
234
+ expirationDate: json.expirationDate,
235
+ id: json.id,
236
+ });
237
+
238
+ cred.issuedAt = json.issuedAt;
239
+ cred.claimCommitments = Object.fromEntries(
240
+ Object.entries(json.claimCommitments || {}).map(([k, v]) => [k, BigInt(v)])
241
+ );
242
+ cred.blindingFactors = Object.fromEntries(
243
+ Object.entries(json.blindingFactors || {}).map(([k, v]) => [k, BigInt(v)])
244
+ );
245
+ cred.signature = json.signature ? BigInt(json.signature) : null;
246
+
247
+ await cred.init();
248
+ return cred;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Credential Wallet
254
+ *
255
+ * Manages multiple credentials for a user
256
+ */
257
+ class CredentialWallet {
258
+ /**
259
+ * Create a credential wallet
260
+ * @param {string} [subjectDID] - User's DID
261
+ */
262
+ constructor(subjectDID = null) {
263
+ this.subjectDID = subjectDID || this.generateDID();
264
+ this.credentials = new Map(); // credentialId -> VerifiableCredential
265
+ this.initialized = false;
266
+ }
267
+
268
+ /**
269
+ * Initialize wallet
270
+ */
271
+ async init() {
272
+ await init();
273
+ this.initialized = true;
274
+ }
275
+
276
+ /**
277
+ * Generate a DID for the user
278
+ * @returns {string}
279
+ */
280
+ generateDID() {
281
+ const bytes = crypto.randomBytes(32);
282
+ const did = 'did:zk:' + bytes.toString('hex');
283
+ return did;
284
+ }
285
+
286
+ /**
287
+ * Add a credential to the wallet
288
+ * @param {VerifiableCredential} credential
289
+ */
290
+ addCredential(credential) {
291
+ if (credential.subject !== this.subjectDID) {
292
+ throw new Error('Credential subject does not match wallet DID');
293
+ }
294
+ this.credentials.set(credential.id, credential);
295
+ }
296
+
297
+ /**
298
+ * Get a credential by ID
299
+ * @param {string} credentialId
300
+ * @returns {VerifiableCredential|null}
301
+ */
302
+ getCredential(credentialId) {
303
+ return this.credentials.get(credentialId) || null;
304
+ }
305
+
306
+ /**
307
+ * Get all credentials
308
+ * @returns {VerifiableCredential[]}
309
+ */
310
+ getAllCredentials() {
311
+ return Array.from(this.credentials.values());
312
+ }
313
+
314
+ /**
315
+ * Find credentials by type
316
+ * @param {string} type
317
+ * @returns {VerifiableCredential[]}
318
+ */
319
+ getCredentialsByType(type) {
320
+ return Array.from(this.credentials.values()).filter(c => c.type === type);
321
+ }
322
+
323
+ /**
324
+ * Remove a credential
325
+ * @param {string} credentialId
326
+ */
327
+ removeCredential(credentialId) {
328
+ this.credentials.delete(credentialId);
329
+ }
330
+
331
+ /**
332
+ * Export wallet (for backup)
333
+ * @returns {Object}
334
+ */
335
+ toJSON() {
336
+ return {
337
+ subjectDID: this.subjectDID,
338
+ credentials: Array.from(this.credentials.values()).map(c => c.toJSON()),
339
+ };
340
+ }
341
+
342
+ /**
343
+ * Import wallet from backup
344
+ * @param {Object} json
345
+ * @returns {Promise<CredentialWallet>}
346
+ */
347
+ static async fromJSON(json) {
348
+ const wallet = new CredentialWallet(json.subjectDID);
349
+ await wallet.init();
350
+
351
+ for (const credJson of json.credentials) {
352
+ const cred = await VerifiableCredential.fromJSON(credJson);
353
+ wallet.credentials.set(cred.id, cred);
354
+ }
355
+
356
+ return wallet;
357
+ }
358
+ }
359
+
360
+ module.exports = {
361
+ VerifiableCredential,
362
+ CredentialWallet,
363
+ init,
364
+ poseidonHash,
365
+ randomFieldElement,
366
+ };