anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
401 lines • 14.9 kB
JavaScript
"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.FileStorageProvider = void 0;
const fs_1 = require("fs");
const path = __importStar(require("path"));
const uuid_1 = require("uuid");
const crypto = __importStar(require("crypto"));
class FileStorageProvider {
constructor(dataPath, encryption = true, passphrase) {
this.data = {
dids: {},
credentials: {},
keyPairs: {},
revocationLists: {},
schemas: {},
phoneNumbers: {},
phoneNumbersByUser: {},
emailAddresses: {},
emailAddressesByUser: {},
addresses: {},
addressesByUser: {}
};
this.dataPath = dataPath;
this.encryption = encryption;
if (encryption && passphrase) {
// Derive encryption key from passphrase
this.encryptionKey = crypto.scryptSync(passphrase, 'salt', 32);
}
}
async ensureDirectory() {
const dir = path.dirname(this.dataPath);
try {
await fs_1.promises.access(dir);
}
catch {
await fs_1.promises.mkdir(dir, { recursive: true });
}
}
encrypt(data) {
if (!this.encryption || !this.encryptionKey)
return data;
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return JSON.stringify({
encrypted,
authTag: authTag.toString('hex'),
iv: iv.toString('hex')
});
}
decrypt(encryptedData) {
if (!this.encryption || !this.encryptionKey)
return encryptedData;
try {
const { encrypted, authTag, iv } = JSON.parse(encryptedData);
const decipher = crypto.createDecipheriv('aes-256-gcm', this.encryptionKey, Buffer.from(iv, 'hex'));
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
catch (error) {
throw new Error('Failed to decrypt data. Invalid passphrase or corrupted data.');
}
}
async load() {
try {
const rawData = await fs_1.promises.readFile(this.dataPath, 'utf8');
const decryptedData = this.decrypt(rawData);
this.data = JSON.parse(decryptedData);
}
catch (error) {
// File doesn't exist or is corrupted, start with empty data
this.data = {
dids: {},
credentials: {},
keyPairs: {},
revocationLists: {},
schemas: {},
phoneNumbers: {},
phoneNumbersByUser: {},
emailAddresses: {},
emailAddressesByUser: {},
addresses: {},
addressesByUser: {}
};
}
}
async save() {
await this.ensureDirectory();
const jsonData = JSON.stringify(this.data, null, 2);
const dataToSave = this.encrypt(jsonData);
await fs_1.promises.writeFile(this.dataPath, dataToSave, 'utf8');
}
// DID Operations
async storeDID(did, document) {
await this.load();
this.data.dids[did] = document;
await this.save();
}
async resolveDID(did) {
await this.load();
return this.data.dids[did] || null;
}
async listDIDs(owner) {
await this.load();
const dids = Object.keys(this.data.dids);
if (!owner)
return dids;
return dids.filter(did => {
const doc = this.data.dids[did];
if (!doc || !doc.verificationMethod)
return false;
return doc.verificationMethod.some(vm => vm.controller === owner);
});
}
// Credential Operations
async storeCredential(credential) {
await this.load();
this.data.credentials[credential.id] = credential;
await this.save();
}
async getCredential(id) {
await this.load();
return this.data.credentials[id] || null;
}
async listCredentials(holder) {
await this.load();
return Object.values(this.data.credentials).filter(cred => cred.credentialSubject.id === holder);
}
async deleteCredential(id) {
await this.load();
delete this.data.credentials[id];
await this.save();
}
// Revocation Operations
async publishRevocation(issuerDID, revocationList) {
await this.load();
this.data.revocationLists[issuerDID] = revocationList;
await this.save();
}
async checkRevocation(issuerDID, credentialId) {
await this.load();
const revocationList = this.data.revocationLists[issuerDID];
if (!revocationList)
return false;
return revocationList.revokedCredentialIds.includes(credentialId);
}
async getRevocationList(issuerDID) {
await this.load();
return this.data.revocationLists[issuerDID] || null;
}
// Key Management
async storeKeyPair(identifier, encryptedKeyPair) {
await this.load();
this.data.keyPairs[identifier] = encryptedKeyPair;
await this.save();
}
async retrieveKeyPair(identifier) {
await this.load();
return this.data.keyPairs[identifier] || null;
}
async deleteKeyPair(identifier) {
await this.load();
delete this.data.keyPairs[identifier];
await this.save();
}
// Schema Operations
async registerSchema(schema) {
await this.load();
const schemaId = schema.id || `schema:${(0, uuid_1.v4)()}`;
const schemaWithId = { ...schema, id: schemaId };
this.data.schemas[schemaId] = schemaWithId;
await this.save();
return schemaId;
}
async getSchema(schemaId) {
await this.load();
return this.data.schemas[schemaId] || null;
}
async listSchemas(issuerDID) {
await this.load();
const schemas = Object.values(this.data.schemas);
if (!issuerDID)
return schemas;
return schemas.filter(schema => schema.issuerDID === issuerDID);
}
// Phone Number Operations
async storePhoneNumber(userDID, phoneNumber) {
await this.load();
const phoneId = phoneNumber.id || `phone:${(0, uuid_1.v4)()}`;
const phoneWithId = { ...phoneNumber, id: phoneId };
this.data.phoneNumbers[phoneId] = phoneWithId;
// Update user index
if (!this.data.phoneNumbersByUser[userDID]) {
this.data.phoneNumbersByUser[userDID] = [];
}
if (!this.data.phoneNumbersByUser[userDID].includes(phoneId)) {
this.data.phoneNumbersByUser[userDID].push(phoneId);
}
await this.save();
return phoneId;
}
async getPhoneNumber(userDID, phoneId) {
await this.load();
const userPhones = this.data.phoneNumbersByUser[userDID] || [];
if (!userPhones.includes(phoneId)) {
return null;
}
return this.data.phoneNumbers[phoneId] || null;
}
async listPhoneNumbers(userDID) {
await this.load();
const phoneIds = this.data.phoneNumbersByUser[userDID] || [];
return phoneIds
.map(id => this.data.phoneNumbers[id])
.filter(phone => phone !== undefined);
}
async updatePhoneNumber(userDID, phoneId, phoneNumber) {
await this.load();
const userPhones = this.data.phoneNumbersByUser[userDID] || [];
if (!userPhones.includes(phoneId)) {
throw new Error('Phone number not found');
}
const existingPhone = this.data.phoneNumbers[phoneId];
if (existingPhone) {
this.data.phoneNumbers[phoneId] = { ...existingPhone, ...phoneNumber, id: phoneId };
await this.save();
}
}
async deletePhoneNumber(userDID, phoneId) {
await this.load();
const userPhones = this.data.phoneNumbersByUser[userDID] || [];
this.data.phoneNumbersByUser[userDID] = userPhones.filter(id => id !== phoneId);
if (this.data.phoneNumbersByUser[userDID].length === 0) {
delete this.data.phoneNumbersByUser[userDID];
}
delete this.data.phoneNumbers[phoneId];
await this.save();
}
// Email Address Operations
async storeEmailAddress(userDID, emailAddress) {
await this.load();
const emailId = emailAddress.id || `email:${(0, uuid_1.v4)()}`;
const emailWithId = { ...emailAddress, id: emailId };
this.data.emailAddresses[emailId] = emailWithId;
// Update user index
if (!this.data.emailAddressesByUser[userDID]) {
this.data.emailAddressesByUser[userDID] = [];
}
if (!this.data.emailAddressesByUser[userDID].includes(emailId)) {
this.data.emailAddressesByUser[userDID].push(emailId);
}
await this.save();
return emailId;
}
async getEmailAddress(userDID, emailId) {
await this.load();
const userEmails = this.data.emailAddressesByUser[userDID] || [];
if (!userEmails.includes(emailId)) {
return null;
}
return this.data.emailAddresses[emailId] || null;
}
async listEmailAddresses(userDID) {
await this.load();
const emailIds = this.data.emailAddressesByUser[userDID] || [];
return emailIds
.map(id => this.data.emailAddresses[id])
.filter(email => email !== undefined);
}
async updateEmailAddress(userDID, emailId, emailAddress) {
await this.load();
const userEmails = this.data.emailAddressesByUser[userDID] || [];
if (!userEmails.includes(emailId)) {
throw new Error('Email address not found');
}
const existingEmail = this.data.emailAddresses[emailId];
if (existingEmail) {
this.data.emailAddresses[emailId] = { ...existingEmail, ...emailAddress, id: emailId };
await this.save();
}
}
async deleteEmailAddress(userDID, emailId) {
await this.load();
const userEmails = this.data.emailAddressesByUser[userDID] || [];
this.data.emailAddressesByUser[userDID] = userEmails.filter(id => id !== emailId);
if (this.data.emailAddressesByUser[userDID].length === 0) {
delete this.data.emailAddressesByUser[userDID];
}
delete this.data.emailAddresses[emailId];
await this.save();
}
// Address Operations
async storeAddress(userDID, address) {
await this.load();
const addressId = address.id || `address:${(0, uuid_1.v4)()}`;
const addressWithId = { ...address, id: addressId };
this.data.addresses[addressId] = addressWithId;
// Update user index
if (!this.data.addressesByUser[userDID]) {
this.data.addressesByUser[userDID] = [];
}
if (!this.data.addressesByUser[userDID].includes(addressId)) {
this.data.addressesByUser[userDID].push(addressId);
}
await this.save();
return addressId;
}
async getAddress(userDID, addressId) {
await this.load();
const userAddresses = this.data.addressesByUser[userDID] || [];
if (!userAddresses.includes(addressId)) {
return null;
}
return this.data.addresses[addressId] || null;
}
async listAddresses(userDID) {
await this.load();
const addressIds = this.data.addressesByUser[userDID] || [];
return addressIds
.map(id => this.data.addresses[id])
.filter(address => address !== undefined);
}
async updateAddress(userDID, addressId, address) {
await this.load();
const userAddresses = this.data.addressesByUser[userDID] || [];
if (!userAddresses.includes(addressId)) {
throw new Error('Address not found');
}
const existingAddress = this.data.addresses[addressId];
if (existingAddress) {
this.data.addresses[addressId] = { ...existingAddress, ...address, id: addressId };
await this.save();
}
}
async deleteAddress(userDID, addressId) {
await this.load();
const userAddresses = this.data.addressesByUser[userDID] || [];
this.data.addressesByUser[userDID] = userAddresses.filter(id => id !== addressId);
if (this.data.addressesByUser[userDID].length === 0) {
delete this.data.addressesByUser[userDID];
}
delete this.data.addresses[addressId];
await this.save();
}
// General operations
async clear() {
this.data = {
dids: {},
credentials: {},
keyPairs: {},
revocationLists: {},
schemas: {},
phoneNumbers: {},
phoneNumbersByUser: {},
emailAddresses: {},
emailAddressesByUser: {},
addresses: {},
addressesByUser: {}
};
await this.save();
}
}
exports.FileStorageProvider = FileStorageProvider;
//# sourceMappingURL=file-storage-provider.js.map