@cheny56/zk-kyc-did
v1.0.0
Published
Zero-Knowledge KYC and Decentralized Identity - Prove credentials without revealing personal data
Maintainers
Readme
@cheny56/zk-kyc-did
Zero-Knowledge KYC and Decentralized Identity
Prove you meet requirements (age, nationality, verification status) without revealing personal data. Based on W3C Verifiable Credentials with ZK selective disclosure.
Features
- 🔒 Selective Disclosure - Prove predicates (age >= 18) without revealing exact values
- 🆔 W3C Verifiable Credentials - Standards-compliant credential format
- 🌐 Any EVM Chain - Works on Ethereum, Quorum, Polygon, etc.
- 📦 Self-Contained - No external dependencies on special nodes
- 🎯 Privacy-First - Only commitments and proofs are public
Installation
npm install @cheny56/zk-kyc-didQuick Start
const { VerifiableCredential, CredentialWallet, Predicate, PredicateOp } = require('@cheny56/zk-kyc-did');
// 1. User creates wallet
const wallet = new CredentialWallet();
await wallet.init();
// 2. KYC provider issues credential
const credential = new VerifiableCredential({
issuer: '0x...', // KYC provider
subject: wallet.subjectDID,
claims: { age: 25, country: 'US', verified: true },
});
await credential.init();
wallet.addCredential(credential);
// 3. User proves predicates without revealing values
const predicates = [
new Predicate({ claimKey: 'age', op: PredicateOp.GTE, value: 18 }),
new Predicate({ claimKey: 'country', op: PredicateOp.IN, value: ['US', 'UK'] }),
];
// 4. Generate ZK proof
const proof = await generateProof(credential, predicates);
// 5. Verify on-chain
await contract.verifyCredential(proof, predicates);Table of Contents
- How It Works
- Core Concepts
- API Reference
- Step-by-Step Guide
- Examples
- Circuit Compilation
- Contract Deployment
- Use Cases
- Security Considerations
How It Works
┌─────────────────────────────────────────────────────────────────────────┐
│ ZK KYC/DID FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. CREDENTIAL ISSUANCE (Off-Chain) │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │
│ │ KYC Provider│ --> │ User's │ --> │ Credential with │ │
│ │ │ │ Claims │ │ Commitments │ │
│ │ {age: 25, │ │ │ │ (values hidden) │ │
│ │ country:US}│ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └────────────────────────┘ │
│ │
│ 2. PROOF GENERATION (User Side) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ User wants to prove: │ │
│ │ - age >= 18 (without revealing 25) │ │
│ │ - country IN [US, UK] (without revealing US) │ │
│ │ │ │
│ │ ZK Circuit proves: │ │
│ │ 1. Credential is valid (signature check) │ │
│ │ 2. age >= 18 (range proof) │ │
│ │ 3. country IN set (membership proof) │ │
│ │ │ │
│ │ WITHOUT revealing: │ │
│ │ - Exact age │ │
│ │ - Which country │ │
│ │ - Other claims │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ 3. ON-CHAIN VERIFICATION │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────────┐ │
│ │ ZK Proof │ --> │ Verifier │ --> │ Contract verifies │ │
│ │ │ │ Contract │ │ and grants access │ │
│ └────────────┘ └────────────┘ └────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘Core Concepts
Verifiable Credential
A Credential contains claims (key-value pairs) that are committed using Poseidon hash:
Credential = {
issuer: "0x...", // KYC provider
subject: "did:zk:...", // User's DID
claims: {
age: 25, // Hidden in commitment
country: "US", // Hidden in commitment
verified: true // Hidden in commitment
},
claimCommitments: { // Public (on-chain)
age: "0xabc...",
country: "0xdef...",
verified: "0x123..."
},
signature: "0x..." // Issuer's signature
}Selective Disclosure
Predicates allow proving properties without revealing values:
// Prove age >= 18 without revealing age is 25
new Predicate({ claimKey: 'age', op: PredicateOp.GTE, value: 18 })
// Prove country is in allowed list without revealing which
new Predicate({
claimKey: 'country',
op: PredicateOp.IN,
value: ['US', 'UK', 'CA']
})DID (Decentralized Identifier)
A DID is a self-sovereign identifier:
did:zk:0x1234567890abcdef...Users control their DID and can generate multiple credentials linked to it.
API Reference
VerifiableCredential
const { VerifiableCredential } = require('@cheny56/zk-kyc-did');
// Create credential
const credential = new VerifiableCredential({
issuer: '0x...',
subject: 'did:zk:...',
claims: { age: 25, country: 'US' },
type: 'KYC',
expirationDate: Date.now() + 365*24*60*60*1000, // 1 year
});
await credential.init();
// Sign credential (issuer does this)
credential.sign(issuerPrivateKey);
// Get commitments
const commitments = credential.getAllCommitments();
// Verify signature
const valid = credential.verifySignature(issuerPublicKey);
// Check expiration
const expired = credential.isExpired();CredentialWallet
const { CredentialWallet } = require('@cheny56/zk-kyc-did');
const wallet = new CredentialWallet();
await wallet.init();
// Add credential
wallet.addCredential(credential);
// Get credentials
const all = wallet.getAllCredentials();
const kycCreds = wallet.getCredentialsByType('KYC');
// Export for backup
const backup = wallet.toJSON();
// Restore from backup
const restored = await CredentialWallet.fromJSON(backup);Predicate
const { Predicate, PredicateOp } = require('@cheny56/zk-kyc-did');
// Create predicates
const predicates = [
new Predicate({ claimKey: 'age', op: PredicateOp.GTE, value: 18 }),
new Predicate({ claimKey: 'country', op: PredicateOp.IN, value: ['US', 'UK'] }),
new Predicate({ claimKey: 'verified', op: PredicateOp.EQ, value: true }),
];
// Evaluate predicate
const satisfied = predicate.evaluate(claimValue);KYCClient
const { KYCClient } = require('@cheny56/zk-kyc-did/client');
const client = new KYCClient({
provider,
signer,
verifierAddress: '0x...',
});
await client.init();
// Generate proof
const proofData = await client.generateProof(credentialId, predicates);
// Verify on-chain
const { verified } = await client.verifyOnChain(proofData);Step-by-Step Guide
Step 1: Install and Initialize
npm install @cheny56/zk-kyc-didStep 2: Create User Wallet
const { CredentialWallet } = require('@cheny56/zk-kyc-did');
const wallet = new CredentialWallet();
await wallet.init();
console.log('Your DID:', wallet.subjectDID);Step 3: Issue Credential (KYC Provider)
const { VerifiableCredential } = require('@cheny56/zk-kyc-did');
// KYC provider creates credential
const credential = new VerifiableCredential({
issuer: kycProviderPublicKey,
subject: userWallet.subjectDID,
claims: {
age: 25,
country: 'US',
documentType: 'passport',
verified: true,
},
type: 'KYC',
});
await credential.init();
credential.sign(kycProviderPrivateKey);
// Send to user (user stores in wallet)
userWallet.addCredential(credential);Step 4: Generate Proof
const { Predicate, PredicateOp, generateProofInputs } = require('@cheny56/zk-kyc-did');
// Define what to prove
const predicates = [
new Predicate({ claimKey: 'age', op: PredicateOp.GTE, value: 18 }),
new Predicate({ claimKey: 'country', op: PredicateOp.IN, value: ['US', 'UK', 'CA'] }),
];
// Generate proof inputs
const privateInputs = await generateProofInputs(credential, predicates);
// Use snarkjs to generate actual proof
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
privateInputs,
'circuits/credential-proof.wasm',
'keys/credential-proof_final.zkey'
);Step 5: Verify On-Chain
const { KYCClient } = require('@cheny56/zk-kyc-did/client');
const client = new KYCClient({
provider,
signer,
verifierAddress: '0x...',
});
await client.init();
const proofData = await client.generateProof(credentialId, predicates);
const { verified } = await client.verifyOnChain(proofData);
if (verified) {
console.log('Access granted!');
}Examples
Basic Credential Creation
// examples/create-credential.js
const { VerifiableCredential, CredentialWallet } = require('@cheny56/zk-kyc-did');
async function main() {
const wallet = new CredentialWallet();
await wallet.init();
const credential = new VerifiableCredential({
issuer: '0x...',
subject: wallet.subjectDID,
claims: { age: 25, country: 'US' },
});
await credential.init();
wallet.addCredential(credential);
console.log('Credential created:', credential.id);
}
main();Generate Proof
// examples/generate-proof.js
const { Predicate, PredicateOp, generateProofInputs } = require('@cheny56/zk-kyc-did');
const predicates = [
new Predicate({ claimKey: 'age', op: PredicateOp.GTE, value: 18 }),
];
const privateInputs = await generateProofInputs(credential, predicates);
// ... generate proof with snarkjsCircuit Compilation
# Install circom
npm install -g circom snarkjs
# Compile circuit
circom circuits/credential-proof.circom --r1cs --wasm --sym -o build/
# Download powers of tau
wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_12.ptau -O pot12_final.ptau
# Generate proving keys
snarkjs groth16 setup build/credential-proof.r1cs pot12_final.ptau keys/credential-proof_0000.zkey
# Contribute randomness
snarkjs zkey contribute keys/credential-proof_0000.zkey keys/credential-proof_final.zkey
# Export Solidity verifier
snarkjs zkey export solidityverifier keys/credential-proof_final.zkey contracts/CredentialProofVerifier.solContract Deployment
const { ethers } = require('ethers');
async function deploy() {
const provider = new ethers.JsonRpcProvider('http://localhost:8545');
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
// Deploy verifier contract (generated by snarkjs)
const Verifier = await ethers.getContractFactory('CredentialProofVerifier');
const verifier = await Verifier.deploy();
// Deploy CredentialVerifier
const CredentialVerifier = await ethers.getContractFactory('CredentialVerifier');
const credentialVerifier = await CredentialVerifier.deploy(await verifier.getAddress());
// Add trusted issuers
await credentialVerifier.addTrustedIssuer(KYC_PROVIDER_ADDRESS);
console.log('Deployed:', await credentialVerifier.getAddress());
}Use Cases
1. Age Verification
// Prove user is 18+ without revealing exact age
const predicate = new Predicate({
claimKey: 'age',
op: PredicateOp.GTE,
value: 18,
});2. Geographic Restrictions
// Prove user is from allowed country without revealing which
const predicate = new Predicate({
claimKey: 'country',
op: PredicateOp.IN,
value: ['US', 'UK', 'CA', 'EU'],
});3. KYC Tier Verification
// Prove user has completed KYC level 2
const predicate = new Predicate({
claimKey: 'kycTier',
op: PredicateOp.GTE,
value: 2,
});4. Document Verification
// Prove user has verified passport
const predicate = new Predicate({
claimKey: 'documentVerified',
op: PredicateOp.EQ,
value: true,
});Security Considerations
- Trusted Issuers: Only credentials from trusted issuers should be accepted
- Credential Expiration: Always check
credential.isExpired()before use - Signature Verification: Verify issuer signatures before accepting credentials
- Predicate Validation: Ensure predicates are satisfied before generating proofs
- Key Management: Protect credential wallets and private keys
Privacy Properties
| Information | Public | Hidden | |-------------|--------|--------| | Issuer public key | ✅ | | | Predicates (>=18, IN[...]) | ✅ | | | Credential root | ✅ | | | ZK proof | ✅ | | | User's DID | | ✅ | | Actual claim values | | ✅ | | Blinding factors | | ✅ | | Credential signature | | ✅ | | Other claims | | ✅ |
Package Contents
zk-kyc-did/
├── lib/
│ ├── index.js # Main exports
│ ├── credential.js # Credential management
│ └── predicate.js # Predicate proofs
├── client/
│ └── kyc-client.js # Contract interaction
├── contracts/
│ ├── CredentialVerifier.sol
│ └── interfaces/
├── circuits/
│ └── credential-proof.circom
├── examples/
│ ├── create-credential.js
│ ├── generate-proof.js
│ ├── verify-credential.js
│ └── verify-setup.js
├── package.json
└── README.mdLicense
MIT
Related
- @cheny56/zk-voting - ZK voting system
- @cheny56/zk-confidential-offchain - Confidential tokens
