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

1 lines • 21.4 kB
{"version":3,"file":"MdocDeviceResponse.mjs","names":[],"sources":["../../../src/modules/mdoc/MdocDeviceResponse.ts"],"sourcesContent":["import {\n CoseKey,\n DeviceRequest,\n DeviceResponse,\n DocRequest,\n Document,\n defaultVerificationCallback,\n Holder,\n ItemsRequest,\n type MdocContext,\n SessionTranscript,\n} from '@owf/mdoc'\nimport type { InputDescriptorV2 } from '@sphereon/pex-models'\nimport {\n convertDcqlQueryToDeviceRequest,\n convertPresentationDefinitionToDeviceRequest,\n type Field,\n type PresentationDefinition,\n} from '@verifiables/request-converter'\nimport { AgentContext } from '../../agent'\nimport { getMdocContext } from '../../crypto/contexts/mdocContext'\nimport { TypedArrayEncoder } from './../../utils'\nimport { PublicJwk } from '../kms'\nimport { ClaimFormat } from '../vc'\nimport type { Mdoc } from './Mdoc'\nimport { MdocError } from './MdocError'\nimport type {\n MdocDeviceResponseDcqlQueryOptions,\n MdocDeviceResponseOptions,\n MdocDeviceResponsePresentationDefinitionOptions,\n MdocDeviceResponseVerifyOptions,\n MdocSessionTranscriptOptions,\n} from './MdocOptions'\nimport { isMdocSupportedSignatureAlgorithm, mdocSupportedSignatureAlgorithms } from './mdocSupportedAlgs'\nimport {\n assertMdocStatusListChainsMatchIssuanceChain,\n convertMdocTrustedCertificates,\n mdocTrustedCertificatesToRaw,\n nameSpacesRecordToDeviceNamespaces,\n nameSpacesRecordToMap,\n} from './mdocUtil'\nimport { convertDocumentRequest } from './utils/convertDocumentRequest'\n\nexport type DeviceAndIssuerClaims = {\n [docType: string]: {\n [namespace: string]: {\n [claimIdentifier: string]: unknown\n }\n }\n}\n\nexport class MdocDeviceResponse {\n private constructor(public readonly deviceResponse: DeviceResponse) {}\n\n /**\n * claim format is convenience method added to all credential instances\n */\n public get claimFormat() {\n return ClaimFormat.MsoMdoc as const\n }\n\n /**\n * Encoded is convenience method added to all credential instances\n */\n public get encoded() {\n return TypedArrayEncoder.toBase64Url(this.deviceResponse.encode())\n }\n\n /**\n * To support a single DeviceResponse with multiple documents in OpenID4VP\n */\n public splitIntoSingleDocumentResponses(): MdocDeviceResponse[] {\n const deviceResponses: MdocDeviceResponse[] = []\n\n if (!this.deviceResponse.documents || this.deviceResponse.documents.length === 0) {\n throw new MdocError('mdoc device response does not contain any mdocs')\n }\n\n for (const document of this.deviceResponse.documents) {\n deviceResponses.push(\n new MdocDeviceResponse(\n DeviceResponse.createSimple({\n version: this.deviceResponse.version,\n status: this.deviceResponse.status,\n documentErrors: this.deviceResponse.documentErrors,\n documents: [document],\n })\n )\n )\n }\n\n return deviceResponses\n }\n\n public static fromBase64Url(base64Url: string) {\n const parsed = DeviceResponse.decode(TypedArrayEncoder.fromBase64Url(base64Url))\n\n return new MdocDeviceResponse(parsed)\n }\n\n public static async createDeviceResponseWithPresentationDefinition(\n agentContext: AgentContext,\n options: MdocDeviceResponsePresentationDefinitionOptions\n ) {\n // @ts-expect-error: we need to match the types credo uses and the converter expects\n const { deviceRequest } = convertPresentationDefinitionToDeviceRequest(options.presentationDefinition)\n const documentRequests = convertDocumentRequest(deviceRequest.docRequests)\n\n return MdocDeviceResponse.createDeviceResponse(agentContext, { ...options, documentRequests })\n }\n\n /**\n * @deprecated Credo does not use this method itself, and it only supports part of DCQL. Every claim\n * of every `mso_mdoc` credential query is requested: `credential_sets`, `claim_sets` and `values`\n * are ignored, credential queries with the same doctype are merged, nested claim paths are\n * truncated, and a claim missing from the mdoc throws instead of being treated as optional. Use\n * `DcqlService.createPresentation` to present mdocs for a DCQL query, or\n * {@link MdocDeviceResponse.createDeviceResponse} with explicit `documentRequests`.\n */\n public static async createDeviceResponseWithDcqlQuery(\n agentContext: AgentContext,\n options: MdocDeviceResponseDcqlQueryOptions\n ) {\n // @ts-expect-error: we need to match the types credo uses and the converter expects\n const { deviceRequest } = convertDcqlQueryToDeviceRequest(options.dcqlQuery)\n const documentRequests = convertDocumentRequest(deviceRequest.docRequests)\n\n return MdocDeviceResponse.createDeviceResponse(agentContext, { ...options, documentRequests })\n }\n\n public static async limitDisclosureToInputDescriptor(\n _agentContext: AgentContext,\n { mdoc, ...options }: { inputDescriptor: InputDescriptorV2; mdoc: Mdoc }\n ) {\n const inputDescriptor = MdocDeviceResponse.assertMdocInputDescriptor(options.inputDescriptor)\n\n // First we try with all optional fields enabled, if the mdoc cannot satisfy that request, we try again, skipping all optional fields\n const docRequestMatch =\n MdocDeviceResponse.matchInputDescriptor(mdoc, inputDescriptor, { skipOptionalFields: false }) ??\n MdocDeviceResponse.matchInputDescriptor(mdoc, inputDescriptor, { skipOptionalFields: true })\n\n if (!docRequestMatch) {\n throw new MdocError(`Mdoc with doctype ${mdoc.docType} does not satisfy the input descriptor`)\n }\n\n const disclosedPayloadAsRecord: Record<string, Record<string, unknown>> = {}\n for (const claim of docRequestMatch.claims.validClaims) {\n disclosedPayloadAsRecord[claim.namespace] ??= {}\n disclosedPayloadAsRecord[claim.namespace][claim.disclosedElementIdentifier] = claim.elementValue\n }\n\n return disclosedPayloadAsRecord\n }\n\n private static matchInputDescriptor(\n mdoc: Mdoc,\n inputDescriptor: PresentationDefinition['input_descriptors'][number],\n { skipOptionalFields }: { skipOptionalFields: boolean }\n ) {\n // We take the first document request as we also only input a single input descriptor\n const {\n deviceRequest: {\n docRequests: [convertedDocumentRequest],\n },\n } = convertPresentationDefinitionToDeviceRequest(\n {\n id: '<UNUSED_CREDO_ID>',\n input_descriptors: [inputDescriptor],\n },\n { skipOptionalFields }\n )\n\n const { docRequests } = Holder.matchDeviceRequest({\n deviceRequest: DeviceRequest.create({\n version: '1.0',\n docRequests: [\n DocRequest.create({\n itemsRequest: ItemsRequest.create({\n docType: convertedDocumentRequest.itemsRequest.docType,\n namespaces: convertedDocumentRequest.itemsRequest.nameSpaces,\n }),\n }),\n ],\n }),\n credentials: [mdoc.issuerSigned],\n })\n\n return docRequests[0].success ? docRequests[0].validCredentials[0] : undefined\n }\n\n private static assertMdocInputDescriptor(inputDescriptor: InputDescriptorV2) {\n if (!inputDescriptor.format?.mso_mdoc) {\n throw new MdocError(`Input descriptor must contain 'mso_mdoc' format property`)\n }\n\n if (!inputDescriptor.format.mso_mdoc.alg) {\n throw new MdocError(`Input descriptor mso_mdoc must contain 'alg' property`)\n }\n\n if (!inputDescriptor.constraints?.limit_disclosure || inputDescriptor.constraints.limit_disclosure !== 'required') {\n throw new MdocError(\n `Input descriptor must contain 'limit_disclosure' constraints property which is set to required`\n )\n }\n\n if (!inputDescriptor.constraints?.fields?.every((field) => field.intent_to_retain !== undefined)) {\n throw new MdocError(`Input descriptor must contain 'intent_to_retain' constraints property`)\n }\n\n return {\n ...inputDescriptor,\n format: {\n mso_mdoc: inputDescriptor.format.mso_mdoc,\n },\n constraints: {\n ...inputDescriptor.constraints,\n limit_disclosure: 'required',\n fields: (inputDescriptor.constraints.fields ?? []).map((field) => {\n return {\n ...field,\n intent_to_retain: field.intent_to_retain ?? false,\n }\n }) as Field[],\n },\n } satisfies PresentationDefinition['input_descriptors'][number]\n }\n\n public static async createDeviceResponse(agentContext: AgentContext, options: MdocDeviceResponseOptions) {\n const documents: Document[] = []\n\n for (const document of options.mdocs) {\n const deviceKeyJwk = document.deviceKey\n const deviceKeyJwkJson = deviceKeyJwk.toJson()\n\n if (!deviceKeyJwkJson.alg) {\n // FIXME: we cannot provide alg anymore?\n deviceKeyJwkJson.alg = MdocDeviceResponse.getAlgForDeviceKeyJwk(deviceKeyJwk)\n }\n\n // Set keyId to legacy key id if it doesn't have a key id set\n if (!deviceKeyJwk.hasKeyId) {\n deviceKeyJwk.keyId = deviceKeyJwk.legacyKeyId\n }\n\n const deviceRequestForDocument = DeviceRequest.create({\n version: '1.0',\n docRequests: options.documentRequests\n .filter((request) => request.docType === document.docType)\n .map((request) =>\n DocRequest.create({\n itemsRequest: ItemsRequest.create({\n docType: request.docType,\n namespaces: nameSpacesRecordToMap(request.nameSpaces),\n }),\n })\n ),\n })\n\n const mdocContext = getMdocContext(agentContext)\n const deviceResponse = await DeviceResponse.createWithDeviceRequest(\n {\n deviceRequest: deviceRequestForDocument,\n sessionTranscript: await MdocDeviceResponse.calculateSessionTranscriptBytes(\n mdocContext,\n options.sessionTranscriptOptions\n ),\n documents: [\n {\n issuerSigned: document.issuerSigned,\n // `deviceRequestForDocument` only holds doc requests for this document's doc type, and\n // only the first disclosed document is kept below, so answer the first one.\n docRequestIndex: 0,\n deviceNamespaces: options.deviceNameSpaces\n ? nameSpacesRecordToDeviceNamespaces(options.deviceNameSpaces)\n : undefined,\n signature: {\n signingKey: CoseKey.fromJwk(deviceKeyJwk.toJson()),\n },\n },\n ],\n },\n mdocContext\n )\n\n if (!deviceResponse.documents) {\n throw new MdocError('Device response does not contain any documents')\n }\n documents.push(deviceResponse.documents[0])\n }\n\n return new MdocDeviceResponse(\n DeviceResponse.createSimple({\n version: '1.0',\n documents,\n })\n )\n }\n\n public async verify(agentContext: AgentContext, options: Omit<MdocDeviceResponseVerifyOptions, 'deviceResponse'>) {\n const mdocContext = getMdocContext(agentContext)\n\n defaultVerificationCallback({\n status: this.deviceResponse.documents?.length ? 'PASSED' : 'FAILED',\n check: 'Device Response must include at least one document.',\n category: 'DOCUMENT_FORMAT',\n })\n\n const trustedCertificates = convertMdocTrustedCertificates(options.trustedCertificates ?? [])\n\n const { documents: verificationResults } = await this.deviceResponse\n .verify(\n {\n trustedCertificates: mdocTrustedCertificatesToRaw(trustedCertificates),\n disableCertificateChainValidation: false,\n now: options.now,\n skewSeconds: agentContext.config.validitySkewSeconds,\n sessionTranscript: await MdocDeviceResponse.calculateSessionTranscriptBytes(\n mdocContext,\n options.sessionTranscriptOptions\n ),\n },\n mdocContext\n )\n .catch((error) => {\n throw new MdocError(`Mdoc with doctype ${this.deviceResponse.documents?.[0].docType} is not valid`, {\n cause: error,\n })\n })\n\n for (const { trustedIssuanceChain, trustedStatusListChain, trustedIdentifierListChain } of verificationResults) {\n if (!trustedIssuanceChain) continue\n\n assertMdocStatusListChainsMatchIssuanceChain(trustedCertificates, {\n trustedIssuanceChain,\n trustedStatusListChain,\n trustedIdentifierListChain,\n })\n }\n }\n\n private static async calculateSessionTranscriptBytes(\n mdocContext: MdocContext,\n options: MdocSessionTranscriptOptions\n ) {\n if (options.type === 'sessionTranscriptBytes') {\n return options.sessionTranscriptBytes\n }\n\n if (options.type === 'openId4VpDraft18') {\n return await SessionTranscript.forOid4VpDraft18(options, mdocContext)\n }\n\n if (options.type === 'openId4Vp') {\n const { encryptionJwk, verifierGeneratedNonce, ...rest } = options\n return await SessionTranscript.forOid4Vp(\n {\n ...rest,\n jwkThumbprint: encryptionJwk ? encryptionJwk.getJwkThumbprint() : undefined,\n nonce: verifierGeneratedNonce,\n },\n mdocContext\n )\n }\n\n if (options.type === 'openId4VpDcApi') {\n const { encryptionJwk, verifierGeneratedNonce, ...rest } = options\n return await SessionTranscript.forOid4VpDcApi(\n {\n ...rest,\n nonce: verifierGeneratedNonce,\n jwkThumbprint: encryptionJwk ? encryptionJwk.getJwkThumbprint() : undefined,\n },\n mdocContext\n )\n }\n\n if (options.type === 'openId4VpDcApiDraft24') {\n const { verifierGeneratedNonce, ...rest } = options\n\n return await SessionTranscript.forOid4VpDcApiDraft24(\n {\n ...rest,\n nonce: verifierGeneratedNonce,\n },\n mdocContext\n )\n }\n\n throw new MdocError('Unsupported session transcript option')\n }\n\n private static getAlgForDeviceKeyJwk(jwk: PublicJwk) {\n const signatureAlgorithm = jwk.supportedSignatureAlgorithms.find(isMdocSupportedSignatureAlgorithm)\n if (!signatureAlgorithm) {\n throw new MdocError(\n `Unable to create mdoc device response. No supported signature algorithm found to sign device response for jwk ${\n jwk.jwkTypeHumanDescription\n }. Key supports algs ${jwk.supportedSignatureAlgorithms.join(\n ', '\n )}. mdoc supports algs ${mdocSupportedSignatureAlgorithms.join(', ')}`\n )\n }\n\n return signatureAlgorithm\n }\n\n public get issuerClaims(): DeviceAndIssuerClaims {\n if (!this.deviceResponse.documents) return {}\n\n return this.deviceResponse.documents.reduce(\n (prev, document) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: time complexity is not relevant here\n ...prev,\n [document.docType]: Object.fromEntries(\n Array.from(document.issuerSigned.issuerNamespaces?.issuerNamespaces.entries() ?? []).map(\n ([namespace, claim]) => [\n namespace,\n Object.fromEntries(claim.map((c) => [c.elementIdentifier, c.elementValue])),\n ]\n )\n ),\n }),\n {}\n )\n }\n\n public get deviceClaims(): DeviceAndIssuerClaims {\n if (!this.deviceResponse.documents) return {}\n\n return this.deviceResponse.documents.reduce(\n (prev, document) => ({\n // biome-ignore lint/performance/noAccumulatingSpread: time complexity is not relevant here\n ...prev,\n [document.docType]: Object.fromEntries(\n Array.from(document.deviceSigned.deviceNamespaces.deviceNamespaces.entries()).map(([namespace, claim]) => [\n namespace,\n Object.fromEntries(claim.deviceSignedItems),\n ])\n ),\n }),\n {}\n )\n }\n}\n"],"mappings":"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"}