UNPKG

anon-identity

Version:

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

221 lines 7.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CompositeStatusChecker = exports.StatusList2021StatusChecker = exports.StatusList2021 = exports.RevocationList2020StatusChecker = void 0; const vc2_1 = require("../types/vc2"); const jose_1 = require("jose"); /** * RevocationList2020 status checker * Compatible with existing revocation implementation */ class RevocationList2020StatusChecker { constructor(revocationLists = new Map()) { this.revocationLists = revocationLists; } async checkStatus(credentialId, statusInfo) { if (statusInfo.type !== vc2_1.CredentialStatusType.REVOCATION_LIST_2020) { throw new Error(`Unsupported status type: ${statusInfo.type}`); } const list = this.revocationLists.get(statusInfo.id); if (!list) { // If we can't find the list, assume not revoked return { revoked: false, checkedAt: new Date().toISOString() }; } const isRevoked = list.revokedCredentials?.includes(credentialId) || false; return { revoked: isRevoked, reason: isRevoked ? 'Credential has been revoked' : undefined, checkedAt: new Date().toISOString() }; } /** * Add or update a revocation list */ addRevocationList(listId, list) { this.revocationLists.set(listId, list); } } exports.RevocationList2020StatusChecker = RevocationList2020StatusChecker; /** * StatusList2021 implementation * Uses a bitstring to efficiently store credential status */ class StatusList2021 { constructor(size = 100000) { this.size = size; // Each byte holds 8 bits this.bitstring = new Uint8Array(Math.ceil(size / 8)); } /** * Set status for a credential at given index * @param index The index in the status list * @param revoked Whether the credential is revoked */ setStatus(index, revoked) { if (index < 0 || index >= this.size) { throw new Error(`Index ${index} out of bounds (0-${this.size - 1})`); } const byteIndex = Math.floor(index / 8); const bitIndex = index % 8; if (revoked) { // Set bit to 1 this.bitstring[byteIndex] |= (1 << bitIndex); } else { // Set bit to 0 this.bitstring[byteIndex] &= ~(1 << bitIndex); } } /** * Check status for a credential at given index * @param index The index in the status list * @returns Whether the credential is revoked */ getStatus(index) { if (index < 0 || index >= this.size) { throw new Error(`Index ${index} out of bounds (0-${this.size - 1})`); } const byteIndex = Math.floor(index / 8); const bitIndex = index % 8; return (this.bitstring[byteIndex] & (1 << bitIndex)) !== 0; } /** * Encode the bitstring as base64 */ encode() { return Buffer.from(this.bitstring).toString('base64'); } /** * Decode from base64 */ static decode(encoded, size) { const list = new StatusList2021(size); list.bitstring = new Uint8Array(Buffer.from(encoded, 'base64')); return list; } /** * Create a signed status list credential */ async createStatusListCredential(issuerDID, privateKey, listId) { const now = new Date().toISOString(); const statusListCredential = { "@context": [ "https://www.w3.org/ns/credentials/v2", "https://w3id.org/vc/status-list/2021/v1" ], id: listId, type: ["VerifiableCredential", "StatusList2021Credential"], issuer: issuerDID.id, validFrom: now, credentialSubject: { id: `${listId}#list`, type: "StatusList2021", statusPurpose: "revocation", encodedList: this.encode() } }; // Sign the credential const jwt = await new jose_1.SignJWT({ vc: statusListCredential, iss: issuerDID.id, sub: statusListCredential.credentialSubject.id }) .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT' }) .setIssuedAt() .sign(privateKey); return { ...statusListCredential, proof: { type: "Ed25519Signature2020", created: now, verificationMethod: `${issuerDID.id}#key-1`, proofPurpose: "assertionMethod", jws: jwt } }; } } exports.StatusList2021 = StatusList2021; /** * StatusList2021 status checker */ class StatusList2021StatusChecker { constructor(statusLists = new Map()) { this.statusLists = statusLists; } async checkStatus(credentialId, statusInfo) { if (statusInfo.type !== vc2_1.CredentialStatusType.STATUS_LIST_2021) { throw new Error(`Unsupported status type: ${statusInfo.type}`); } const statusListIndex = statusInfo.statusListIndex; if (typeof statusListIndex !== 'number') { throw new Error('StatusList2021 requires statusListIndex'); } const list = this.statusLists.get(statusInfo.id); if (!list) { // If we can't find the list, assume not revoked return { revoked: false, statusListIndex, checkedAt: new Date().toISOString() }; } const isRevoked = list.getStatus(statusListIndex); return { revoked: isRevoked, statusListIndex, reason: isRevoked ? 'Credential has been revoked' : undefined, checkedAt: new Date().toISOString() }; } /** * Add or update a status list */ addStatusList(listId, list) { this.statusLists.set(listId, list); } /** * Load a status list from a credential */ async loadStatusListCredential(credential) { // Verify the credential first if (!credential.proof?.jws) { throw new Error('Status list credential must be signed'); } // Extract the encoded list const encodedList = credential.credentialSubject?.encodedList; if (!encodedList) { throw new Error('Status list credential missing encodedList'); } // TODO: Get size from credential metadata const size = 100000; // Default size const list = StatusList2021.decode(encodedList, size); this.addStatusList(credential.id, list); } } exports.StatusList2021StatusChecker = StatusList2021StatusChecker; /** * Composite status checker that supports multiple status types */ class CompositeStatusChecker { constructor() { this.checkers = new Map(); // Register default checkers this.registerChecker(vc2_1.CredentialStatusType.REVOCATION_LIST_2020, new RevocationList2020StatusChecker()); this.registerChecker(vc2_1.CredentialStatusType.STATUS_LIST_2021, new StatusList2021StatusChecker()); } registerChecker(type, checker) { this.checkers.set(type, checker); } async checkStatus(credentialId, statusInfo) { const checker = this.checkers.get(statusInfo.type); if (!checker) { throw new Error(`No status checker registered for type: ${statusInfo.type}`); } return checker.checkStatus(credentialId, statusInfo); } } exports.CompositeStatusChecker = CompositeStatusChecker; //# sourceMappingURL=credential-status.js.map