@credo-ts/core
Version:
<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1
268 lines (267 loc) • 14.9 kB
JavaScript
import { CredoError } from "../../error/CredoError.mjs";
import "../../error/index.mjs";
import { asArray } from "../../utils/array.mjs";
import { injectable } from "../../plugins/index.mjs";
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.mjs";
import __decorate from "../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.mjs";
import "./models/ClaimFormat.mjs";
import { W3cV2JwtVerifiableCredential } from "./jwt-vc/W3cV2JwtVerifiableCredential.mjs";
import { W3cV2DataIntegrityVerifiableCredential } from "./data-integrity/W3cV2DataIntegrityVerifiableCredential.mjs";
import { W3cV2DataIntegrityVerifiablePresentation } from "./data-integrity/W3cV2DataIntegrityVerifiablePresentation.mjs";
import { W3cV2SdJwtVerifiableCredential } from "./sd-jwt-vc/W3cV2SdJwtVerifiableCredential.mjs";
import { W3cV2EnvelopedVerifiableCredential } from "./models/credential/W3cV2EnvelopedVerifiableCredential.mjs";
import { W3cV2EnvelopedVerifiablePresentation } from "./models/presentation/W3cV2EnvelopedVerifiablePresentation.mjs";
import { W3cV2JwtVerifiablePresentation } from "./jwt-vc/W3cV2JwtVerifiablePresentation.mjs";
import { W3cV2SdJwtVerifiablePresentation } from "./sd-jwt-vc/W3cV2SdJwtVerifiablePresentation.mjs";
import { getVerificationMethodForJwt } from "./v2-jwt-utils.mjs";
import { W3cV2JwtCredentialService } from "./jwt-vc/W3cV2JwtCredentialService.mjs";
import "./jwt-vc/index.mjs";
import { getSignerForDataIntegrityProof } from "./data-integrity/v2-di-utils.mjs";
import { W3cV2DataIntegrityCredentialService } from "./data-integrity/W3cV2DataIntegrityCredentialService.mjs";
import "./data-integrity/index.mjs";
import { W3cV2SdJwtCredentialService } from "./sd-jwt-vc/W3cV2SdJwtCredentialService.mjs";
import "./sd-jwt-vc/index.mjs";
import { decodeW3cV2VerifiableCredential } from "./models/credential/W3cV2VerifiableCredential.mjs";
import { decodeW3cV2VerifiablePresentation } from "./models/presentation/W3cV2VerifiablePresentation.mjs";
import "./models/index.mjs";
import { W3cV2CredentialRepository } from "./repository/W3cV2CredentialRepository.mjs";
import "./repository/index.mjs";
//#region src/modules/vc/W3cV2CredentialService.ts
let W3cV2CredentialService = class W3cV2CredentialService {
constructor(w3cV2CredentialRepository, w3cV2SdJwtCredentialService, w3cV2JwtCredentialService, w3cV2DataIntegrityCredentialService) {
this.w3cV2CredentialRepository = w3cV2CredentialRepository;
this.w3cV2SdJwtCredentialService = w3cV2SdJwtCredentialService;
this.w3cV2JwtCredentialService = w3cV2JwtCredentialService;
this.w3cV2DataIntegrityCredentialService = w3cV2DataIntegrityCredentialService;
}
/**
* Signs a credential
*
* @param credential the credential to be signed
* @returns the signed credential
*/
async signCredential(agentContext, options) {
if (options.format === "vc+jwt") return await this.w3cV2JwtCredentialService.signCredential(agentContext, options);
if (options.format === "vc+sd-jwt") return await this.w3cV2SdJwtCredentialService.signCredential(agentContext, options);
if (options.format === "di_vc") return await this.w3cV2DataIntegrityCredentialService.signCredential(agentContext, options);
throw new CredoError(`Unsupported format in options. Format must be either 'vc+jwt', 'vc+sd-jwt', or 'di_vc'`);
}
/**
* Verifies the signature(s) of a credential
*/
async verifyCredential(agentContext, options) {
const credential = typeof options.credential === "string" ? decodeW3cV2VerifiableCredential(options.credential) : options.credential;
if (credential instanceof W3cV2JwtVerifiableCredential) return this.w3cV2JwtCredentialService.verifyCredential(agentContext, {
...options,
credential
});
if (credential instanceof W3cV2SdJwtVerifiableCredential) return this.w3cV2SdJwtCredentialService.verifyCredential(agentContext, {
...options,
credential
});
if (credential instanceof W3cV2DataIntegrityVerifiableCredential) return this.w3cV2DataIntegrityCredentialService.verifyCredential(agentContext, {
...options,
credential
});
throw new CredoError("Unsupported credential type in options. Credential must be either a W3cV2JwtVerifiableCredential, a W3cV2SdJwtVerifiableCredential, or a W3cV2DataIntegrityVerifiableCredential");
}
/**
* Signs a presentation including the credentials it includes
*
* @param presentation the presentation to be signed
* @returns the signed presentation
*/
async signPresentation(agentContext, options) {
if (options.format === "vp+jwt") return await this.w3cV2JwtCredentialService.signPresentation(agentContext, options);
if (options.format === "vp+sd-jwt") return await this.w3cV2SdJwtCredentialService.signPresentation(agentContext, options);
if (options.format === "di_vp") return await this.w3cV2DataIntegrityCredentialService.signPresentation(agentContext, options);
throw new CredoError(`Unsupported format in options. Format must be either 'vp+jwt', 'vp+sd-jwt', or 'di_vp'`);
}
/**
* Verifies a W3C V2 verifiable presentation and each enclosed credential entry.
*
* Flow:
* 1. Verifies the outer VP using the format-specific service (`vp+jwt` or `vp+sd-jwt`).
* 2. Derives the presenter identifier (holder id, or JWT verification method controller fallback).
* 3. Recursively walks enclosed entries and verifies each credential entry with the matching
* credential verifier (`vc+jwt` or `vc+sd-jwt`) based on the actual enclosed format.
* 4. Adds `credentialSubjectAuthentication` validation to each credential result by checking
* whether the authenticated presenter matches at least one credentialSubject id.
*
* Current support and constraints:
* - Compact string presentations are accepted for `vp+jwt` and `vp+sd-jwt` and normalized
* to typed presentation instances before routing.
* - `di_vp` routes through the DI credential service for outer-VP verification.
* - `di_vc` entries route through the DI credential service for entry verification.
* - Nested VP entries are treated as invalid. VC 2.0 verifiable presentations support VC entries,
* not VP-within-VP entries.
*
* Return semantics:
* - `presentation` contains the outer VP verification result.
* - `credentialEntries` contains one result per traversed credential entry, including unsupported
* entries as invalid results.
* - Top-level `isValid` is true only if the outer VP and all credential entry validations are valid.
*
* @param agentContext The framework agent context used for resolution and cryptographic checks.
* @param options Verification input containing the presentation to verify.
* @returns Aggregated presentation and credential-entry verification result.
*/
async verifyPresentation(agentContext, options) {
const presentation = typeof options.presentation === "string" ? decodeW3cV2VerifiablePresentation(options.presentation) : options.presentation;
const validationResults = {
isValid: false,
presentation: {
isValid: false,
validations: {}
},
credentialEntries: []
};
let entries = [];
let signerId;
if (presentation instanceof W3cV2JwtVerifiablePresentation) {
const presentationResult = await this.w3cV2JwtCredentialService.verifyPresentation(agentContext, {
...options,
presentation
});
validationResults.presentation = presentationResult.presentation;
if (!presentationResult.presentation.isValid) {
validationResults.isValid = false;
return validationResults;
}
const holderId = presentation.resolvedPresentation.holderId;
if (holderId) signerId = holderId;
else try {
signerId = (await getVerificationMethodForJwt(agentContext, presentation, ["authentication"])).controller;
} catch (error) {
validationResults.isValid = false;
validationResults.error = error;
return validationResults;
}
entries = this.extractCredentialEntriesFromPresentation(presentation);
} else if (presentation instanceof W3cV2SdJwtVerifiablePresentation) {
const presentationResult = await this.w3cV2SdJwtCredentialService.verifyPresentation(agentContext, {
...options,
presentation
});
validationResults.presentation = presentationResult.presentation;
if (!presentationResult.presentation.isValid) {
validationResults.isValid = false;
return validationResults;
}
const holderId = presentation.resolvedPresentation.holderId;
if (holderId) signerId = holderId;
else try {
signerId = (await getVerificationMethodForJwt(agentContext, presentation, ["authentication"])).controller;
} catch (error) {
validationResults.isValid = false;
validationResults.error = error;
return validationResults;
}
entries = this.extractCredentialEntriesFromPresentation(presentation);
} else if (presentation instanceof W3cV2DataIntegrityVerifiablePresentation) {
const presentationResult = await this.w3cV2DataIntegrityCredentialService.verifyPresentation(agentContext, {
...options,
presentation
});
validationResults.presentation = presentationResult.presentation;
if (!presentationResult.presentation.isValid) {
validationResults.isValid = false;
return validationResults;
}
signerId = presentation.resolvedPresentation.holderId;
if (!signerId) try {
signerId = await getSignerForDataIntegrityProof(agentContext, presentation.securedPresentation, "authentication");
} catch (error) {
validationResults.isValid = false;
validationResults.error = error;
return validationResults;
}
entries = this.extractCredentialEntriesFromPresentation(presentation);
} else throw new CredoError("Unsupported credential type in options. Presentation must be either a W3cV2JwtVerifiablePresentation, a W3cV2SdJwtVerifiablePresentation, or a W3cV2DataIntegrityVerifiablePresentation");
validationResults.credentialEntries = (await Promise.all(entries.map((entry) => this.verifyPresentationEntry(agentContext, entry, signerId)))).flat();
validationResults.isValid = validationResults.presentation.isValid && validationResults.credentialEntries.every((entry) => entry.isValid);
return validationResults;
}
async verifyPresentationEntry(agentContext, entry, signerId) {
if (entry instanceof W3cV2DataIntegrityVerifiablePresentation) return [this.createInvalidCredentialEntryResult(new CredoError("Nested verifiable presentation entries are not supported in VC 2.0 presentations."))];
if (entry instanceof W3cV2DataIntegrityVerifiableCredential) {
const credentialResult = await this.w3cV2DataIntegrityCredentialService.verifyCredential(agentContext, { credential: entry });
return [this.mergeCredentialSubjectAuthenticationValidation(credentialResult, signerId, entry.resolvedCredential?.credentialSubjectIds ?? [])];
}
if (entry instanceof W3cV2EnvelopedVerifiablePresentation) return [this.createInvalidCredentialEntryResult(new CredoError("Nested verifiable presentation entries are not supported in VC 2.0 presentations."))];
if (!(entry instanceof W3cV2EnvelopedVerifiableCredential)) return [this.createInvalidCredentialEntryResult(new CredoError("Unsupported credential entry type in presentation."))];
const enclosed = entry.envelopedCredential;
let credentialResult;
if (enclosed instanceof W3cV2JwtVerifiableCredential) credentialResult = await this.w3cV2JwtCredentialService.verifyCredential(agentContext, { credential: enclosed });
else if (enclosed instanceof W3cV2SdJwtVerifiableCredential) credentialResult = await this.w3cV2SdJwtCredentialService.verifyCredential(agentContext, { credential: enclosed });
else return [this.createInvalidCredentialEntryResult(new CredoError(`Credential entry uses '${this.getClaimFormat(enclosed) ?? "an unsupported enclosed credential format"}'.`))];
return [this.mergeCredentialSubjectAuthenticationValidation(credentialResult, signerId, entry.resolvedCredential.credentialSubjectIds)];
}
extractCredentialEntriesFromPresentation(presentation) {
return this.extractCredentialEntriesFromResolvedPresentation(presentation.resolvedPresentation);
}
extractCredentialEntriesFromResolvedPresentation(resolvedPresentation) {
return asArray(resolvedPresentation.verifiableCredential);
}
createInvalidCredentialEntryResult(error) {
return {
isValid: false,
error,
validations: {}
};
}
getClaimFormat(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
const candidate = value;
return typeof candidate.claimFormat === "string" ? candidate.claimFormat : void 0;
}
mergeCredentialSubjectAuthenticationValidation(credentialResult, signerId, credentialSubjectIds) {
const presentationAuthenticatesCredentialSubject = credentialSubjectIds.some((subjectId) => signerId === subjectId);
const credentialSubjectAuthentication = credentialSubjectIds.length > 0 && !presentationAuthenticatesCredentialSubject ? {
isValid: false,
error: new CredoError("Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject")
} : { isValid: true };
return {
...credentialResult,
isValid: credentialResult.isValid && credentialSubjectAuthentication.isValid,
validations: {
...credentialResult.validations,
credentialSubjectAuthentication
}
};
}
/**
* Writes a credential to storage
*
* @param record the credential to be stored
* @returns the credential record that was written to storage
*/
async storeCredential(agentContext, options) {
await this.w3cV2CredentialRepository.save(agentContext, options.record);
return options.record;
}
async removeCredentialRecord(agentContext, id) {
await this.w3cV2CredentialRepository.deleteById(agentContext, id);
}
async getAllCredentialRecords(agentContext) {
return await this.w3cV2CredentialRepository.getAll(agentContext);
}
async getCredentialRecordById(agentContext, id) {
return await this.w3cV2CredentialRepository.getById(agentContext, id);
}
async findCredentialsByQuery(agentContext, query, queryOptions) {
return (await this.w3cV2CredentialRepository.findByQuery(agentContext, query, queryOptions)).map((record) => record.firstCredential);
}
async findCredentialRecordByQuery(agentContext, query) {
return (await this.w3cV2CredentialRepository.findSingleByQuery(agentContext, query))?.firstCredential;
}
};
W3cV2CredentialService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [
typeof W3cV2CredentialRepository === "undefined" ? Object : W3cV2CredentialRepository,
typeof W3cV2SdJwtCredentialService === "undefined" ? Object : W3cV2SdJwtCredentialService,
typeof W3cV2JwtCredentialService === "undefined" ? Object : W3cV2JwtCredentialService,
typeof W3cV2DataIntegrityCredentialService === "undefined" ? Object : W3cV2DataIntegrityCredentialService
])], W3cV2CredentialService);
//#endregion
export { W3cV2CredentialService };
//# sourceMappingURL=W3cV2CredentialService.mjs.map