UNPKG

@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
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