@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
628 lines (627 loc) • 32.6 kB
JavaScript
import { asArray } from "../../utils/array.mjs";
import { TypedArrayEncoder } from "../../utils/TypedArrayEncoder.mjs";
import { JsonTransformer } from "../../utils/JsonTransformer.mjs";
import "../../utils/index.mjs";
import { injectable } from "../../plugins/index.mjs";
import __decorate from "../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.mjs";
import { X509Certificate } from "../x509/X509Certificate.mjs";
import { isNonEmptyArray, mapNonEmptyArray } from "../../types.mjs";
import { getPublicJwkFromVerificationMethod } from "../dids/domain/key-type/keyDidMapping.mjs";
import { DidsApi } from "../dids/DidsApi.mjs";
import "../dids/index.mjs";
import "../../utils/credentialUseTypes.mjs";
import "../vc/models/ClaimFormat.mjs";
import { W3cPresentation } from "../vc/models/presentation/W3cPresentation.mjs";
import { W3cV2Credential } from "../vc/models/credential/W3cV2Credential.mjs";
import { W3cV2SdJwtVerifiableCredential } from "../vc/sd-jwt-vc/W3cV2SdJwtVerifiableCredential.mjs";
import { W3cV2EnvelopedVerifiableCredential } from "../vc/models/credential/W3cV2EnvelopedVerifiableCredential.mjs";
import { W3cV2Presentation } from "../vc/models/presentation/W3cV2Presentation.mjs";
import { applyDisclosuresForPaths, buildDisclosureFrameForPayload, buildPresentationFrameForPaths, getClaimPathsForDcqlClaimSet } from "../sd-jwt-vc/disclosureFrame.mjs";
import { W3cV2SdJwtCredentialService } from "../vc/sd-jwt-vc/W3cV2SdJwtCredentialService.mjs";
import "../vc/sd-jwt-vc/index.mjs";
import { W3cCredentialRepository } from "../vc/repository/W3cCredentialRepository.mjs";
import { W3cV2CredentialRepository } from "../vc/repository/W3cV2CredentialRepository.mjs";
import { purposes } from "../vc/linked-data-proofs/adapters/jsonld-signatures-adapter.mjs";
import { SignatureSuiteRegistry } from "../vc/linked-data-proofs/SignatureSuiteRegistry.mjs";
import { W3cJsonLdCredentialService } from "../vc/linked-data-proofs/W3cJsonLdCredentialService.mjs";
import { W3cCredentialService } from "../vc/W3cCredentialService.mjs";
import { W3cV2CredentialService } from "../vc/W3cV2CredentialService.mjs";
import "../vc/index.mjs";
import "../x509/index.mjs";
import { Mdoc } from "../mdoc/Mdoc.mjs";
import { canUseInstanceFromCredentialRecord, useInstanceFromCredentialRecord } from "../../utils/credentialUse.mjs";
import { MdocDeviceResponse } from "../mdoc/MdocDeviceResponse.mjs";
import { MdocApi } from "../mdoc/MdocApi.mjs";
import "../mdoc/index.mjs";
import { SdJwtVcService } from "../sd-jwt-vc/SdJwtVcService.mjs";
import { SdJwtVcApi } from "../sd-jwt-vc/SdJwtVcApi.mjs";
import "../sd-jwt-vc/index.mjs";
import { DcqlError } from "./DcqlError.mjs";
import { dcqlGetPresentationsToCreate } from "./utils/DcqlPresentationsToCreate.mjs";
import "./utils/index.mjs";
import { DcqlPresentationResult, DcqlQuery } from "dcql";
//#region src/modules/dcql/DcqlService.ts
let DcqlService = class DcqlService {
/**
* Queries the wallet for credentials that match the given dcql query. This only does an initial query based on the
* schema of the input descriptors. It does not do any further filtering based on the constraints in the input descriptors.
*/
async queryCredentialsForDcqlQuery(agentContext, dcqlQuery) {
const formats = new Set(dcqlQuery.credentials.map((c) => c.format));
const allRecords = [];
const mdocDoctypes = dcqlQuery.credentials.filter((credentialQuery) => credentialQuery.format === "mso_mdoc").map((c) => c.meta?.doctype_value);
const mdocApi = this.getMdocApi(agentContext);
if (mdocDoctypes.every((doctype) => doctype !== void 0)) {
const mdocRecords = await mdocApi.findAllByQuery({ $or: mdocDoctypes.map((docType) => ({ docType })) });
allRecords.push(...mdocRecords);
} else if (formats.has("mso_mdoc")) {
const mdocRecords = await mdocApi.getAll();
allRecords.push(...mdocRecords);
}
const sdJwts = dcqlQuery.credentials.filter((credentialQuery) => credentialQuery.format === "vc+sd-jwt" && !(credentialQuery.meta && "type_values" in credentialQuery.meta) || credentialQuery.format === "dc+sd-jwt");
const sdJwtVctValues = sdJwts.flatMap((c) => c.meta?.vct_values);
const sdJwtVcApi = this.getSdJwtVcApi(agentContext);
if (sdJwtVctValues.every((vct) => vct !== void 0)) {
const sdjwtVcRecords = await sdJwtVcApi.findAllByQuery({ $or: [...sdJwtVctValues.map((vct) => ({ vct })), ...sdJwtVctValues.map((vct) => ({ extendedVctValues: [vct] }))] });
allRecords.push(...sdjwtVcRecords);
} else if (sdJwts.length > 0) {
const sdJwtVcRecords = await sdJwtVcApi.getAll();
allRecords.push(...sdJwtVcRecords);
}
const w3cCredentialRepository = agentContext.dependencyManager.resolve(W3cCredentialRepository);
if (formats.has("jwt_vc_json")) {
const w3cRecords = await w3cCredentialRepository.findByQuery(agentContext, {
claimFormat: "jwt_vc",
$or: dcqlQuery.credentials.flatMap((c) => c.format === "jwt_vc_json" ? c.meta.type_values : []).map((typeValues) => ({ types: typeValues }))
});
allRecords.push(...w3cRecords);
}
if (formats.has("ldp_vc")) {
const w3cRecords = await w3cCredentialRepository.findByQuery(agentContext, {
claimFormat: "ldp_vc",
$or: dcqlQuery.credentials.flatMap((c) => c.format === "jwt_vc_json" ? c.meta.type_values : []).map((typeValues) => ({ expandedTypes: typeValues }))
});
allRecords.push(...w3cRecords);
}
if (dcqlQuery.credentials.filter((credentialQuery) => credentialQuery.format === "vc+sd-jwt" && !!credentialQuery.meta && "type_values" in credentialQuery.meta).length > 0) {
const w3cV2Records = await agentContext.dependencyManager.resolve(W3cV2CredentialRepository).findByQuery(agentContext, {
claimFormat: "vc+sd-jwt",
$or: dcqlQuery.credentials.flatMap((c) => c.format === "vc+sd-jwt" && c.meta && "type_values" in c.meta ? c.meta.type_values : []).map((typeValues) => ({ types: typeValues }))
});
allRecords.push(...w3cV2Records);
}
return allRecords;
}
getAuthorityForCredential(credential) {
if (credential.claimFormat === "dc+sd-jwt") {
const akiValues = credential.header.x5c?.map((c) => {
const akiHex = X509Certificate.fromEncodedCertificate(c).authorityKeyIdentifier;
return akiHex ? TypedArrayEncoder.toBase64Url(TypedArrayEncoder.fromHex(akiHex)) : void 0;
}).filter((aki) => aki !== void 0);
return akiValues && isNonEmptyArray(akiValues) ? {
type: "aki",
values: akiValues
} : void 0;
}
if (credential.claimFormat === "mso_mdoc") {
const akiValues = credential.issuerSignedCertificateChain.map((c) => {
const akiHex = X509Certificate.fromRawCertificate(c).authorityKeyIdentifier;
return akiHex ? TypedArrayEncoder.toBase64Url(TypedArrayEncoder.fromHex(akiHex)) : void 0;
}).filter((aki) => aki !== void 0);
return akiValues && isNonEmptyArray(akiValues) ? {
type: "aki",
values: akiValues
} : void 0;
}
}
async getDcqlCredentialRepresentation(agentContext, presentation, queryCredential) {
if (presentation.claimFormat === "dc+sd-jwt") {
const requestedVcts = queryCredential.format === "dc+sd-jwt" ? queryCredential.meta?.vct_values : void 0;
let presentationVctValues = [presentation.payload.vct];
if (!requestedVcts?.includes(presentation.payload.vct)) {
const typeMetadataChain = await agentContext.resolve(SdJwtVcService).fetchTypeMetadata(agentContext, presentation, {
throwErrorOnFetchError: false,
throwErrorOnUnsupportedVctValue: false
});
if (typeMetadataChain) presentationVctValues = typeMetadataChain?.vctValues;
}
return {
credential_format: queryCredential.format === "dc+sd-jwt" ? "dc+sd-jwt" : "vc+sd-jwt",
authority: this.getAuthorityForCredential(presentation),
cryptographic_holder_binding: true,
vct: presentationVctValues.find((presentationVctValue) => requestedVcts?.includes(presentationVctValue)) ?? presentation.payload.vct,
claims: presentation.prettyClaims
};
}
if (presentation.claimFormat === "mso_mdoc") {
if (!presentation.deviceResponse.documents || presentation.deviceResponse.documents.length !== 1) throw new DcqlError("MDOC presentations must contain exactly one document");
return {
cryptographic_holder_binding: true,
credential_format: "mso_mdoc",
authority: this.getAuthorityForCredential(new Mdoc(presentation.deviceResponse.documents[0].issuerSigned)),
doctype: presentation.deviceResponse.documents[0].docType,
namespaces: Object.entries(Object.fromEntries(presentation.deviceResponse.documents[0].issuerSigned.issuerNamespaces?.issuerNamespaces ?? [])).reduce((prev, [key, value]) => ({
...prev,
[key]: value.reduce((prev, curr) => ({
...prev,
[curr.elementIdentifier]: curr.elementValue
}), {})
}), {})
};
}
if (presentation.claimFormat === "jwt_vp") {
const vc = Array.isArray(presentation.verifiableCredential) ? presentation.verifiableCredential[0] : presentation.verifiableCredential;
return {
cryptographic_holder_binding: true,
credential_format: "jwt_vc_json",
authority: this.getAuthorityForCredential(vc),
claims: vc.jsonCredential,
type: vc.type
};
}
if (presentation.claimFormat === "ldp_vp") {
const vc = Array.isArray(presentation.verifiableCredential) ? presentation.verifiableCredential[0] : presentation.verifiableCredential;
const expandedTypes = await agentContext.dependencyManager.resolve(W3cJsonLdCredentialService).getExpandedTypesForCredential(agentContext, vc);
return {
cryptographic_holder_binding: true,
credential_format: "ldp_vc",
authority: this.getAuthorityForCredential(vc),
claims: vc.jsonCredential,
type: expandedTypes
};
}
if (presentation.claimFormat === "vp+sd-jwt") {
const firstEntry = Array.isArray(presentation.resolvedPresentation.verifiableCredential) ? presentation.resolvedPresentation.verifiableCredential[0] : presentation.resolvedPresentation.verifiableCredential;
if (!(firstEntry instanceof W3cV2EnvelopedVerifiableCredential)) throw new DcqlError("Expected an EnvelopedVerifiableCredential entry for vc+sd-jwt presentation claim extraction");
return {
cryptographic_holder_binding: true,
credential_format: "vc+sd-jwt",
authority: this.getAuthorityForCredential(firstEntry),
type: asArray(firstEntry.resolvedCredential.type),
claims: firstEntry.resolvedCredential.toJSON()
};
}
throw new DcqlError("Unsupported claim format for presentation");
}
async getCredentialsForRequest(agentContext, dcqlQuery) {
const credentialRecords = await this.queryCredentialsForDcqlQuery(agentContext, dcqlQuery);
const credentialRecordsWithFormatDuplicates = [];
const parsedQuery = DcqlQuery.parse(dcqlQuery);
const dcqlCredentials = credentialRecords.flatMap((record) => {
if (record.type === "MdocRecord") {
credentialRecordsWithFormatDuplicates.push(record);
const mdoc = record.firstCredential;
return {
authority: this.getAuthorityForCredential(mdoc),
credential_format: "mso_mdoc",
doctype: mdoc.docType,
namespaces: mdoc.issuerSignedNamespaces,
cryptographic_holder_binding: true
};
}
if (record.type === "SdJwtVcRecord") {
const sdJwtVc = record.firstCredential;
const claims = sdJwtVc.prettyClaims;
credentialRecordsWithFormatDuplicates.push(record, record, ...record.extendedVctValues.map(() => record), ...record.extendedVctValues.map(() => record));
return [
{
authority: this.getAuthorityForCredential(sdJwtVc),
credential_format: "dc+sd-jwt",
vct: record.getTags().vct,
claims,
cryptographic_holder_binding: true
},
...record.extendedVctValues.map((extendedVctValue) => ({
authority: this.getAuthorityForCredential(sdJwtVc),
credential_format: "dc+sd-jwt",
vct: extendedVctValue,
claims,
cryptographic_holder_binding: true
})),
{
authority: this.getAuthorityForCredential(sdJwtVc),
credential_format: "vc+sd-jwt",
vct: record.getTags().vct,
claims,
cryptographic_holder_binding: true
},
...record.extendedVctValues.map((extendedVctValue) => ({
authority: this.getAuthorityForCredential(sdJwtVc),
credential_format: "vc+sd-jwt",
vct: extendedVctValue,
claims,
cryptographic_holder_binding: true
}))
];
}
if (record.type === "W3cCredentialRecord") {
const firstCredential = record.firstCredential;
credentialRecordsWithFormatDuplicates.push(record);
if (firstCredential.claimFormat === "ldp_vc") return {
credential_format: "ldp_vc",
authority: this.getAuthorityForCredential(firstCredential),
type: record.getTags().expandedTypes ?? [],
claims: firstCredential.jsonCredential,
cryptographic_holder_binding: true
};
return {
credential_format: "jwt_vc_json",
authority: this.getAuthorityForCredential(firstCredential),
type: firstCredential.type,
claims: firstCredential.jsonCredential,
cryptographic_holder_binding: true
};
}
if (record.type === "W3cV2CredentialRecord") {
credentialRecordsWithFormatDuplicates.push(record);
const firstCredential = record.firstCredential;
return {
credential_format: "vc+sd-jwt",
authority: this.getAuthorityForCredential(firstCredential),
type: asArray(firstCredential.resolvedCredential.type),
claims: firstCredential.resolvedCredential.toJSON(),
cryptographic_holder_binding: true
};
}
throw new DcqlError("Unsupported record type");
});
const queryResult = DcqlQuery.query(parsedQuery, dcqlCredentials);
const matchesWithRecord = Object.fromEntries(Object.entries(queryResult.credential_matches).map(([credential_query_id, result]) => {
const failedCredentials = result.failed_credentials ? mapNonEmptyArray(result.failed_credentials, (credential) => {
const record = credentialRecordsWithFormatDuplicates[credential.input_credential_index];
return {
...credential,
record,
claims: credential.claims.success ? {
...credential.claims,
success: true,
valid_claim_sets: mapNonEmptyArray(credential.claims.valid_claim_sets, (claimSet) => ({
...claimSet,
...this.getDisclosedClaimSetOutput(record, claimSet, parsedQuery, credential_query_id)
}))
} : credential.claims
};
}) : void 0;
if (!result.success) return [credential_query_id, {
...result,
failed_credentials: failedCredentials
}];
return [credential_query_id, {
...result,
failed_credentials: failedCredentials,
valid_credentials: mapNonEmptyArray(result.valid_credentials, (credential) => {
const record = credentialRecordsWithFormatDuplicates[credential.input_credential_index];
return {
...credential,
record,
claims: {
...credential.claims,
valid_claim_sets: mapNonEmptyArray(credential.claims.valid_claim_sets, (claimSet) => ({
...claimSet,
...this.getDisclosedClaimSetOutput(record, claimSet, parsedQuery, credential_query_id)
}))
}
};
})
}];
}));
return {
...queryResult,
credential_matches: matchesWithRecord
};
}
/**
* What a selectively disclosable credential discloses for the output of a claim set.
*
* The output of DCQL only holds the requested claims, while a claim that is not selectively
* disclosable is always disclosed. So the output is replaced with the claims as the verifier receives
* them, and `disclosed_paths` added: the paths to those claims in the credential, which are what to
* disclose when presenting it.
*
* Other credentials disclose the output as it is.
*/
getDisclosedClaimSetOutput(record, claimSet, dcqlQuery, credentialQueryId) {
const getClaimPaths = () => {
const claimsQueries = dcqlQuery.credentials.find((credential) => credential.id === credentialQueryId)?.claims;
const claimsQueryPaths = (claimSet.valid_claim_indexes ?? []).flatMap((index) => {
const claimsQuery = claimsQueries?.[index];
return claimsQuery && "path" in claimsQuery ? [claimsQuery.path] : [];
});
return getClaimPathsForDcqlClaimSet(claimsQueryPaths, claimSet.output);
};
if (record.type === "SdJwtVcRecord") {
const { prettyClaims, disclosedPaths } = applyDisclosuresForPaths(record.encoded, getClaimPaths());
return {
output: prettyClaims,
disclosed_paths: disclosedPaths
};
}
if (record.type === "W3cV2CredentialRecord" && record.firstCredential instanceof W3cV2SdJwtVerifiableCredential) {
const { prettyClaims, disclosedPaths } = applyDisclosuresForPaths(record.firstCredential.encoded, getClaimPaths());
return {
output: JsonTransformer.fromJSON(prettyClaims, W3cV2Credential, { validate: false }).toJSON(),
disclosed_paths: disclosedPaths
};
}
return {};
}
async assertValidDcqlPresentation(agentContext, dcqlPresentation, dcqlQuery) {
const internalDcqlPresentation = Object.fromEntries(await Promise.all(Object.entries(dcqlPresentation).map(async ([credentialId, presentations]) => {
const queryCredential = dcqlQuery.credentials.find((c) => c.id === credentialId);
if (!queryCredential) throw new DcqlError(`DCQL presentation contains presentation entry for credential id '${credentialId}', but this id is not present in the DCQL query`);
return [credentialId, await Promise.all(presentations.map((presentation) => this.getDcqlCredentialRepresentation(agentContext, presentation, queryCredential)))];
})));
const presentationResult = DcqlPresentationResult.fromDcqlPresentation(internalDcqlPresentation, { dcqlQuery });
if (!presentationResult.can_be_satisfied) throw new DcqlError("Presentations do not satisfy the DCQL query.", { additionalMessages: Object.entries(presentationResult.credential_matches ?? {}).flatMap(([queryId, match]) => match.success ? void 0 : !match.failed_credentials ? `Unable to match query credential '${queryId}'. No prsentations provided` : match.failed_credentials.map((failedCredential) => `Presentation at index ${failedCredential.input_credential_index} does not match query credential '${queryId}'. ${JSON.stringify({
...failedCredential.claims.success ? {} : { claims: failedCredential.claims.failed_claim_sets.map((cs) => cs.issues) },
...failedCredential.trusted_authorities.success ? {} : { trusted_authorities: failedCredential.trusted_authorities.failed_trusted_authorities.map((ta) => ta.issues) },
...failedCredential.meta.success ? {} : { meta: failedCredential.meta.issues }
}, null, 2)}`)).filter((message) => message !== void 0) });
return presentationResult;
}
getSelectiveDisclosure(validCredential) {
const [{ output, disclosed_paths }] = validCredential.claims.valid_claim_sets;
return disclosed_paths ? {
disclosedPaths: disclosed_paths,
disclosedPayload: output
} : { disclosedPayload: output };
}
dcqlCredentialForRequestForValidCredential(validCredential) {
if (validCredential.record.type === "MdocRecord") return {
claimFormat: "mso_mdoc",
credentialRecord: validCredential.record,
disclosedPayload: validCredential.claims.valid_claim_sets[0].output
};
if (validCredential.record.type === "SdJwtVcRecord") return {
claimFormat: "dc+sd-jwt",
credentialRecord: validCredential.record,
...this.getSelectiveDisclosure(validCredential)
};
if (validCredential.record.type === "W3cCredentialRecord") return {
claimFormat: validCredential.record.firstCredential.claimFormat,
credentialRecord: validCredential.record,
disclosedPayload: validCredential.record.firstCredential.jsonCredential
};
if (validCredential.record.type === "W3cV2CredentialRecord") {
const claimFormat = validCredential.record.firstCredential.claimFormat;
if (claimFormat === "di_vc") throw new DcqlError("DCQL credential selection does not support W3C V2 Data Integrity credentials yet");
if (claimFormat === "vc+sd-jwt") return {
claimFormat,
credentialRecord: validCredential.record,
...this.getSelectiveDisclosure(validCredential)
};
return {
claimFormat,
credentialRecord: validCredential.record,
disclosedPayload: validCredential.claims.valid_claim_sets[0].output
};
}
throw new DcqlError("Unsupported record type for DCQL");
}
/**
* Selects the credentials to use based on the output from `getCredentialsForRequest`
* Use this method if you don't want to manually select the credentials yourself.
*/
selectCredentialsForRequest(dcqlQueryResult, { useMode = "NewOrFirst" } = {}) {
if (!dcqlQueryResult.can_be_satisfied) throw new DcqlError("Cannot select the credentials for the dcql query presentation if the request cannot be satisfied");
const credentials = {};
if (dcqlQueryResult.credential_sets) credentialSetLoop: for (const credentialSet of dcqlQueryResult.credential_sets) {
if (credentialSet.required === false) continue;
const fullfillableOptions = credentialSet.matching_options;
if (!fullfillableOptions) throw new DcqlError("Invalid dcql query result. No option is fullfillable");
for (const fullfillableOption of fullfillableOptions) {
const optionMatches = fullfillableOption.map((credentialQueryId) => {
const credentialMatch = dcqlQueryResult.credential_matches[credentialQueryId];
if (!credentialMatch.success) return void 0;
const match = credentialMatch.valid_credentials.find((match) => canUseInstanceFromCredentialRecord({
credentialRecord: match.record,
useMode
}));
if (!match) return void 0;
return {
match,
credentialQueryId
};
});
if (optionMatches.every((c) => c !== void 0)) {
for (const { match, credentialQueryId } of optionMatches) credentials[credentialQueryId] = [this.dcqlCredentialForRequestForValidCredential(match)];
continue credentialSetLoop;
}
}
throw new DcqlError("Unable to select credentials for credential set. No new credential instance available on any of the available credentials.");
}
else for (const credentialQuery of dcqlQueryResult.credentials) {
const credentialMatch = dcqlQueryResult.credential_matches[credentialQuery.id];
if (!credentialMatch.success) throw new DcqlError(`Invalid dcql query result for credential query id '${credentialQuery.id}'. Cannot auto-select credentials`);
const credential = credentialMatch.valid_credentials.find((match) => canUseInstanceFromCredentialRecord({
credentialRecord: match.record,
useMode
}));
if (!credential) throw new DcqlError(`Unable to select credential for credential query id '${credentialQuery.id}'. No new credential instance available on any of the available credentials.`);
credentials[credentialQuery.id] = [this.dcqlCredentialForRequestForValidCredential(credential)];
}
return credentials;
}
validateDcqlQuery(dcqlQuery) {
const parsed = DcqlQuery.parse(dcqlQuery);
DcqlQuery.validate(parsed);
return parsed;
}
async createPresentation(agentContext, options) {
const { domain, challenge, mdocSessionTranscript } = options;
const dcqlPresentation = {};
const encodedDcqlPresentation = {};
const vcPresentationsToCreate = dcqlGetPresentationsToCreate(options.credentialQueryToCredential);
for (const [credentialQueryId, presentationsToCreate] of Object.entries(vcPresentationsToCreate)) for (const presentationToCreate of presentationsToCreate) {
let createdPresentation;
let encodedCreatedPresentation;
if (presentationToCreate.claimFormat === "mso_mdoc") {
const mdocRecord = presentationToCreate.credentialRecord;
if (!mdocSessionTranscript) throw new DcqlError("Missing mdoc session transcript options for creating MDOC presentation.");
const { credentialInstance } = await useInstanceFromCredentialRecord({
agentContext,
useMode: presentationToCreate.useMode,
credentialRecord: mdocRecord
});
const deviceResponseBase64Url = (await MdocDeviceResponse.createDeviceResponse(agentContext, {
mdocs: [credentialInstance],
documentRequests: [{
docType: mdocRecord.getTags().docType,
nameSpaces: Object.fromEntries(Object.entries(presentationToCreate.disclosedPayload).map(([key, value]) => {
return [key, Object.fromEntries(Object.entries(value).map(([key]) => [key, false]))];
}))
}],
sessionTranscriptOptions: mdocSessionTranscript
})).encoded;
encodedCreatedPresentation = deviceResponseBase64Url;
createdPresentation = MdocDeviceResponse.fromBase64Url(deviceResponseBase64Url);
} else if (presentationToCreate.claimFormat === "dc+sd-jwt") {
if (!domain) throw new DcqlError("Missing domain property for creating SdJwtVc presentation.");
const { credentialInstance } = await useInstanceFromCredentialRecord({
agentContext,
useMode: presentationToCreate.useMode,
credentialRecord: presentationToCreate.credentialRecord
});
const presentationFrame = this.getPresentationFrame(typeof credentialInstance === "string" ? credentialInstance : credentialInstance.compact, presentationToCreate);
const sdJwtVcApi = this.getSdJwtVcApi(agentContext);
const presentation = await sdJwtVcApi.present({
sdJwtVc: credentialInstance,
presentationFrame,
verifierMetadata: {
audience: domain,
nonce: challenge,
issuedAt: Math.floor(Date.now() / 1e3)
},
additionalPayload: presentationToCreate.additionalPayload
});
encodedCreatedPresentation = presentation;
createdPresentation = sdJwtVcApi.fromCompact(presentation);
} else if (presentationToCreate.claimFormat === "jwt_vp") {
const { credentialInstance } = await useInstanceFromCredentialRecord({
agentContext,
useMode: presentationToCreate.useMode,
credentialRecord: presentationToCreate.credentialRecord
});
if (!credentialInstance.credentialSubjectIds[0]) throw new DcqlError("Cannot create presentation for credentials without subject id");
const verificationMethod = await this.getVerificationMethodForSubjectId(agentContext, credentialInstance.credentialSubjectIds[0]);
const w3cCredentialService = agentContext.resolve(W3cCredentialService);
const w3cPresentation = new W3cPresentation({
verifiableCredential: [credentialInstance],
holder: verificationMethod.controller
});
const publicJwk = getPublicJwkFromVerificationMethod(verificationMethod);
const signedPresentation = await w3cCredentialService.signPresentation(agentContext, {
format: "jwt_vp",
alg: publicJwk.signatureAlgorithm,
verificationMethod: verificationMethod.id,
presentation: w3cPresentation,
challenge,
domain
});
encodedCreatedPresentation = signedPresentation.encoded;
createdPresentation = signedPresentation;
} else if (presentationToCreate.claimFormat === "ldp_vp") {
const { credentialInstance } = await useInstanceFromCredentialRecord({
agentContext,
useMode: presentationToCreate.useMode,
credentialRecord: presentationToCreate.credentialRecord
});
if (!credentialInstance.credentialSubjectIds[0]) throw new DcqlError("Cannot create presentation for credentials without subject id");
const verificationMethod = await this.getVerificationMethodForSubjectId(agentContext, credentialInstance.credentialSubjectIds[0]);
const w3cCredentialService = agentContext.resolve(W3cCredentialService);
const w3cPresentation = new W3cPresentation({
verifiableCredential: [credentialInstance],
holder: verificationMethod.controller
});
const signedPresentation = await w3cCredentialService.signPresentation(agentContext, {
format: "ldp_vp",
proofType: this.getProofTypeForLdpVc(agentContext, verificationMethod),
proofPurpose: new purposes.AuthenticationProofPurpose({
challenge,
domain
}),
verificationMethod: verificationMethod.id,
presentation: w3cPresentation,
challenge,
domain
});
encodedCreatedPresentation = signedPresentation.encoded;
createdPresentation = signedPresentation;
} else if (presentationToCreate.claimFormat === "vp+jwt") {
const w3cV2CredentialService = agentContext.resolve(W3cV2CredentialService);
const firstCredential = presentationToCreate.credentialRecord.firstCredential;
if (firstCredential.claimFormat === "di_vc") throw new DcqlError("DCQL JWT W3C VP creation does not support W3C V2 Data Integrity credentials yet");
const w3cV2Presentation = new W3cV2Presentation({
holder: firstCredential.resolvedCredential.credentialSubjectIds[0],
verifiableCredential: [W3cV2EnvelopedVerifiableCredential.fromVerifiableCredential(firstCredential)]
});
const signedPresentation = await w3cV2CredentialService.signPresentation(agentContext, {
format: "vp+jwt",
presentation: w3cV2Presentation,
challenge,
domain
});
encodedCreatedPresentation = signedPresentation.encoded;
createdPresentation = signedPresentation;
} else if (presentationToCreate.claimFormat === "vp+sd-jwt") {
const presentationFrame = this.getPresentationFrame(presentationToCreate.credentialRecord.firstCredential.encoded, presentationToCreate);
if (!domain) throw new DcqlError("Missing domain property for creating SdJwtVc presentation.");
const sdJwtVc = await agentContext.resolve(W3cV2SdJwtCredentialService).present(agentContext, {
credential: presentationToCreate.credentialRecord.firstCredential.encoded,
presentationFrame
});
const w3cV2CredentialService = agentContext.resolve(W3cV2CredentialService);
const w3cV2Presentation = new W3cV2Presentation({
holder: presentationToCreate.credentialRecord.firstCredential.resolvedCredential.credentialSchemaIds[0],
verifiableCredential: [W3cV2EnvelopedVerifiableCredential.fromVerifiableCredential(sdJwtVc)]
});
const signedPresentation = await w3cV2CredentialService.signPresentation(agentContext, {
format: "vp+sd-jwt",
presentation: w3cV2Presentation,
challenge,
domain
});
encodedCreatedPresentation = signedPresentation.encoded;
createdPresentation = signedPresentation;
} else throw new DcqlError("Unsupported presentation format.");
if (!dcqlPresentation[credentialQueryId]) dcqlPresentation[credentialQueryId] = [createdPresentation];
else dcqlPresentation[credentialQueryId].push(createdPresentation);
if (!encodedDcqlPresentation[credentialQueryId]) encodedDcqlPresentation[credentialQueryId] = [encodedCreatedPresentation];
else encodedDcqlPresentation[credentialQueryId].push(encodedCreatedPresentation);
}
return {
dcqlPresentation,
encodedDcqlPresentation
};
}
getPresentationFrame(compactSdJwt, { disclosedPaths, disclosedPayload }) {
if (disclosedPaths) return buildPresentationFrameForPaths(compactSdJwt, disclosedPaths);
if (disclosedPayload) return buildDisclosureFrameForPayload(disclosedPayload);
throw new DcqlError("Either disclosedPaths or disclosedPayload is required to present a selectively disclosable credential.");
}
getSdJwtVcApi(agentContext) {
return agentContext.dependencyManager.resolve(SdJwtVcApi);
}
getMdocApi(agentContext) {
return agentContext.dependencyManager.resolve(MdocApi);
}
async getVerificationMethodForSubjectId(agentContext, subjectId) {
const didsApi = agentContext.dependencyManager.resolve(DidsApi);
if (!subjectId.startsWith("did:")) throw new DcqlError(`Only dids are supported as credentialSubject id. ${subjectId} is not a valid did`);
const didDocument = await didsApi.resolveDidDocument(subjectId);
if (!didDocument.authentication || didDocument.authentication.length === 0) throw new DcqlError(`No authentication verificationMethods found for did ${subjectId} in did document`);
const [verificationMethod] = didDocument.findVerificationMethodsByPurpose(["authentication"]);
return verificationMethod;
}
getProofTypeForLdpVc(agentContext, verificationMethod) {
const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry);
const publicJwk = getPublicJwkFromVerificationMethod(verificationMethod);
const supportedSignatureSuites = signatureSuiteRegistry.getAllByPublicJwkType(publicJwk);
if (supportedSignatureSuites.length === 0) throw new DcqlError(`Couldn't find a supported signature suite for the given jwk ${publicJwk.jwkTypeHumanDescription}`);
return supportedSignatureSuites[0].proofType;
}
};
DcqlService = __decorate([injectable()], DcqlService);
//#endregion
export { DcqlService };
//# sourceMappingURL=DcqlService.mjs.map