anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
250 lines • 9.24 kB
JavaScript
;
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.defaultJsonLdProcessor = exports.JsonLdProcessor = void 0;
const jsonld = __importStar(require("jsonld"));
const context_loader_1 = require("./context-loader");
/**
* JSON-LD Processor for Verifiable Credentials
*/
class JsonLdProcessor {
constructor(options = {}) {
this.contextLoader = options.contextLoader || context_loader_1.defaultContextLoader;
this.documentLoader = this.contextLoader.createDocumentLoader();
this.safeMode = options.safeMode ?? true;
this.validateContexts = options.validateContexts ?? true;
}
/**
* Expand a JSON-LD document
*/
async expand(document) {
try {
const expanded = await jsonld.expand(document, {
documentLoader: this.documentLoader
});
return expanded;
}
catch (error) {
throw new Error(`JSON-LD expansion failed: ${error}`);
}
}
/**
* Compact a JSON-LD document
*/
async compact(document, context) {
try {
const compacted = await jsonld.compact(document, context, {
documentLoader: this.documentLoader
});
return compacted;
}
catch (error) {
throw new Error(`JSON-LD compaction failed: ${error}`);
}
}
/**
* Canonicalize a JSON-LD document for signing
*/
async canonicalize(document) {
try {
const canonicalized = await jsonld.canonize(document, {
algorithm: 'URDNA2015',
format: 'application/n-quads',
documentLoader: this.documentLoader
});
return canonicalized;
}
catch (error) {
throw new Error(`JSON-LD canonicalization failed: ${error}`);
}
}
/**
* Frame a JSON-LD document
*/
async frame(document, frame) {
try {
const framed = await jsonld.frame(document, frame, {
documentLoader: this.documentLoader
});
return framed;
}
catch (error) {
throw new Error(`JSON-LD framing failed: ${error}`);
}
}
/**
* Validate a credential's JSON-LD structure
*/
async validateCredential(credential) {
const errors = [];
try {
// 1. Check that @context is present
if (!credential['@context']) {
errors.push('Missing @context property');
return { valid: false, errors };
}
// 2. Expand the document to check for errors
const expanded = await this.expand(credential);
// 3. Check that required properties expanded correctly
if (expanded.length === 0) {
errors.push('Document expanded to empty array');
}
// 4. Validate specific credential properties
const expandedCred = expanded[0];
// Check for required credential properties
const requiredProps = [
'https://www.w3.org/2018/credentials#credentialSubject',
'https://www.w3.org/2018/credentials#issuer'
];
for (const prop of requiredProps) {
if (!expandedCred[prop]) {
errors.push(`Missing required property: ${prop}`);
}
}
// 5. If proof exists, validate it expanded correctly
if (credential.proof && !expandedCred['https://w3id.org/security#proof']) {
errors.push('Proof property did not expand correctly');
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined
};
}
catch (error) {
errors.push(`JSON-LD processing error: ${error}`);
return { valid: false, errors };
}
}
/**
* Validate a presentation's JSON-LD structure
*/
async validatePresentation(presentation) {
const errors = [];
try {
// 1. Check that @context is present
if (!presentation['@context']) {
errors.push('Missing @context property');
return { valid: false, errors };
}
// 2. Expand the document
const expanded = await this.expand(presentation);
if (expanded.length === 0) {
errors.push('Document expanded to empty array');
}
// 3. Validate credentials if present
if (presentation.verifiableCredential) {
for (let i = 0; i < presentation.verifiableCredential.length; i++) {
const cred = presentation.verifiableCredential[i];
if (typeof cred !== 'string') {
const result = await this.validateCredential(cred);
if (!result.valid) {
errors.push(`Credential ${i}: ${result.errors?.join(', ')}`);
}
}
}
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined
};
}
catch (error) {
errors.push(`JSON-LD processing error: ${error}`);
return { valid: false, errors };
}
}
/**
* Normalize a document for comparison
*/
async normalize(document) {
// First expand, then compact with a standard context
const expanded = await this.expand(document);
const context = document['@context'] || 'https://www.w3.org/ns/credentials/v2';
return await this.compact(expanded, context);
}
/**
* Extract claims from an expanded credential
*/
async extractClaims(credential) {
const claims = new Map();
try {
const expanded = await this.expand(credential);
if (expanded.length === 0)
return claims;
const expandedCred = expanded[0];
const subject = expandedCred['https://www.w3.org/2018/credentials#credentialSubject'];
if (Array.isArray(subject)) {
// Multiple subjects
subject.forEach((subj, index) => {
this.extractClaimsFromSubject(subj, claims, `subject${index}`);
});
}
else if (subject) {
// Single subject
this.extractClaimsFromSubject(subject, claims);
}
return claims;
}
catch (error) {
throw new Error(`Failed to extract claims: ${error}`);
}
}
/**
* Extract claims from a subject object
*/
extractClaimsFromSubject(subject, claims, prefix = '') {
for (const [key, value] of Object.entries(subject)) {
if (key === '@id' || key === '@type')
continue;
const claimKey = prefix ? `${prefix}.${key}` : key;
if (Array.isArray(value) && value.length > 0 && value[0] && typeof value[0] === 'object' && '@value' in value[0]) {
// Literal value
claims.set(claimKey, value[0]['@value']);
}
else if (value && typeof value === 'object' && '@value' in value) {
// Single literal value
claims.set(claimKey, value['@value']);
}
else {
// Complex value or reference
claims.set(claimKey, value);
}
}
}
}
exports.JsonLdProcessor = JsonLdProcessor;
// Default instance
exports.defaultJsonLdProcessor = new JsonLdProcessor();
//# sourceMappingURL=jsonld-processor.js.map