{"version":3,"file":"W3cV2SdJwtCredentialService.mjs","names":[],"sources":["../../../../src/modules/vc/sd-jwt-vc/W3cV2SdJwtCredentialService.ts"],"sourcesContent":["import { SDJwtInstance } from '@sd-jwt/core'\nimport type { DisclosureFrame, PresentationFrame, SDJWTConfig } from '@sd-jwt/types'\nimport type { AgentContext } from '../../../agent/context'\nimport { JwtPayload } from '../../../crypto'\nimport { CredoError } from '../../../error'\nimport { injectable } from '../../../plugins'\nimport { asArray, JsonTransformer, MessageValidator, nowInSeconds, TypedArrayEncoder } from '../../../utils'\nimport { getPublicJwkFromVerificationMethod } from '../../dids/domain/key-type/keyDidMapping'\nimport { KeyManagementApi } from '../../kms'\nimport {\n  extractKeyFromHolderBinding,\n  getSdJwtSigner,\n  getSdJwtVerifier,\n  parseHolderBindingFromCredential,\n} from '../../sd-jwt-vc/utils'\nimport type {\n  SingleValidationResult,\n  W3cV2JsonCredential,\n  W3cV2JsonPresentation,\n  W3cV2VerifyCredentialResult,\n  W3cV2VerifyPresentationResult,\n} from '../models'\nimport {\n  extractHolderFromPresentationCredentials,\n  getVerificationMethodForJwt,\n  validateAndResolveVerificationMethod,\n} from '../v2-jwt-utils'\nimport type {\n  W3cV2SdJwtSignCredentialOptions,\n  W3cV2SdJwtSignPresentationOptions,\n  W3cV2SdJwtVcPresentOptions,\n  W3cV2SdJwtVerifyCredentialOptions,\n  W3cV2SdJwtVerifyPresentationOptions,\n} from '../W3cV2CredentialServiceOptions'\nimport { sdJwtVcHasher } from './W3cV2SdJwt'\nimport { W3cV2SdJwtVerifiableCredential } from './W3cV2SdJwtVerifiableCredential'\nimport { W3cV2SdJwtVerifiablePresentation } from './W3cV2SdJwtVerifiablePresentation'\n\n/**\n * List of fields that cannot be selectively disclosed.\n *\n * @see https://www.w3.org/TR/vc-jose-cose/#securing-with-sd-jwt\n * @see https://www.w3.org/TR/vc-jose-cose/#securing-vps-sd-jwt\n */\nconst NON_DISCLOSEABLE_FIELDS = ['@context', 'type', 'credentialStatus', 'credentialSchema', 'relatedResource']\n\n/**\n * Supports signing and verifying W3C Verifiable Credentials and Presentations\n * secured with Selective Disclosure JSON Web Tokens (SD-JWT).\n *\n * @see https://www.w3.org/TR/vc-data-model/\n * @see https://www.w3.org/TR/vc-jose-cose/#with-sd-jwt\n */\n@injectable()\nexport class W3cV2SdJwtCredentialService {\n  /**\n   * Signs a credential\n   */\n  public async signCredential(\n    agentContext: AgentContext,\n    options: W3cV2SdJwtSignCredentialOptions\n  ): Promise<W3cV2SdJwtVerifiableCredential> {\n    // Validate the instance\n    MessageValidator.validateSync(options.credential)\n\n    // The JWT payload is simply the credential\n    const payload = JsonTransformer.toJSON(options.credential) as W3cV2JsonCredential\n\n    // Add iat and cnf to the payload\n    payload.iat = nowInSeconds()\n    payload.cnf = options.holder ? (await extractKeyFromHolderBinding(agentContext, options.holder)).cnf : undefined\n\n    // Validate and resolve the verification method\n    const publicJwk = await validateAndResolveVerificationMethod(agentContext, options.verificationMethod, [\n      'assertionMethod',\n    ])\n\n    // Validate the disclosure frame\n    const disclosureFrame = options.disclosureFrame as DisclosureFrame<W3cV2JsonCredential> | undefined\n    this.validateDisclosureFrame(disclosureFrame)\n\n    const sdJwt = new SDJwtInstance({\n      ...this.getBaseSdJwtConfig(agentContext),\n      signer: getSdJwtSigner(agentContext, publicJwk),\n      hashAlg: options.hashingAlgorithm ?? 'sha-256',\n      signAlg: options.alg,\n    })\n\n    // Sign SD-JWT\n    const compact = await sdJwt.issue<W3cV2JsonCredential>(payload, disclosureFrame, {\n      header: {\n        typ: 'vc+sd-jwt',\n        alg: options.alg,\n        kid: options.verificationMethod,\n      },\n    })\n\n    return W3cV2SdJwtVerifiableCredential.fromCompact(compact)\n  }\n\n  /**\n   * Verifies the signature(s) of a credential\n   *\n   * @param credential the credential to be verified\n   * @returns the verification result\n   */\n  public async verifyCredential(\n    agentContext: AgentContext,\n    options: W3cV2SdJwtVerifyCredentialOptions\n  ): Promise<W3cV2VerifyCredentialResult> {\n    const validationResults: W3cV2VerifyCredentialResult = {\n      isValid: false,\n      validations: {},\n    }\n\n    const sdJwt = new SDJwtInstance({\n      ...this.getBaseSdJwtConfig(agentContext),\n    })\n\n    try {\n      let credential: W3cV2SdJwtVerifiableCredential\n      try {\n        // If instance is provided as input, we want to validate the credential\n        // Otherwise, it is done by fromCompact below\n        if (options.credential instanceof W3cV2SdJwtVerifiableCredential) {\n          options.credential.validate()\n        }\n\n        credential =\n          options.credential instanceof W3cV2SdJwtVerifiableCredential\n            ? options.credential\n            : W3cV2SdJwtVerifiableCredential.fromCompact(options.credential)\n\n        // Validate JWT payload\n        JwtPayload.fromJson(credential.sdJwt.payload).validate({\n          skewSeconds: agentContext.config.validitySkewSeconds,\n        })\n\n        validationResults.validations.dataModel = {\n          isValid: true,\n        }\n      } catch (error) {\n        validationResults.validations.dataModel = {\n          isValid: false,\n          error,\n        }\n\n        return validationResults\n      }\n\n      const issuerVerificationMethod = await getVerificationMethodForJwt(agentContext, credential, ['assertionMethod'])\n      const issuerPublicKey = getPublicJwkFromVerificationMethod(issuerVerificationMethod)\n\n      const holderBinding = parseHolderBindingFromCredential(credential.sdJwt.prettyClaims)\n      const holder = holderBinding ? await extractKeyFromHolderBinding(agentContext, holderBinding) : undefined\n\n      sdJwt.config({\n        verifier: getSdJwtVerifier(agentContext, issuerPublicKey),\n        kbVerifier: holder ? getSdJwtVerifier(agentContext, holder.publicJwk) : undefined,\n      })\n\n      try {\n        await sdJwt.verify(credential.encoded, {\n          skewSeconds: agentContext.config.validitySkewSeconds,\n        })\n\n        validationResults.validations.signature = {\n          isValid: true,\n        }\n      } catch (error) {\n        validationResults.validations.signature = {\n          isValid: false,\n          error,\n        }\n      }\n\n      // Validate whether the credential is signed with the 'issuer' id\n      // NOTE: this uses the verificationMethod.controller. We may want to use the verificationMethod.id?\n      if (credential.resolvedCredential.issuerId !== issuerVerificationMethod.controller) {\n        validationResults.validations.issuerIsSigner = {\n          isValid: false,\n          error: new CredoError(\n            `Credential is signed using verification method ${issuerVerificationMethod.id}, while the issuer of the credential is '${credential.resolvedCredential.issuerId}'`\n          ),\n        }\n      } else {\n        validationResults.validations.issuerIsSigner = {\n          isValid: true,\n        }\n      }\n\n      validationResults.isValid = Object.values(validationResults.validations).every((v) => v.isValid)\n      return validationResults\n    } catch (error) {\n      validationResults.error = error\n      return validationResults\n    }\n  }\n\n  /**\n   * Signs a presentation including the credentials it includes\n   *\n   * @param presentation the presentation to be signed\n   * @returns the signed presentation\n   */\n  public async signPresentation(\n    agentContext: AgentContext,\n    options: W3cV2SdJwtSignPresentationOptions\n  ): Promise<W3cV2SdJwtVerifiablePresentation> {\n    // Validate the instance\n    MessageValidator.validateSync(options.presentation)\n\n    // The JWT payload is simply the presentation\n    const payload = JsonTransformer.toJSON(options.presentation) as W3cV2JsonPresentation\n\n    // Add the nonce and aud to the payload\n    payload.nonce = options.challenge\n    payload.aud = options.domain\n\n    const holder = await extractHolderFromPresentationCredentials(agentContext, options.presentation)\n\n    const sdJwt = new SDJwtInstance({\n      ...this.getBaseSdJwtConfig(agentContext),\n      signer: getSdJwtSigner(agentContext, holder.publicJwk),\n      hashAlg: options.hashingAlgorithm ?? 'sha-256',\n      signAlg: holder.alg,\n    })\n\n    // Validate the disclosure frame\n    const disclosureFrame = options.disclosureFrame as DisclosureFrame<W3cV2JsonPresentation> | undefined\n    this.validateDisclosureFrame(disclosureFrame)\n\n    // Sign SD-JWT\n    const compact = await sdJwt.issue<W3cV2JsonPresentation>(payload, disclosureFrame, {\n      header: {\n        typ: 'vp+sd-jwt',\n        alg: holder.alg,\n        kid: holder?.cnf?.kid,\n      },\n    })\n\n    return W3cV2SdJwtVerifiablePresentation.fromCompact(compact)\n  }\n\n  /**\n   * Verifies a presentation including the credentials it includes\n   *\n   * @param presentation the presentation to be verified\n   * @returns the verification result\n   */\n  public async verifyPresentation(\n    agentContext: AgentContext,\n    options: W3cV2SdJwtVerifyPresentationOptions\n  ): Promise<W3cV2VerifyPresentationResult> {\n    const validationResults: W3cV2VerifyPresentationResult = {\n      isValid: false,\n      validations: {},\n    }\n\n    const sdjwt = new SDJwtInstance({\n      ...this.getBaseSdJwtConfig(agentContext),\n    })\n\n    try {\n      let presentation: W3cV2SdJwtVerifiablePresentation\n      try {\n        // If instance is provided as input, we want to validate the presentation\n        if (options.presentation instanceof W3cV2SdJwtVerifiablePresentation) {\n          MessageValidator.validateSync(options.presentation.resolvedPresentation)\n        }\n\n        presentation =\n          options.presentation instanceof W3cV2SdJwtVerifiablePresentation\n            ? options.presentation\n            : W3cV2SdJwtVerifiablePresentation.fromCompact(options.presentation)\n\n        // Validate JWT payload\n        JwtPayload.fromJson(presentation.sdJwt.payload).validate({\n          skewSeconds: agentContext.config.validitySkewSeconds,\n        })\n\n        validationResults.validations.dataModel = {\n          isValid: true,\n        }\n      } catch (error) {\n        validationResults.validations.dataModel = {\n          isValid: false,\n          error,\n        }\n\n        return validationResults\n      }\n\n      const proverVerificationMethod = await getVerificationMethodForJwt(agentContext, presentation, ['authentication'])\n      const proverPublicKey = getPublicJwkFromVerificationMethod(proverVerificationMethod)\n      const holderBinding = parseHolderBindingFromCredential(presentation.sdJwt.prettyClaims)\n      const holder = holderBinding ? await extractKeyFromHolderBinding(agentContext, holderBinding) : undefined\n\n      sdjwt.config({\n        verifier: getSdJwtVerifier(agentContext, proverPublicKey),\n        kbVerifier: holder ? getSdJwtVerifier(agentContext, holder.publicJwk) : undefined,\n      })\n\n      try {\n        await sdjwt.verify(presentation.encoded, {\n          skewSeconds: agentContext.config.validitySkewSeconds,\n        })\n\n        validationResults.validations.presentationSignature = {\n          isValid: true,\n        }\n      } catch (error) {\n        validationResults.validations.presentationSignature = {\n          isValid: false,\n          error,\n        }\n      }\n\n      // Validate whether the presentation is signed with the 'holder' id\n      // NOTE: this uses the verificationMethod.controller. We may want to use the verificationMethod.id?\n      if (\n        presentation.resolvedPresentation.holderId &&\n        proverVerificationMethod.controller !== presentation.resolvedPresentation.holderId\n      ) {\n        validationResults.validations.holderIsSigner = {\n          isValid: false,\n          error: new CredoError(\n            `Presentation is signed using verification method ${proverVerificationMethod.id}, while the holder of the presentation is '${presentation.resolvedPresentation.holderId}'`\n          ),\n        }\n      } else {\n        // If no holderId is present, this validation passes by default as there can't be\n        // a mismatch between the 'holder' property and the signer of the presentation.\n        validationResults.validations.holderIsSigner = {\n          isValid: true,\n        }\n      }\n\n      // To keep things simple, we only support JWT VCs in JWT VPs for now\n      const credentials = asArray(presentation.resolvedPresentation.verifiableCredential)\n\n      // Verify all credentials in parallel, and await the result\n      validationResults.validations.credentials = await Promise.all(\n        credentials.map(async (credential) => {\n          if (!(credential.envelopedCredential instanceof W3cV2SdJwtVerifiableCredential)) {\n            return {\n              isValid: false,\n              error: new CredoError(\n                'Credential is not of format SD-JWT. Presentations in SD-JWT format can only contain credentials in SD-JWT format.'\n              ),\n              validations: {},\n            }\n          }\n\n          const credentialResult = await this.verifyCredential(agentContext, {\n            credential: credential.envelopedCredential,\n          })\n\n          let credentialSubjectAuthentication: SingleValidationResult\n\n          // Check whether any of the credentialSubjectIds for each credential is the same as the controller of the verificationMethod\n          // This authenticates the presentation creator controls one of the credentialSubject ids.\n          // NOTE: this doesn't take into account the case where the credentialSubject is no the holder. In the\n          // future we can add support for other flows, but for now this is the most common use case.\n          // TODO: should this be handled on a higher level? I don't really see it being handled in the jsonld lib\n          // or in the did-jwt-vc lib (it seems they don't even verify the credentials itself), but we probably need some\n          // more experience on the use cases before we loosen the restrictions (as it means we need to handle it on a higher layer).\n          const credentialSubjectIds = credential.resolvedCredential.credentialSubjectIds\n          const presentationAuthenticatesCredentialSubject = credentialSubjectIds.some(\n            (subjectId) => proverVerificationMethod.controller === subjectId\n          )\n\n          if (credentialSubjectIds.length > 0 && !presentationAuthenticatesCredentialSubject) {\n            credentialSubjectAuthentication = {\n              isValid: false,\n              error: new CredoError(\n                'Credential has one or more credentialSubject ids, but presentation does not authenticate credential subject'\n              ),\n            }\n          } else {\n            credentialSubjectAuthentication = {\n              isValid: true,\n            }\n          }\n\n          return {\n            ...credentialResult,\n            isValid: credentialResult.isValid && credentialSubjectAuthentication.isValid,\n            validations: {\n              ...credentialResult.validations,\n              credentialSubjectAuthentication,\n            },\n          }\n        })\n      )\n\n      // Deeply nested check whether all validations have passed\n      validationResults.isValid = Object.values(validationResults.validations).every((v) =>\n        Array.isArray(v) ? v.every((vv) => vv.isValid) : v.isValid\n      )\n\n      return validationResults\n    } catch (error) {\n      validationResults.error = error\n      return validationResults\n    }\n  }\n\n  public async present(\n    agentContext: AgentContext,\n    options: W3cV2SdJwtVcPresentOptions\n  ): Promise<W3cV2SdJwtVerifiableCredential> {\n    const originalCompact =\n      options.credential instanceof W3cV2SdJwtVerifiableCredential ? options.credential.encoded : options.credential\n\n    const presentationFrame = options.presentationFrame as PresentationFrame<W3cV2JsonCredential>\n\n    const sdjwt = new SDJwtInstance(this.getBaseSdJwtConfig(agentContext))\n    const disclosedCompact = await sdjwt.present(originalCompact, presentationFrame)\n\n    return W3cV2SdJwtVerifiableCredential.fromCompact(disclosedCompact)\n  }\n\n  private validateDisclosureFrame(disclosureFrame?: DisclosureFrame<W3cV2JsonCredential | W3cV2JsonPresentation>) {\n    if (!disclosureFrame) return\n\n    for (const field of NON_DISCLOSEABLE_FIELDS) {\n      if (disclosureFrame[field]) {\n        throw new CredoError(`'${field}' property cannot be selectively disclosed`)\n      }\n\n      if (Array.isArray(disclosureFrame._sd) && disclosureFrame._sd?.includes(field)) {\n        throw new CredoError(`'${field}' property cannot be selectively disclosed`)\n      }\n    }\n  }\n\n  private getBaseSdJwtConfig(agentContext: AgentContext): SDJWTConfig {\n    const kms = agentContext.resolve(KeyManagementApi)\n\n    return {\n      hasher: sdJwtVcHasher,\n      saltGenerator: (length) => TypedArrayEncoder.toBase64Url(kms.randomBytes({ length })).slice(0, length),\n    }\n  }\n}\n"],"mappings":"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"}