UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

182 lines 7.69 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectiveDisclosure = void 0; exports.getPublicKeyFromPrivate = getPublicKeyFromPrivate; const crypto = __importStar(require("crypto")); const crypto_1 = require("../core/crypto"); const jose_1 = require("jose"); class SelectiveDisclosure { /** * Create a selectively disclosed credential revealing only specified attributes */ static async createSelectivelyDisclosedCredential(originalCredential, attributesToDisclose, holderPrivateKey, holderDID) { // Validate that requested attributes exist in the credential const availableAttributes = Object.keys(originalCredential.credentialSubject) .filter(key => key !== 'id'); for (const attr of attributesToDisclose) { if (!availableAttributes.includes(attr)) { throw new Error(`Attribute '${attr}' not found in credential`); } } // Create disclosed credential subject with only requested attributes const disclosedSubject = { id: originalCredential.credentialSubject.id }; for (const attr of attributesToDisclose) { disclosedSubject[attr] = originalCredential.credentialSubject[attr]; } // Generate a nonce for this disclosure const nonce = crypto.randomBytes(32).toString('base64'); // Create proof of selective disclosure const disclosureData = { originalCredentialId: originalCredential.id, issuer: originalCredential.issuer, issuanceDate: originalCredential.issuanceDate, disclosedAttributes: attributesToDisclose, nonce, timestamp: new Date().toISOString() }; // Sign the disclosure proof with holder's private key const proofValue = await this.createDisclosureProof(disclosureData, holderPrivateKey, holderDID); // Create the selectively disclosed credential const disclosedCredential = { "@context": originalCredential["@context"], id: `${originalCredential.id}#disclosed-${Date.now()}`, type: [...originalCredential.type, "SelectivelyDisclosedCredential"], issuer: originalCredential.issuer, issuanceDate: originalCredential.issuanceDate, credentialSubject: disclosedSubject, proof: originalCredential.proof, // Keep original issuer's proof disclosureProof: { type: "SelectiveDisclosureProof2024", originalCredentialId: originalCredential.id, disclosedAttributes: attributesToDisclose, nonce, proofValue } }; return disclosedCredential; } /** * Create a cryptographic proof for the selective disclosure */ static async createDisclosureProof(disclosureData, privateKey, holderDID) { // Convert private key to JWK format const publicKey = await crypto_1.CryptoService.getPublicKeyFromPrivate(privateKey); const privateKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(publicKey).toString('base64url'), d: Buffer.from(privateKey).toString('base64url') }; const key = await (0, jose_1.importJWK)(privateKeyJwk, 'EdDSA'); // Create JWT proof const jwt = await new jose_1.SignJWT(disclosureData) .setProtectedHeader({ alg: 'EdDSA', typ: 'DisclosureProof', kid: `${holderDID}#key-1` }) .setIssuedAt() .setIssuer(holderDID) .sign(key); return jwt; } /** * Verify a selective disclosure proof */ static async verifySelectiveDisclosure(disclosedCredential, holderPublicKey) { try { if (!disclosedCredential.disclosureProof) { return false; } // Verify the disclosure proof JWT const publicKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(holderPublicKey).toString('base64url') }; const key = await (0, jose_1.importJWK)(publicKeyJwk, 'EdDSA'); const { payload } = await (0, jose_1.jwtVerify)(disclosedCredential.disclosureProof.proofValue, key, { algorithms: ['EdDSA'] }); // Verify the disclosed attributes match what's claimed const claimedAttributes = disclosedCredential.disclosureProof.disclosedAttributes; const actualAttributes = Object.keys(disclosedCredential.credentialSubject) .filter(key => key !== 'id'); if (!this.arraysEqual(claimedAttributes.sort(), actualAttributes.sort())) { return false; } // Verify the original credential ID matches if (payload.originalCredentialId !== disclosedCredential.disclosureProof.originalCredentialId) { return false; } return true; } catch (error) { return false; } } /** * Create a commitment to a value (for future ZKP enhancement) */ static createCommitment(value, salt) { const actualSalt = salt || crypto.randomBytes(32).toString('base64'); const data = JSON.stringify({ value, salt: actualSalt }); return crypto.createHash('sha256').update(data).digest('base64'); } /** * Verify a commitment (for future ZKP enhancement) */ static verifyCommitment(value, salt, commitment) { const computedCommitment = this.createCommitment(value, salt); return computedCommitment === commitment; } static arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } } exports.SelectiveDisclosure = SelectiveDisclosure; // Add helper to CryptoService async function getPublicKeyFromPrivate(privateKey) { const ed = await Promise.resolve().then(() => __importStar(require('@noble/ed25519'))); return await ed.getPublicKey(privateKey); } //# sourceMappingURL=selective-disclosure.js.map