anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
265 lines • 9.95 kB
JavaScript
;
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