UNPKG

anon-identity

Version:

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

265 lines 9.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MockRevocationRegistry = exports.RevocationService = void 0; const uuid_1 = require("uuid"); const jose_1 = require("jose"); const storage_1 = require("../storage"); /** * Mock revocation registry - in production this would be a distributed ledger or database */ class MockRevocationRegistry { static publish(issuerDID, revocationList) { this.registry.set(issuerDID, revocationList); return `${this.BASE_URL}/${encodeURIComponent(issuerDID)}`; } static async fetch(issuerDID) { // Simulate network delay await new Promise(resolve => setTimeout(resolve, 100)); return this.registry.get(issuerDID) || null; } static async fetchByUrl(url) { const match = url.match(/https:\/\/revocation\.example\.com\/(.+)$/); if (!match) return null; const issuerDID = decodeURIComponent(match[1]); return this.fetch(issuerDID); } static clear() { this.registry.clear(); } } exports.MockRevocationRegistry = MockRevocationRegistry; MockRevocationRegistry.registry = new Map(); MockRevocationRegistry.BASE_URL = 'https://revocation.example.com'; class RevocationService { constructor(keyPair, issuerDID, storageProvider) { this.keyPair = keyPair; this.issuerDID = issuerDID; this.storageProvider = storageProvider || storage_1.StorageFactory.getDefaultProvider(); } /** * Revoke a credential by adding its ID to the revocation list */ async revokeCredential(credentialId) { const currentList = await this.storageProvider.getRevocationList(this.issuerDID); const revokedIds = currentList ? [...currentList.revokedCredentialIds] : []; if (!revokedIds.includes(credentialId)) { revokedIds.push(credentialId); await this.updateRevocationList(revokedIds); } } /** * Unrevoke a credential by removing its ID from the revocation list */ async unrevokeCredential(credentialId) { const currentList = await this.storageProvider.getRevocationList(this.issuerDID); if (!currentList) return; const revokedIds = currentList.revokedCredentialIds.filter(id => id !== credentialId); await this.updateRevocationList(revokedIds); } /** * Check if a credential is revoked */ async isRevoked(credentialId) { return await this.storageProvider.checkRevocation(this.issuerDID, credentialId); } /** * Get all revoked credential IDs */ async getRevokedCredentials() { const revocationList = await this.storageProvider.getRevocationList(this.issuerDID); return revocationList ? revocationList.revokedCredentialIds : []; } /** * Update the revocation list in storage */ async updateRevocationList(revokedIds) { const timestamp = Date.now(); const signature = await this.createRevocationSignature(revokedIds, timestamp); const revocationList = { issuerDID: this.issuerDID, revokedCredentialIds: revokedIds, timestamp, signature }; await this.storageProvider.publishRevocation(this.issuerDID, revocationList); } /** * Create a signature for the revocation list */ async createRevocationSignature(revokedIds, timestamp) { const dataToSign = { issuerDID: this.issuerDID, revokedCredentialIds: revokedIds, timestamp }; // Convert private key to JWK format for jose const privateKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(this.keyPair.publicKey).toString('base64url'), d: Buffer.from(this.keyPair.privateKey).toString('base64url') }; const privateKey = await (0, jose_1.importJWK)(privateKeyJwk, 'EdDSA'); // Create JWT const jwt = await new jose_1.SignJWT(dataToSign) .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: `${this.issuerDID}#key-1` }) .setIssuedAt() .setIssuer(this.issuerDID) .sign(privateKey); return jwt; } /** * Create and sign a revocation list (for backward compatibility) */ async createRevocationList() { const revocationListId = `urn:uuid:${(0, uuid_1.v4)()}`; const issuanceDate = new Date().toISOString(); const revokedCredentials = await this.getRevokedCredentials(); // Create the revocation list without proof const revocationList = { "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/vc-revocation-list-2020/v1" ], id: revocationListId, type: ["RevocationList2020"], issuer: this.issuerDID, issuanceDate: issuanceDate, revokedCredentials: revokedCredentials }; // Sign the revocation list const signedList = await this.signRevocationList(revocationList); return signedList; } /** * Sign a revocation list */ async signRevocationList(revocationList) { // Create a copy without the proof field for signing const listToSign = { ...revocationList }; delete listToSign.proof; // Convert private key to JWK format for jose const privateKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(this.keyPair.publicKey).toString('base64url'), d: Buffer.from(this.keyPair.privateKey).toString('base64url') }; const privateKey = await (0, jose_1.importJWK)(privateKeyJwk, 'EdDSA'); // Create JWT const jwt = await new jose_1.SignJWT(listToSign) .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: `${this.issuerDID}#key-1` }) .setIssuedAt() .setIssuer(this.issuerDID) .sign(privateKey); // Add proof to revocation list const signedList = { ...revocationList, proof: { type: 'Ed25519Signature2020', created: new Date().toISOString(), proofPurpose: 'assertionMethod', verificationMethod: `${this.issuerDID}#key-1`, jws: jwt } }; return signedList; } /** * Publish the revocation list to the mock registry */ async publishRevocationList() { const revocationList = await this.createRevocationList(); const url = MockRevocationRegistry.publish(this.issuerDID, revocationList); // Also update storage provider const revokedCredentials = await this.getRevokedCredentials(); await this.updateRevocationList(revokedCredentials); return url; } /** * Verify a revocation list signature */ static async verifyRevocationList(revocationList, issuerPublicKey) { try { if (!revocationList.proof?.jws) { return false; } // Convert public key to JWK for jose const publicKeyJwk = { kty: 'OKP', crv: 'Ed25519', x: Buffer.from(issuerPublicKey).toString('base64url') }; const key = await (0, jose_1.importJWK)(publicKeyJwk, 'EdDSA'); // Verify JWT const { payload } = await (0, jose_1.jwtVerify)(revocationList.proof.jws, key, { algorithms: ['EdDSA'] }); // Verify the payload matches the revocation list if (payload.issuer !== revocationList.issuer) { return false; } // Check if revokedCredentials match const payloadRevoked = payload.revokedCredentials || []; if (JSON.stringify(payloadRevoked.sort()) !== JSON.stringify(revocationList.revokedCredentials.sort())) { return false; } return true; } catch (error) { return false; } } /** * Fetch a revocation list from a URL (using mock registry) */ static async fetchRevocationList(url) { return MockRevocationRegistry.fetchByUrl(url); } /** * Fetch a revocation list by issuer DID (using mock registry) */ static async fetchRevocationListByIssuer(issuerDID) { return MockRevocationRegistry.fetch(issuerDID); } /** * Clear the mock registry (for testing) */ static clearRegistry() { MockRevocationRegistry.clear(); } // Sync compatibility methods (for backward compatibility) revokeCredentialSync(credentialId) { // Convert to async internally but maintain sync interface for backward compatibility this.revokeCredential(credentialId).catch(console.error); } unrevokeCredentialSync(credentialId) { // Convert to async internally but maintain sync interface for backward compatibility this.unrevokeCredential(credentialId).catch(console.error); } isRevokedSync(credentialId) { // This is a breaking change - need to handle differently console.warn('isRevokedSync() sync method is deprecated. Use async isRevoked() instead.'); return false; } getRevokedCredentialsSync() { // This is a breaking change - need to handle differently console.warn('getRevokedCredentialsSync() sync method is deprecated. Use async getRevokedCredentials() instead.'); return []; } setStorageProvider(provider) { this.storageProvider = provider; } } exports.RevocationService = RevocationService; //# sourceMappingURL=revocation-service.js.map