{"version":3,"file":"MdocDcApiService.mjs","names":[],"sources":["../../../src/modules/mdoc/MdocDcApiService.ts"],"sourcesContent":["import {\n  CoseKey,\n  type CredentialMatchFailure,\n  type CredentialMatchSuccess,\n  defaultVerificationCallback,\n  EncryptionInfo,\n  Holder,\n  IsoMdocDcApi,\n  type IsoMdocDcApiParsedRequest,\n  type IsoMdocDcApiRequest,\n  type IsoMdocDcApiResponse,\n  onCategoryCheck,\n  reportDeviceRequestMatch,\n  Verifier,\n} from '@owf/mdoc'\nimport { AgentContext } from '../../agent'\nimport { EventEmitter } from '../../agent/EventEmitter'\nimport { TrustedIssuerContext } from '../../agent/TrustedIssuerContext'\nimport type { VerificationTypeMdocReaderAuth } from '../../agent/TrustedIssuersForVerification'\nimport { getMdocContext } from '../../crypto/contexts/mdocContext'\nimport { injectable } from '../../plugins'\nimport { mapNonEmptyArray } from '../../types'\nimport { TypedArrayEncoder } from '../../utils'\nimport {\n  CredentialMultiInstanceUseMode,\n  canUseInstanceFromCredentialRecord,\n  useInstanceFromCredentialRecord,\n} from '../../utils/credentialUse'\nimport { KeyManagementApi } from '../kms'\nimport { type EncodedX509Certificate, X509Certificate, X509ModuleConfig, X509Service } from '../x509'\nimport { convertLegacyTrustedCertificates } from '../x509/utils/convertLegacyTrustedCertificates'\nimport { Mdoc } from './Mdoc'\nimport { MdocDeviceResponse } from './MdocDeviceResponse'\nimport { MdocDeviceRequestNotSatisfiedError, MdocError, MdocVerificationSessionExpiredError } from './MdocError'\nimport { MdocEventTypes, type MdocVerificationSessionStateChangedEvent } from './MdocEvents'\nimport type {\n  MdocDcApiCreateResponseOptions,\n  MdocDcApiCreateVerificationSessionOptions,\n  MdocDcApiFailedCredential,\n  MdocDcApiResolvedDocRequest,\n  MdocDcApiResolvedDocRequestSuccess,\n  MdocDcApiResolvedRequest,\n  MdocDcApiResolveRequestOptions,\n  MdocDcApiValidCredential,\n  MdocDcApiVerifyResponseOptions,\n} from './MdocOptions'\nimport { MdocVerificationSessionState } from './MdocVerificationSessionState'\nimport { mdocSigningJwk, nameSpacesRecordToDeviceNamespaces } from './mdocUtil'\nimport {\n  MdocRecord,\n  MdocRepository,\n  MdocVerificationSessionRecord,\n  MdocVerificationSessionRepository,\n} from './repository'\nimport { createDeviceRequestDefinition, getDeviceRequestMatchOptions } from './utils/deviceRequestDefinition'\n\nconst defaultExpiresInSeconds = 5 * 60\n\n/**\n * Verifier and wallet APIs for the ISO/IEC TS 18013-7:2025 Annex C (`org-iso-mdoc`) DC API.\n *\n * @internal\n */\n@injectable()\nexport class MdocDcApiService {\n  public constructor(\n    private mdocRepository: MdocRepository,\n    private mdocVerificationSessionRepository: MdocVerificationSessionRepository,\n    private eventEmitter: EventEmitter\n  ) {}\n\n  /**\n   * Create an `org-iso-mdoc` request (verifier side) and persist a verification session for it.\n   *\n   * The origin is not part of the request payload; it only enters the protocol through the session\n   * transcript. It therefore has to be supplied again to {@link MdocDcApiService.verifyResponse},\n   * except that a `readerAuth` request signs over the transcript and is fixed to the origin it is\n   * created for.\n   */\n  public async createVerificationSession(\n    agentContext: AgentContext,\n    options: MdocDcApiCreateVerificationSessionOptions\n  ): Promise<{ verificationSession: MdocVerificationSessionRecord; request: IsoMdocDcApiRequest }> {\n    const kms = agentContext.resolve(KeyManagementApi)\n    const mdocContext = getMdocContext(agentContext)\n\n    const deviceRequestDefinition = createDeviceRequestDefinition(\n      options.docRequests,\n      options.treatAmbiguousMultipleDocRequestsAsAlternatives\n    )\n\n    const { keyId, publicJwk } = await kms.createKey({ type: { kty: 'EC', crv: 'P-256' } })\n\n    try {\n      const readerCertificateChain = options.readerAuth\n        ? Array.isArray(options.readerAuth.certificate)\n          ? options.readerAuth.certificate\n          : [options.readerAuth.certificate]\n        : undefined\n\n      const { request, encryptionInfo } = await IsoMdocDcApi.createRequest(\n        {\n          docRequests: deviceRequestDefinition.docRequests.map(({ docType, nameSpaces }) => ({\n            docType,\n            namespaces: Object.fromEntries(\n              Object.entries(nameSpaces).map(([nameSpace, elements]) => [\n                nameSpace,\n                Object.fromEntries(\n                  Object.entries(elements).map(([elementIdentifier, { intentToRetain }]) => [\n                    elementIdentifier,\n                    intentToRetain,\n                  ])\n                ),\n              ])\n            ),\n          })),\n          recipientPublicKey: CoseKey.fromJwk({ ...publicJwk, kid: keyId }),\n          readerAuth:\n            readerCertificateChain && options.readerAuth\n              ? {\n                  signingKey: CoseKey.fromJwk(mdocSigningJwk(readerCertificateChain[0].publicJwk)),\n                  certificateChain: readerCertificateChain.map((certificate) => certificate.rawCertificate),\n                  origin: options.readerAuth.origin,\n                }\n              : undefined,\n        },\n        mdocContext\n      )\n\n      const verificationSession = new MdocVerificationSessionRecord({\n        state: MdocVerificationSessionState.RequestCreated,\n        deviceRequestBase64Url: request.deviceRequest,\n        deviceRequestDefinition,\n        sessionTranscript: {\n          type: 'isoMdocDcApi',\n          encryptionInfoBase64Url: request.encryptionInfo,\n          nonce: TypedArrayEncoder.toBase64Url(encryptionInfo.nonce),\n        },\n        sessionKeyId: keyId,\n        expiresAt: new Date(Date.now() + (options.expiresInSeconds ?? defaultExpiresInSeconds) * 1000),\n      })\n\n      await this.mdocVerificationSessionRepository.save(agentContext, verificationSession)\n      this.emitStateChangedEvent(agentContext, verificationSession, null)\n\n      return { verificationSession, request }\n    } catch (error) {\n      await kms.deleteKey({ keyId }).catch((deleteError) => {\n        agentContext.config.logger.error(`Failed to delete session key ${keyId} after failed request creation`, {\n          deleteError,\n          requestCreationError: error,\n        })\n      })\n\n      throw error\n    }\n  }\n\n  /**\n   * Decrypt and verify an `org-iso-mdoc` response (verifier side).\n   *\n   * The response is bound to the origin through the session transcript, so decryption only succeeds\n   * for the origin the wallet actually used. That is what makes a response relayed from another\n   * origin fail.\n   */\n  public async verifyResponse(agentContext: AgentContext, options: MdocDcApiVerifyResponseOptions) {\n    const mdocContext = getMdocContext(agentContext)\n\n    const verificationSession = await this.mdocVerificationSessionRepository.getById(\n      agentContext,\n      options.verificationSessionId\n    )\n    verificationSession.assertState(MdocVerificationSessionState.RequestCreated)\n\n    const sessionTranscript = verificationSession.getSessionTranscript('isoMdocDcApi')\n    const response = options.response.response\n\n    try {\n      if (verificationSession.isExpired) {\n        throw new MdocVerificationSessionExpiredError(verificationSession.expiresAt)\n      }\n\n      // The recipient key is identified by the `keyId` on the public key, the same way signing keys\n      // are resolved. Take the public key from the `encryptionInfo` we sent, and bind the `keyId`\n      // from the session record so the KMS resolves the private key we created for this session.\n      const recipientKey = CoseKey.fromJwk({\n        ...EncryptionInfo.fromBase64Url(sessionTranscript.encryptionInfoBase64Url).recipientPublicKey.jwk,\n        kid: verificationSession.sessionKeyId,\n      })\n\n      const decrypted = await IsoMdocDcApi.decryptResponse(\n        {\n          response,\n          origin: options.origin,\n          encryptionInfo: sessionTranscript.encryptionInfoBase64Url,\n          recipientKey,\n        },\n        mdocContext\n      )\n\n      const deviceResponse = MdocDeviceResponse.fromBase64Url(\n        TypedArrayEncoder.toBase64Url(decrypted.deviceResponse.encode())\n      )\n\n      const sessionTranscriptOptions = {\n        type: 'sessionTranscriptBytes',\n        sessionTranscriptBytes: decrypted.sessionTranscript.encode(),\n      } as const\n\n      if (options.trustedCertificates) {\n        await deviceResponse.verify(agentContext, {\n          trustedCertificates: options.trustedCertificates,\n          sessionTranscriptOptions,\n          now: options.now,\n        })\n      } else {\n        // Trust is resolved per document, as each document can be issued by a different issuer and\n        // the trust callbacks are given the certificate chain of the document being verified.\n        for (const documentResponse of deviceResponse.splitIntoSingleDocumentResponses()) {\n          // biome-ignore lint/style/noNonNullAssertion: splitIntoSingleDocumentResponses always returns responses with exactly one document\n          const mdoc = new Mdoc(documentResponse.deviceResponse.documents![0].issuerSigned)\n\n          await documentResponse.verify(agentContext, {\n            trustedCertificates: await this.getTrustedCertificatesForMdoc(agentContext, mdoc),\n            sessionTranscriptOptions,\n            now: options.now,\n          })\n        }\n      }\n\n      // Annex C has no query language such as DCQL, so the device request we sent is what the\n      // response has to satisfy. Matched on the full response, as a doc request can be answered by\n      // any of the documents in it. The wallet selected its credentials with the same rules. The\n      // session's definition also carries whether the doc requests were meant as alternatives, of\n      // which the response only has to satisfy one.\n      const deviceRequestMatch = Verifier.matchDeviceRequest({\n        deviceRequest: TypedArrayEncoder.fromBase64Url(verificationSession.deviceRequestBase64Url),\n        deviceResponse: decrypted.deviceResponse,\n        matchOptions: getDeviceRequestMatchOptions(verificationSession.deviceRequestDefinition),\n      })\n\n      if (!deviceRequestMatch.success) {\n        const reasons: string[] = []\n        reportDeviceRequestMatch(deviceRequestMatch, ({ status, reason, check }) => {\n          if (status === 'FAILED') reasons.push(reason ?? check)\n        })\n\n        throw new MdocDeviceRequestNotSatisfiedError(\n          deviceRequestMatch,\n          `Device response does not satisfy the device request. ${reasons.join('. ')}`\n        )\n      }\n\n      verificationSession.sessionTranscript = { ...sessionTranscript, origin: options.origin }\n      await this.updateState(agentContext, verificationSession, MdocVerificationSessionState.ResponseVerified)\n\n      return { verificationSession, deviceResponse, deviceRequestMatch, origin: options.origin }\n    } catch (error) {\n      verificationSession.errorMessage = error instanceof Error ? error.message : 'Unknown error'\n      await this.updateState(agentContext, verificationSession, MdocVerificationSessionState.Error)\n\n      throw error\n    }\n  }\n\n  /**\n   * Resolve the trusted certificates for a single mdoc in a device response.\n   *\n   * Same resolution order as the other credential verification paths: the global\n   * `getTrustedIssuersForVerification` callback first, then the deprecated\n   * `getTrustedCertificatesForVerification` callback, and only if neither returns anything the\n   * statically configured trusted certificates. Both callbacks receive the certificate chain of\n   * the mdoc being verified, so trust can be decided per document.\n   */\n  private async getTrustedCertificatesForMdoc(agentContext: AgentContext, mdoc: Mdoc) {\n    const x509ModuleConfig = agentContext.dependencyManager.resolve(X509ModuleConfig)\n    const certificateChain = mdoc.issuerSignedCertificateChain.map((certificate) =>\n      X509Certificate.fromRawCertificate(certificate)\n    )\n\n    const trustedIssuers = await TrustedIssuerContext.getTrustedIssuersForVerification(agentContext, {\n      signer: {\n        method: 'x509',\n        certificateChain,\n      },\n      verification: {\n        type: 'credential',\n        credential: mdoc,\n      },\n    })\n\n    return (\n      trustedIssuers?.trustedIssuers ??\n      (await x509ModuleConfig.getTrustedCertificatesForVerification?.(agentContext, {\n        certificateChain,\n        verification: {\n          type: 'credential',\n          credential: mdoc,\n        },\n      })) ??\n      x509ModuleConfig.trustedCertificates ??\n      []\n    )\n  }\n\n  /**\n   * Assert the reader certificate chain of a single doc request is trusted.\n   *\n   * Same resolution order as the issuer trust in {@link MdocDcApiService.getTrustedCertificatesForMdoc}:\n   * the certificates passed to `resolveRequest` first, then the global\n   * `getTrustedIssuersForVerification` callback, then the deprecated\n   * `getTrustedCertificatesForVerification` callback, and only if none of these return anything\n   * the statically configured trusted certificates.\n   *\n   * As everywhere else x509 signers are verified, resolving nothing is not a reason to continue\n   * without establishing trust: the reader certificate chain is signed by the reader itself, so\n   * skipping the chain check would leave the reader identity self-asserted. A wallet that wants to\n   * decide on the reader identity itself can return the leaf certificate from the callback, which\n   * trusts the reader on the certificate it presented.\n   */\n  private async assertTrustedReaderCertificateChain(\n    agentContext: AgentContext,\n    options: {\n      certificateChain: X509Certificate[]\n      verification: VerificationTypeMdocReaderAuth\n      trustedReaderCertificates?: Array<EncodedX509Certificate | X509Certificate>\n      now?: Date\n    }\n  ) {\n    const x509ModuleConfig = agentContext.dependencyManager.resolve(X509ModuleConfig)\n\n    let trustedCertificates = options.trustedReaderCertificates?.map((certificate) =>\n      typeof certificate === 'string' ? certificate : certificate.toString('pem')\n    )\n\n    if (!trustedCertificates) {\n      const trustedIssuers = await TrustedIssuerContext.getTrustedIssuersForVerification(agentContext, {\n        signer: {\n          method: 'x509',\n          certificateChain: options.certificateChain,\n        },\n        verification: options.verification,\n      })\n\n      const legacyTrustedCertificates = trustedIssuers\n        ? undefined\n        : await x509ModuleConfig.getTrustedCertificatesForVerification?.(agentContext, {\n            certificateChain: options.certificateChain,\n            verification: options.verification,\n          })\n\n      trustedCertificates =\n        trustedIssuers?.trustedIssuers.flatMap(({ issuance }) => issuance) ??\n        (legacyTrustedCertificates\n          ? convertLegacyTrustedCertificates(legacyTrustedCertificates).flatMap(({ issuance }) => issuance)\n          : undefined) ??\n        x509ModuleConfig.trustedCertificates\n    }\n\n    if (!trustedCertificates) {\n      throw new MdocError('No trusted certificates found. Cannot verify reader authentication.')\n    }\n\n    // Reported as a check rather than thrown directly, so it fails the same way as the reader auth\n    // signature check in `parseRequest`. The default callback throws on a failed check.\n    const onCheck = onCategoryCheck(defaultVerificationCallback, 'READER_AUTH')\n    try {\n      await X509Service.validateCertificateChain(agentContext, {\n        certificateChain: options.certificateChain.map((certificate) => certificate.toString('pem')),\n        trustedCertificates,\n        verificationDate: options.now,\n      })\n\n      onCheck({\n        status: 'PASSED',\n        check: 'Reader certificate chain must be trusted',\n      })\n    } catch (error) {\n      onCheck({\n        status: 'FAILED',\n        check: 'Reader certificate chain must be trusted',\n        reason: error instanceof Error ? error.message : 'Unknown error',\n      })\n    }\n  }\n\n  /**\n   * Parse an incoming `org-iso-mdoc` request (wallet side) and match it against stored mdocs.\n   *\n   * `origin` must be the origin provided by the platform; Annex C C.5 requires the mdoc to abort\n   * when no origin was received.\n   */\n  public async resolveRequest(\n    agentContext: AgentContext,\n    options: MdocDcApiResolveRequestOptions\n  ): Promise<MdocDcApiResolvedRequest> {\n    const mdocContext = getMdocContext(agentContext)\n\n    // Reader trust is not resolved here, but per doc request below. `parseRequest` takes a single\n    // list of trust anchors for the whole request, while every doc request carries its own reader\n    // certificate chain and the trust callbacks are scoped to the chain they are asked about.\n    // `parseRequest` therefore only verifies the reader auth signatures.\n    const parsedRequest = await IsoMdocDcApi.parseRequest(\n      {\n        request: options.request,\n        origin: options.origin,\n        disableReaderCertificateChainValidation: true,\n        now: options.now,\n      },\n      mdocContext\n    )\n\n    const candidates = await this.getCandidates(\n      agentContext,\n      parsedRequest.docRequests.map((docRequest) => docRequest.docType),\n      options.useMode ?? CredentialMultiInstanceUseMode.NewOrFirst\n    )\n\n    // The same rules the verifier matches the response with, so a credential that matches in full\n    // results in a response that satisfies the doc request.\n    const deviceNamespaces = options.deviceNameSpaces\n      ? nameSpacesRecordToDeviceNamespaces(options.deviceNameSpaces)\n      : undefined\n    const holderMatch = Holder.matchDeviceRequest({\n      deviceRequest: parsedRequest.deviceRequest,\n      credentials: candidates.map(({ mdoc }) => ({ issuerSigned: mdoc.issuerSigned, deviceNamespaces })),\n      treatAmbiguousMultipleDocRequestsAsAlternatives: options.treatAmbiguousMultipleDocRequestsAsAlternatives,\n    })\n\n    const toValidCredential = ({\n      credentialIndex,\n      ...credentialMatch\n    }: CredentialMatchSuccess): MdocDcApiValidCredential => ({\n      ...credentialMatch,\n      record: candidates[credentialIndex].record,\n    })\n    const toFailedCredential = ({\n      credentialIndex,\n      ...credentialMatch\n    }: CredentialMatchFailure): MdocDcApiFailedCredential => ({\n      ...credentialMatch,\n      record: candidates[credentialIndex].record,\n    })\n\n    const docRequests: MdocDcApiResolvedDocRequest[] = []\n    for (const [docRequestIndex, docRequest] of parsedRequest.docRequests.entries()) {\n      const nameSpaces = Object.fromEntries(\n        Array.from(docRequest.namespaces.entries()).map(([nameSpace, elements]) => [\n          nameSpace,\n          Object.fromEntries(elements.entries()),\n        ])\n      )\n\n      const readerAuth = docRequest.hasReaderAuth\n        ? {\n            certificateChain: (docRequest.readerCertificateChain ?? []).map((certificate) =>\n              X509Certificate.fromRawCertificate(certificate)\n            ),\n          }\n        : undefined\n\n      if (readerAuth) {\n        await this.assertTrustedReaderCertificateChain(agentContext, {\n          certificateChain: readerAuth.certificateChain,\n          verification: {\n            type: 'mdocReaderAuth',\n            exchange: { type: 'dcApi', origin: parsedRequest.origin },\n            docType: docRequest.docType,\n            nameSpaces,\n          },\n          trustedReaderCertificates: options.trustedReaderCertificates,\n          now: options.now,\n        })\n      }\n\n      const docRequestMatch = holderMatch.docRequests[docRequestIndex]\n      const resolvedDocRequest = {\n        docRequestIndex,\n        docType: docRequest.docType,\n        nameSpaces,\n        readerAuth,\n        failedCredentials: docRequestMatch.failedCredentials.map(toFailedCredential),\n      }\n\n      docRequests.push(\n        docRequestMatch.success\n          ? {\n              ...resolvedDocRequest,\n              success: true,\n              validCredentials: mapNonEmptyArray(docRequestMatch.validCredentials, toValidCredential),\n            }\n          : { ...resolvedDocRequest, success: false, validCredentials: [] }\n      )\n    }\n\n    // `holderMatch.success` already applies the alternatives rule, so it is the single source of\n    // truth for whether the request can be answered. The doc requests are only narrowed to the\n    // successful ones when all of them succeeded, as with alternatives the failed ones are kept so\n    // the wallet can see which alternative it can answer.\n    if (holderMatch.docRequestsAsAlternatives) {\n      return {\n        origin: parsedRequest.origin,\n        success: holderMatch.success,\n        docRequestsAsAlternatives: true,\n        docRequests,\n        parsedRequest,\n      }\n    }\n\n    return holderMatch.success\n      ? {\n          origin: parsedRequest.origin,\n          success: true,\n          docRequestsAsAlternatives: false,\n          docRequests: docRequests.filter(\n            (docRequest): docRequest is MdocDcApiResolvedDocRequestSuccess => docRequest.success\n          ),\n          parsedRequest,\n        }\n      : {\n          origin: parsedRequest.origin,\n          success: false,\n          docRequestsAsAlternatives: false,\n          docRequests,\n          parsedRequest,\n        }\n  }\n\n  /**\n   * Fetch the stored mdocs for the requested doctypes.\n   *\n   * Only the requested doctypes are fetched, as a wallet can hold many more mdocs than a request\n   * covers. The issuer-signed CBOR of each record is decoded once, and matched against every doc\n   * request from there.\n   *\n   * Records that cannot provide an instance for `useMode` are left out, as creating a response with\n   * them would throw.\n   */\n  private async getCandidates(agentContext: AgentContext, docTypes: string[], useMode: CredentialMultiInstanceUseMode) {\n    const uniqueDocTypes = Array.from(new Set(docTypes))\n    if (uniqueDocTypes.length === 0) return []\n\n    const records = await this.mdocRepository.findByQuery(agentContext, {\n      $or: uniqueDocTypes.map((docType) => ({ docType })),\n    })\n\n    return records\n      .filter((record) => canUseInstanceFromCredentialRecord({ credentialRecord: record, useMode }))\n      .map((record) => ({ record, mdoc: record.firstCredential }))\n  }\n\n  /**\n   * Create the encrypted `org-iso-mdoc` response (wallet side).\n   */\n  public async createResponse(\n    agentContext: AgentContext,\n    options: MdocDcApiCreateResponseOptions\n  ): Promise<IsoMdocDcApiResponse> {\n    const mdocContext = getMdocContext(agentContext)\n    const parsedRequest = options.resolvedRequest.parsedRequest as IsoMdocDcApiParsedRequest\n\n    const documents = await Promise.all(\n      options.credentials.map(async ({ docRequestIndex, record, useMode, elements, deviceNameSpaces }) => {\n        const mdocRecord =\n          record instanceof MdocRecord ? record : await this.mdocRepository.getById(agentContext, record)\n\n        const { credentialInstance: mdoc } = await useInstanceFromCredentialRecord({\n          agentContext,\n          credentialRecord: mdocRecord,\n          useMode: useMode ?? CredentialMultiInstanceUseMode.NewOrFirst,\n        })\n\n        const deviceKeyJwk = mdocSigningJwk(mdoc.deviceKey)\n        if (!deviceKeyJwk.kid) {\n          throw new MdocError('Unable to create device response. The mdoc device key has no key id.')\n        }\n\n        return {\n          docRequestIndex,\n          issuerSigned: mdoc.issuerSigned,\n          deviceKey: CoseKey.fromJwk(deviceKeyJwk),\n          elements,\n          deviceNamespaces: deviceNameSpaces ? nameSpacesRecordToDeviceNamespaces(deviceNameSpaces) : undefined,\n        }\n      })\n    )\n\n    return await IsoMdocDcApi.createResponse({ parsedRequest, documents }, mdocContext)\n  }\n\n  private async updateState(\n    agentContext: AgentContext,\n    verificationSession: MdocVerificationSessionRecord,\n    newState: MdocVerificationSessionState\n  ) {\n    const previousState = verificationSession.state\n    verificationSession.state = newState\n\n    await this.mdocVerificationSessionRepository.update(agentContext, verificationSession)\n    this.emitStateChangedEvent(agentContext, verificationSession, previousState)\n  }\n\n  private emitStateChangedEvent(\n    agentContext: AgentContext,\n    verificationSession: MdocVerificationSessionRecord,\n    previousState: MdocVerificationSessionState | null\n  ) {\n    this.eventEmitter.emit<MdocVerificationSessionStateChangedEvent>(agentContext, {\n      type: MdocEventTypes.MdocVerificationSessionStateChanged,\n      payload: {\n        verificationSession: verificationSession.clone(),\n        previousState,\n      },\n    })\n  }\n}\n"],"mappings":"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"}