UNPKG

@sphereon/ssi-types

Version:

SSI Common Types

1 lines • 125 kB
{"version":3,"sources":["../src/index.ts","../src/logging/index.ts","../src/types/datastore.ts","../src/events/index.ts","../src/utils/object.ts","../src/utils/hasher.ts","../src/utils/vc.ts","../src/utils/did.ts","../src/utils/mdoc.ts","../src/utils/sd-jwt.ts","../src/utils/jose.ts","../src/utils/cose.ts","../src/mapper/credential-constraints.ts","../src/mapper/credential-mapper.ts","../src/mapper/jsonld-language-values.ts"],"sourcesContent":["import { Loggers } from './logging'\n// We call the logger first, making sure that any library (re-)using the logger has the static initialized\nconst logger = Loggers.DEFAULT.get('sphereon:ssi')\nlogger.debug(`Sphereon logger initialized`)\n\nexport * from './types'\nexport * from './logging'\nexport * from './events'\nexport * from './utils'\nexport * from './mapper'\n","import createDebug from 'debug'\n\nimport { EventEmitter } from 'events'\n\nexport enum LogLevel {\n TRACE = 0,\n DEBUG,\n INFO,\n WARNING,\n ERROR,\n}\n\nexport enum LoggingEventType {\n AUDIT = 'audit',\n ACTIVITY = 'activity',\n GENERAL = 'general',\n}\n\nexport interface SimpleLogEvent {\n type: LoggingEventType.GENERAL\n level: LogLevel\n correlationId?: string\n timestamp: Date\n data: string\n diagnosticData?: any\n}\n\nexport enum LogMethod {\n DEBUG_PKG,\n CONSOLE,\n EVENT,\n}\n\nexport interface SimpleLogOptions {\n namespace?: string\n eventName?: string\n defaultLogLevel?: LogLevel\n methods?: LogMethod[]\n}\n\nexport function logOptions(opts?: SimpleLogOptions): Required<SimpleLogOptions> {\n return {\n namespace: opts?.namespace ?? 'sphereon',\n eventName: opts?.eventName ?? 'sphereon:default',\n defaultLogLevel: opts?.defaultLogLevel ?? LogLevel.INFO,\n methods: opts?.methods ?? [LogMethod.DEBUG_PKG, LogMethod.EVENT],\n }\n}\n\nexport class Loggers {\n private static readonly DEFAULT_KEY = '__DEFAULT__'\n public static readonly DEFAULT: Loggers = new Loggers({\n defaultLogLevel: LogLevel.INFO,\n methods: [LogMethod.DEBUG_PKG, LogMethod.EVENT],\n })\n private readonly namespaceOptions: Map<string, Required<SimpleLogOptions>> = new Map()\n private readonly loggers: WeakMap<Required<SimpleLogOptions>, ISimpleLogger<any>> = new WeakMap()\n\n constructor(defaultOptions?: Omit<SimpleLogOptions, 'namespace'>) {\n this.defaultOptions(logOptions(defaultOptions))\n }\n\n public options(namespace: string, options: Omit<SimpleLogOptions, 'namespace'>): this {\n this.namespaceOptions.set(namespace, logOptions({ ...options, namespace }))\n return this\n }\n\n public defaultOptions(options: Omit<SimpleLogOptions, 'namespace'>): this {\n this.options(Loggers.DEFAULT_KEY, options)\n return this\n }\n\n register<T>(namespace: string, logger: ISimpleLogger<T>): ISimpleLogger<T> {\n return this.get(namespace, logger)\n }\n\n get<T>(namespace: string, registerLogger?: ISimpleLogger<T>): ISimpleLogger<T> {\n const options = this.namespaceOptions.get(namespace) ?? registerLogger?.options ?? this.namespaceOptions.get(Loggers.DEFAULT_KEY)\n if (!options) {\n throw Error(`No logging options found for namespace ${namespace}`)\n }\n this.namespaceOptions.set(namespace, options)\n\n let logger = this.loggers.get(options)\n if (!logger) {\n logger = registerLogger ?? new SimpleLogger(options)\n this.loggers.set(options, logger)\n }\n return logger\n }\n}\n\nexport type ISimpleLogger<LogType> = {\n options: Required<SimpleLogOptions>\n log(value: LogType, ...args: any[]): void\n info(value: LogType, ...args: any[]): void\n debug(value: LogType, ...args: any[]): void\n trace(value: LogType, ...args: any[]): void\n warning(value: LogType, ...args: any[]): void\n error(value: LogType, ...args: any[]): void\n logl(level: LogLevel, value: LogType, ...argsW: any[]): void\n}\n\nexport class SimpleLogger implements ISimpleLogger<any> {\n private _eventEmitter = new EventEmitter({ captureRejections: true })\n private readonly _options: Required<SimpleLogOptions>\n\n constructor(opts?: SimpleLogOptions) {\n this._options = logOptions(opts)\n }\n\n get eventEmitter(): EventEmitter {\n return this._eventEmitter\n }\n\n get options(): Required<SimpleLogOptions> {\n return this._options\n }\n\n trace(value: any, ...args: any[]) {\n this.logImpl(LogLevel.TRACE, value, ...args)\n }\n\n debug(value: any, ...args: any[]) {\n this.logImpl(LogLevel.DEBUG, value, ...args)\n }\n\n info(value: any, ...args: any[]) {\n this.logImpl(LogLevel.INFO, value, ...args)\n }\n\n warning(value: any, ...args: any[]) {\n this.logImpl(LogLevel.WARNING, value, ...args)\n }\n\n error(value: any, ...args: any[]) {\n this.logImpl(LogLevel.ERROR, value, ...args)\n }\n\n logl(level: LogLevel, value: any, ...args: any[]) {\n this.logImpl(level, value, ...args)\n }\n\n private logImpl(level: LogLevel, value: any, ...args: any[]) {\n const date = new Date().toISOString()\n const filteredArgs = args?.filter((v) => v!!) ?? []\n const arg = filteredArgs.length === 0 || filteredArgs[0] == undefined ? undefined : filteredArgs\n\n function toLogValue(options: SimpleLogOptions): any {\n if (typeof value === 'string') {\n return `${date}-(${options.namespace}) ${value}`\n } else if (typeof value === 'object') {\n value['namespace'] = options.namespace\n value['time'] = date\n }\n return value\n }\n\n const logValue = toLogValue(this.options)\n const logArgs = [logValue]\n if (arg) {\n logArgs.push(args)\n }\n // FIXME: !!!!!!!!!!!!!!!!!!!!!!\n let debugPkgEnabled = false && this.options.methods.includes(LogMethod.DEBUG_PKG)\n if (debugPkgEnabled) {\n const debugPkgDebugger = createDebug.default(this._options.namespace)\n // It was enabled at the options level in code, but could be disabled at runtime using env vars\n debugPkgEnabled = debugPkgDebugger.enabled\n if (debugPkgEnabled) {\n if (arg) {\n debugPkgDebugger(`${date}- ${value},`, ...arg)\n } else {\n debugPkgDebugger(`${date}- ${value}`)\n }\n }\n }\n\n // We do not perform console.logs in case the debug package is enabled in code and used at runtime\n if (this.options.methods.includes(LogMethod.CONSOLE) && !debugPkgEnabled) {\n const [value, args] = logArgs\n let logMethod = console.info\n switch (level) {\n case LogLevel.TRACE:\n logMethod = console.trace\n break\n case LogLevel.DEBUG:\n logMethod = console.debug\n break\n case LogLevel.INFO:\n logMethod = console.info\n break\n case LogLevel.WARNING:\n logMethod = console.warn\n break\n case LogLevel.ERROR:\n logMethod = console.error\n break\n }\n if (args) {\n logMethod(value + ',', ...args)\n } else {\n logMethod(value)\n }\n }\n\n if (this.options.methods.includes(LogMethod.EVENT)) {\n this._eventEmitter.emit(this.options.eventName, {\n data: value.toString(),\n timestamp: new Date(date),\n level,\n type: LoggingEventType.GENERAL,\n diagnosticData: logArgs,\n } satisfies SimpleLogEvent)\n }\n }\n\n log(value: any, ...args: any[]) {\n this.logImpl(this.options.defaultLogLevel, value, ...args)\n }\n}\n\nexport class SimpleRecordLogger extends SimpleLogger implements ISimpleLogger<Record<string, any>> {\n constructor(opts?: SimpleLogOptions) {\n super(opts)\n }\n}\n","export enum CredentialRole {\n ISSUER = 'ISSUER',\n VERIFIER = 'VERIFIER',\n HOLDER = 'HOLDER',\n FEDERATION_TRUST_ANCHOR = 'FEDERATION_TRUST_ANCHOR',\n}\n","import { EventEmitter } from 'events'\nimport { Loggers, LogLevel, LogMethod } from '../logging'\n\nexport enum System {\n GENERAL = 'general',\n KMS = 'kms',\n IDENTITY = 'identity',\n OID4VCI = 'oid4vci',\n OID4VP = 'oid4vp',\n SIOPv2 = 'siopv2',\n PE = 'PE',\n CREDENTIALS = 'credentials',\n WEB3 = 'web3',\n PROFILE = 'profile',\n CONTACT = 'contact',\n}\n\nexport enum SubSystem {\n KEY = 'key',\n DID_PROVIDER = 'did_provider',\n DID_RESOLVER = 'did_resolver',\n OID4VP_OP = 'oid4vp_op',\n OID4VCI_CLIENT = 'oid4vci_client',\n SIOPv2_OP = 'siopv2_op',\n CONTACT_MANAGER = 'contact_manager',\n VC_ISSUER = 'vc_issuer',\n VC_VERIFIER = 'vc_verifier',\n VC_PERSISTENCE = 'vc_persistence',\n TRANSPORT = 'transport',\n PROFILE = 'profile',\n API = 'api',\n}\n\nexport enum ActionType {\n CREATE = 'create',\n READ = 'read',\n UPDATE = 'update',\n DELETE = 'delete',\n EXECUTE = 'execute',\n}\n\nexport enum DefaultActionSubType {\n KEY_GENERATION = 'Key generation',\n KEY_IMPORT = 'Key import',\n KEY_PERSISTENCE = 'Key persistence',\n KEY_REMOVAL = 'Key removal',\n DID_CREATION = 'DID creation',\n DID_RESOLUTION = 'DID resolution',\n DID_SERVICE_UPDATE = 'DID service update',\n VC_ISSUE = 'VC issue',\n VC_VERIFY = 'VC verify',\n VC_SHARE = 'VC share',\n VC_DELETE = 'VC delete',\n VC_ISSUE_DECLINE = 'VC issue decline',\n VC_SHARE_DECLINE = 'VC share decline',\n}\n\nexport type ActionSubType = DefaultActionSubType | string\n\nexport enum InitiatorType {\n USER = 'user',\n SYSTEM = 'system',\n EXTERNAL = 'external',\n}\n\nexport enum SystemCorrelationIdType {\n DID = 'did',\n URL = 'url',\n EMAIL = 'email',\n HOSTNAME = 'hostname',\n PHONE = 'phone',\n USER = 'user',\n}\n\nexport type EventData = {\n system: string\n subSystemType: string\n}\n\nexport interface BasicEvent<SourceType, PayloadType extends EventData> {\n id: string\n correlationId?: string\n eventName: string\n initiator?: string\n initiatorType: InitiatorType\n system: System\n subsystem: SubSystem\n data: PayloadType\n}\n\ntype EmitterInstance = {\n enabled: boolean\n emitter: EventEmitter\n}\n\nexport class EventManager {\n private static readonly INSTANCE = new EventManager()\n private _emitters = new Map<string, EmitterInstance>()\n\n private constructor() {}\n\n public static instance(): EventManager {\n return EventManager.INSTANCE\n }\n\n register(name: string, emitter: EventEmitter, opts?: { disabled: boolean }): EventEmitter {\n this._emitters.set(name, { emitter, enabled: opts?.disabled !== true })\n return emitter\n }\n\n get(name: string, opts?: { onlyEnabled?: boolean }): EventEmitter {\n const { emitter, enabled } = this._emitters.get(name) ?? {}\n if (!emitter) {\n throw Error(`No emitter registered with name ${name}`)\n } else if (opts?.onlyEnabled && !enabled) {\n throw Error(`Emitter with name ${name} is not enabled`)\n }\n return emitter\n }\n\n getOrCreate(name: string, opts?: { onlyEnabled?: boolean }): EventEmitter {\n if (this.has(name)) {\n return this.get(name, opts)\n }\n return this.register(name, new BasicEventEmitter())\n }\n\n has(name: string): boolean {\n return this._emitters.has(name)\n }\n\n emitters(filter?: { eventName?: string | symbol; onlyEnabled?: boolean }): Array<EventEmitter> {\n const all = Array.from(new Set(this._emitters.values()))\n return this.emittersImpl(all, filter).map((e) => e.emitter)\n }\n\n hasEventName(eventName: string | symbol) {\n return this.eventNames().includes(eventName)\n }\n\n eventNames(): Array<string | symbol> {\n return Array.from(new Set(this.emitters().flatMap((emitter) => emitter.eventNames())))\n }\n\n emitBasic(event: BasicEvent<any, any>, ...args: any[]) {\n return this.emit(event.eventName, event, args)\n }\n\n emit(eventName: string | symbol, event: Omit<BasicEvent<any, any>, 'eventName'> | any, ...args: any[]): void {\n if ('id' in event && 'system' in event && !event.eventName) {\n event.eventName = eventName\n }\n Loggers.DEFAULT.options('sphereon:events', {\n methods: [LogMethod.CONSOLE],\n defaultLogLevel: LogLevel.INFO,\n })\n .get('sphereon:events')\n .log(`Emitting '${eventName.toString()}' event`, event)\n const emitters = this.emitters({ eventName })\n emitters.flatMap((emitter) => emitter.emit(eventName, event, args))\n }\n\n listenerCount(eventName: string | symbol) {\n const emitters = this.emitters({ eventName })\n return emitters.map((emitter) => emitter.listenerCount(eventName)).reduce((previous, current) => current + previous)\n }\n\n listeners(filter: { emitterName?: string; eventName: string; onlyEnabled?: boolean }): Array<Function> {\n const emitters = filter?.emitterName ? [this.get(filter.emitterName, filter)] : this.emitters(filter)\n return Array.from(\n new Set(\n this.emittersImpl(\n emitters.map((emitter) => {\n return { emitter, enabled: true }\n }),\n filter,\n ).flatMap((emitter) => emitter.emitter.listeners(filter.eventName)),\n ),\n )\n }\n\n private emittersImpl(\n all: { emitter: EventEmitter; enabled: boolean }[],\n filter?: {\n eventName?: string | symbol\n onlyEnabled?: boolean\n },\n ): Array<EmitterInstance> {\n const { eventName } = filter ?? {}\n if (!eventName) {\n return all\n }\n const filtered = all.filter((emitter) => emitter.emitter.eventNames().includes(eventName) && (emitter.enabled || filter?.onlyEnabled !== true))\n return Array.from(new Set(filtered))\n }\n}\n\nexport class BasicEventEmitter extends EventEmitter {\n addListener(eventName: string | symbol, listener: (event: BasicEvent<any, any>, ...args: any[]) => void): this {\n return super.addListener(eventName, listener)\n }\n\n once(eventName: string | symbol, listener: (event: BasicEvent<any, any>, ...args: any[]) => void): this {\n return super.once(eventName, listener)\n }\n\n emit(eventName: string, event: BasicEvent<any, any>, ...args: any[]): boolean {\n return super.emit(eventName, ...args)\n }\n}\n","const BASE64_REGEX = /^[-A-Za-z0-9+_/]*={0,3}$/g\n\nexport class ObjectUtils {\n public static asArray<T>(value: T): T[] {\n return Array.isArray(value) ? value : [value]\n }\n\n public static isObject(value: unknown): value is object {\n return typeof value === 'object' || Object.prototype.toString.call(value) === '[object Object]'\n }\n\n public static isUrlAbsolute(url: string) {\n // regex to check for absolute IRI (starting scheme and ':') or blank node IRI\n const isAbsoluteRegex = /^([A-Za-z][A-Za-z0-9+-.]*|_):[^\\s]*$/\n ObjectUtils.isString(url) && isAbsoluteRegex.test(url)\n }\n\n public static isString(value: unknown): value is string {\n return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]'\n }\n\n public static isBase64(value: unknown): boolean {\n if (!ObjectUtils.isString(value)) {\n return false\n }\n return value.match(BASE64_REGEX) !== null\n }\n}\n","import { sha256, sha384, sha512 } from '@noble/hashes/sha2'\n\n// @ts-ignore\nimport * as u8a from 'uint8arrays'\nconst { fromString } = u8a\nimport type { HasherSync } from '../types'\n\nconst supportedAlgorithms = ['sha256', 'sha384', 'sha512'] as const\ntype SupportedAlgorithms = (typeof supportedAlgorithms)[number]\n\nexport const shaHasher: HasherSync = (data, algorithm) => {\n const sanitizedAlgorithm = algorithm.toLowerCase().replace(/[-_]/g, '')\n if (!supportedAlgorithms.includes(sanitizedAlgorithm as SupportedAlgorithms)) {\n throw new Error(`Unsupported hashing algorithm ${algorithm}`)\n }\n const hasher = sanitizedAlgorithm === 'sha384' ? sha384 : sanitizedAlgorithm === 'sha512' ? sha512 : sha256\n return hasher(typeof data === 'string' ? fromString(data) : new Uint8Array(data))\n}\n\nexport const defaultHasher: HasherSync = shaHasher\n","import type {\n ICredential,\n IVerifiableCredential,\n WrappedVerifiableCredential,\n WrappedVerifiablePresentation,\n WrappedW3CVerifiableCredential,\n WrappedW3CVerifiablePresentation,\n} from '../types'\nimport type { CredentialPayload, VerifiableCredential } from '@veramo/core'\n\nexport function isWrappedW3CVerifiableCredential(vc: WrappedVerifiableCredential): vc is WrappedW3CVerifiableCredential {\n return vc.format === 'jwt_vc' || vc.format === 'ldp_vc'\n}\n\nexport function isWrappedW3CVerifiablePresentation(vp: WrappedVerifiablePresentation): vp is WrappedW3CVerifiablePresentation {\n return vp.format === 'jwt_vp' || vp.format === 'ldp_vp'\n}\n\nexport enum StatusListType {\n StatusList2021 = 'StatusList2021',\n OAuthStatusList = 'OAuthStatusList',\n BitstringStatusList = 'BitstringStatusList',\n}\n\nfunction isVcdmCredential(\n credential: CredentialPayload | IVerifiableCredential | ICredential | VerifiableCredential | unknown,\n vcdmType: string,\n): boolean {\n if (!credential || typeof credential !== 'object') {\n return false\n } else if (!('@context' in credential && Array.isArray(credential['@context']))) {\n return false\n }\n return credential['@context'].includes(vcdmType)\n}\nexport function isVcdm1Credential(credential: CredentialPayload | IVerifiableCredential | ICredential | VerifiableCredential | unknown): boolean {\n return isVcdmCredential(credential, VCDM_CREDENTIAL_CONTEXT_V1)\n}\n\nexport function isVcdm2Credential(credential: CredentialPayload | IVerifiableCredential | ICredential | VerifiableCredential | unknown): boolean {\n return isVcdmCredential(credential, VCDM_CREDENTIAL_CONTEXT_V2)\n}\n\nexport function addVcdmContextIfNeeded(context?: string[], defaultValue: string = VCDM_CREDENTIAL_CONTEXT_V2): string[] {\n const newContext = [...(context ?? [])]\n const vcdmContext = context?.find((val) => VCDM_CREDENTIAL_CONTEXT_VERSIONS.includes(val))\n if (!vcdmContext) {\n newContext.unshift(defaultValue)\n }\n return newContext\n}\n\nexport const VCDM_CREDENTIAL_CONTEXT_V1 = 'https://www.w3.org/2018/credentials/v1'\nexport const VCDM_CREDENTIAL_CONTEXT_V2 = 'https://www.w3.org/ns/credentials/v2'\nexport const VCDM_CREDENTIAL_CONTEXT_VERSIONS = [VCDM_CREDENTIAL_CONTEXT_V2, VCDM_CREDENTIAL_CONTEXT_V1]\n","import { IParsedDID } from '../types'\n\nexport enum IProofPurpose {\n verificationMethod = 'verificationMethod',\n assertionMethod = 'assertionMethod',\n authentication = 'authentication',\n keyAgreement = 'keyAgreement',\n contractAgreement = 'contactAgreement',\n capabilityInvocation = 'capabilityInvocation',\n capabilityDelegation = 'capabilityDelegation',\n}\n\nexport enum IProofType {\n Ed25519Signature2018 = 'Ed25519Signature2018',\n Ed25519Signature2020 = 'Ed25519Signature2020',\n EcdsaSecp256k1Signature2019 = 'EcdsaSecp256k1Signature2019',\n EcdsaSecp256k1RecoverySignature2020 = 'EcdsaSecp256k1RecoverySignature2020',\n JsonWebSignature2020 = 'JsonWebSignature2020',\n RsaSignature2018 = 'RsaSignature2018',\n GpgSignature2020 = 'GpgSignature2020',\n JcsEd25519Signature2020 = 'JcsEd25519Signature2020',\n BbsBlsSignatureProof2020 = 'BbsBlsSignatureProof2020',\n BbsBlsBoundSignatureProof2020 = 'BbsBlsBoundSignatureProof2020',\n JwtProof2020 = 'JwtProof2020',\n SdJwtProof2024 = 'SdJwtProof2024',\n MdocProof2024 = 'MsoMdocProof2024',\n}\n\nexport const parseDid = (did: string): IParsedDID => {\n const parsedDid = parse(did)\n if (parsedDid === null) {\n throw new Error('invalid did')\n }\n\n return parsedDid\n}\n\nconst parse = (didUrl: string): IParsedDID | null => {\n const PCT_ENCODED = '(?:%[0-9a-fA-F]{2})'\n const ID_CHAR = `(?:[a-zA-Z0-9._-]|${PCT_ENCODED})`\n const METHOD = '([a-z0-9]+)'\n const METHOD_ID = `((?:${ID_CHAR}*:)*(${ID_CHAR}+))`\n const PARAM_CHAR = '[a-zA-Z0-9_.:%-]'\n const PARAM = `;${PARAM_CHAR}+=${PARAM_CHAR}*`\n const PARAMS = `((${PARAM})*)`\n const PATH = `(/[^#?]*)?`\n const QUERY = `([?][^#]*)?`\n const FRAGMENT = `(#.*)?`\n const DID_MATCHER = new RegExp(`^did:${METHOD}:${METHOD_ID}${PARAMS}${PATH}${QUERY}${FRAGMENT}$`)\n\n if (didUrl === '' || !didUrl) return null\n const sections = didUrl.match(DID_MATCHER)\n if (sections) {\n const parts: IParsedDID = {\n did: `did:${sections[1]}:${sections[2]}`,\n method: sections[1],\n id: sections[2],\n didUrl,\n }\n if (sections[4]) {\n const params = sections[4].slice(1).split(';')\n parts.params = {}\n for (const p of params) {\n const kv = p.split('=')\n parts.params[kv[0]] = kv[1]\n }\n }\n if (sections[6]) parts.path = sections[6]\n if (sections[7]) parts.query = sections[7].slice(1)\n if (sections[8]) parts.fragment = sections[8].slice(1)\n return parts\n }\n\n return null\n}\n\n// Copied from did-resolver, so we have types without external dep\n\n// Copyright 2018 Consensys AG\n\n// Licensed under the Apache License, Version 2.0(the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n\n// http://www.apache.org/licenses/LICENSE-2.0\n\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n","import type {\n DocumentJson,\n IssuerSignedItemJson,\n IVerifiableCredential,\n MdocDecodedPayload,\n MdocDeviceResponse,\n MdocDocument,\n MdocIssuerSigned,\n MdocOid4vpIssuerSigned,\n MdocOid4vpMdocVpToken,\n WrappedMdocCredential,\n WrappedMdocPresentation,\n WrappedVerifiableCredential,\n WrappedVerifiablePresentation,\n} from '../types'\nimport mdocPkg from '@sphereon/kmp-mdoc-core'\nconst { com } = mdocPkg\nimport { IProofPurpose, IProofType } from './did'\n\nexport function isWrappedMdocCredential(vc: WrappedVerifiableCredential): vc is WrappedMdocCredential {\n return vc.format === 'mso_mdoc'\n}\n\nexport function isWrappedMdocPresentation(vp: WrappedVerifiablePresentation): vp is WrappedMdocPresentation {\n return vp.format === 'mso_mdoc'\n}\n\nexport function getMdocDecodedPayload(mdoc: MdocDocument): MdocDecodedPayload {\n const mdocJson = mdoc.toJson()\n if (!mdocJson.issuerSigned.nameSpaces) {\n throw Error(`Cannot access Issuer Signed items from the Mdoc`)\n }\n\n const issuerSignedJson = mdoc.issuerSigned.toJsonDTO()\n const namespaces = issuerSignedJson.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>\n\n const decodedPayload: MdocDecodedPayload = {}\n for (const [namespace, items] of Object.entries(namespaces)) {\n decodedPayload[namespace] = items.reduce(\n (acc, item) => ({\n ...acc,\n [item.key]: item.value.value,\n }),\n {},\n )\n }\n\n return decodedPayload\n}\n\n/**\n * Decode an Mdoc from its issuerSigned OID4VP Base64URL (string) to an object containing the disclosures,\n * signed payload, decoded payload\n *\n */\nexport function decodeMdocIssuerSigned(oid4vpIssuerSigned: MdocOid4vpIssuerSigned): MdocDocument {\n // Issuer signed according to 18013-7 in base64url\n const issuerSigned: MdocIssuerSigned = com.sphereon.mdoc.data.device.IssuerSignedCbor.Static.cborDecode(\n com.sphereon.kmp.decodeFrom(oid4vpIssuerSigned, com.sphereon.kmp.Encoding.BASE64URL),\n )\n // Create an mdoc from it. // Validations need to be performed by the caller after this!\n const holderMdoc: MdocDocument = issuerSigned.toDocument()\n return holderMdoc\n}\n\nexport function encodeMdocIssuerSigned(issuerSigned: MdocIssuerSigned, encoding: 'base64url' = 'base64url') {\n return com.sphereon.kmp.encodeTo(issuerSigned.cborEncode(), com.sphereon.kmp.Encoding.BASE64URL)\n}\n\n/**\n * Decode an Mdoc from its vp_token OID4VP Base64URL (string) to an object containing the disclosures,\n * signed payload, decoded payload\n *\n */\nexport function decodeMdocDeviceResponse(vpToken: MdocOid4vpMdocVpToken): MdocDeviceResponse {\n const deviceResponse = com.sphereon.mdoc.data.device.DeviceResponseCbor.Static.cborDecode(\n com.sphereon.kmp.decodeFrom(vpToken, com.sphereon.kmp.Encoding.BASE64URL),\n )\n return deviceResponse\n}\n\n// TODO naive implementation of mapping a mdoc onto a IVerifiableCredential. Needs some fixes and further implementation and needs to be moved out of ssi-types\nexport const mdocDecodedCredentialToUniformCredential = (\n decoded: MdocDocument,\n // @ts-ignore\n opts?: { maxTimeSkewInMS?: number },\n): IVerifiableCredential => {\n const document = decoded.toJson()\n const json = document.toJsonDTO<DocumentJson>()\n const type = 'Personal Identification Data'\n const MSO = document.MSO\n if (!MSO || !json.issuerSigned?.nameSpaces) {\n throw Error(`Cannot access Mobile Security Object or Issuer Signed items from the Mdoc`)\n }\n const nameSpaces = json.issuerSigned.nameSpaces as unknown as Record<string, IssuerSignedItemJson[]>\n if (!('eu.europa.ec.eudi.pid.1' in nameSpaces)) {\n throw Error(`Only PID supported at present`)\n }\n const items = nameSpaces['eu.europa.ec.eudi.pid.1']\n if (!items || items.length === 0) {\n throw Error(`No issuer signed items were found`)\n }\n type DisclosuresAccumulator = {\n [key: string]: any\n }\n\n const credentialSubject = items.reduce((acc: DisclosuresAccumulator, item: IssuerSignedItemJson) => {\n if (Array.isArray(item.value)) {\n acc[item.key] = item.value.map((val) => val.value).join(', ')\n } else {\n acc[item.key] = item.value.value\n }\n return acc\n }, {})\n const validFrom = MSO.validityInfo.validFrom\n const validUntil = MSO.validityInfo.validUntil\n const docType = MSO.docType\n const expirationDate = validUntil\n let issuanceDateStr = validFrom\n\n const issuanceDate = issuanceDateStr\n if (!issuanceDate) {\n throw Error(`JWT issuance date is required but was not present`)\n }\n\n const credential: Omit<IVerifiableCredential, 'issuer' | 'issuanceDate'> = {\n type: [docType], // Mdoc not a W3C VC, so no VerifiableCredential\n '@context': [], // Mdoc has no JSON-LD by default. Certainly not the VC DM1 default context for JSON-LD\n credentialSubject: {\n type,\n ...credentialSubject,\n },\n issuanceDate,\n expirationDate,\n proof: {\n type: IProofType.MdocProof2024,\n created: issuanceDate,\n proofPurpose: IProofPurpose.authentication,\n verificationMethod: json.issuerSigned.issuerAuth.payload,\n mso_mdoc: com.sphereon.kmp.encodeTo(decoded.cborEncode(), com.sphereon.kmp.Encoding.BASE64URL),\n },\n }\n\n return credential as IVerifiableCredential\n}\n","import { decodeSdJwt, decodeSdJwtSync, getClaims, getClaimsSync } from '@sd-jwt/decode'\nimport type {\n AdditionalClaims,\n CompactSdJwtVc,\n Hasher,\n HasherSync,\n ICredentialSubject,\n IVerifiableCredential,\n SdJwtDecodedDisclosure,\n SdJwtDecodedVerifiableCredential,\n SdJwtDecodedVerifiableCredentialPayload,\n SdJwtDisclosure,\n SdJwtSignedVerifiableCredentialPayload,\n SdJwtType,\n SdJwtVcKbJwtHeader,\n SdJwtVcKbJwtPayload,\n SingleOrArray,\n} from '../types'\nimport { IProofPurpose, IProofType } from './did'\n\n/**\n * Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,\n * signed payload, decoded payload and the compact SD-JWT vc.\n *\n * Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so\n * this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)\n */\nexport function decodeSdJwtVc(compactSdJwtVc: CompactSdJwtVc, hasher: HasherSync): SdJwtDecodedVerifiableCredential {\n const { jwt, disclosures, kbJwt } = decodeSdJwtSync(compactSdJwtVc, hasher)\n\n const signedPayload = jwt.payload as SdJwtSignedVerifiableCredentialPayload\n const decodedPayload = getClaimsSync<any>(signedPayload, disclosures, hasher)\n const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined\n const type: SdJwtType = decodedPayload.vct ? 'dc+sd-jwt' : 'vc+sd-jwt'\n\n return {\n compactSdJwtVc,\n type,\n decodedPayload: decodedPayload as SdJwtDecodedVerifiableCredentialPayload,\n disclosures: disclosures.map((d) => {\n const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value]\n if (!d._digest) throw new Error('Implementation error: digest not present in disclosure')\n return {\n decoded: decoded as SdJwtDecodedDisclosure,\n digest: d._digest,\n encoded: d.encode(),\n } satisfies SdJwtDisclosure\n }),\n signedPayload: signedPayload as SdJwtSignedVerifiableCredentialPayload,\n ...(compactKeyBindingJwt &&\n kbJwt && {\n kbJwt: {\n header: kbJwt.header as SdJwtVcKbJwtHeader,\n compact: compactKeyBindingJwt,\n payload: kbJwt.payload as SdJwtVcKbJwtPayload,\n },\n }),\n }\n}\n\n/**\n * Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,\n * signed payload, decoded payload and the compact SD-JWT vc.\n *\n * Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so\n * this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)\n */\nexport async function decodeSdJwtVcAsync(compactSdJwtVc: CompactSdJwtVc, hasher: Hasher): Promise<SdJwtDecodedVerifiableCredential> {\n const { jwt, disclosures, kbJwt } = await decodeSdJwt(compactSdJwtVc, hasher)\n\n const signedPayload = jwt.payload as SdJwtSignedVerifiableCredentialPayload\n const decodedPayload = await getClaims<any>(signedPayload, disclosures, hasher)\n const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined\n\n const type: SdJwtType = decodedPayload.vct ? 'dc+sd-jwt' : 'vc+sd-jwt'\n return {\n compactSdJwtVc,\n type,\n decodedPayload: decodedPayload as SdJwtDecodedVerifiableCredentialPayload,\n disclosures: disclosures.map((d) => {\n const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value]\n if (!d._digest) throw new Error('Implementation error: digest not present in disclosure')\n return {\n decoded: decoded as SdJwtDecodedDisclosure,\n digest: d._digest,\n encoded: d.encode(),\n } satisfies SdJwtDisclosure\n }),\n signedPayload: signedPayload as SdJwtSignedVerifiableCredentialPayload,\n ...(compactKeyBindingJwt &&\n kbJwt && {\n kbJwt: {\n header: kbJwt.header as SdJwtVcKbJwtHeader,\n payload: kbJwt.payload as SdJwtVcKbJwtPayload,\n compact: compactKeyBindingJwt,\n },\n }),\n }\n}\n\nexport const sdJwtDecodedCredentialToUniformCredential = (\n decoded: SdJwtDecodedVerifiableCredential,\n opts?: { maxTimeSkewInMS?: number },\n): IVerifiableCredential => {\n const { decodedPayload } = decoded\n const { exp, nbf, iss, iat, vct, cnf, status, jti, validUntil, validFrom } = decodedPayload\n let credentialSubject: SingleOrArray<ICredentialSubject & AdditionalClaims> | undefined = decodedPayload.credentialSubject as\n | SingleOrArray<ICredentialSubject & AdditionalClaims>\n | undefined\n\n let issuer = iss ?? decodedPayload.issuer\n if (typeof issuer === 'object' && 'id' in issuer && typeof issuer.id === 'string') {\n issuer = issuer.id as string\n }\n const subId = decodedPayload.sub ?? (typeof credentialSubject == 'object' && 'id' in credentialSubject ? credentialSubject.id : undefined)\n\n const maxSkewInMS = opts?.maxTimeSkewInMS ?? 1500\n\n const expirationDate = (validUntil as string | undefined) ?? jwtDateToISOString({ jwtClaim: exp, claimName: 'exp' })\n let issuanceDateStr = (validFrom as string | undefined) ?? jwtDateToISOString({ jwtClaim: iat, claimName: 'iat' })\n\n let nbfDateAsStr: string | undefined\n if (nbf) {\n nbfDateAsStr = jwtDateToISOString({ jwtClaim: nbf, claimName: 'nbf' })\n if (issuanceDateStr && nbfDateAsStr && issuanceDateStr !== nbfDateAsStr) {\n const diff = Math.abs(new Date(nbfDateAsStr).getTime() - new Date(issuanceDateStr).getTime())\n if (!maxSkewInMS || diff > maxSkewInMS) {\n throw Error(`Inconsistent issuance dates between JWT claim (${nbfDateAsStr}) and VC value (${iss})`)\n }\n }\n issuanceDateStr = nbfDateAsStr\n }\n const issuanceDate = issuanceDateStr\n if (!issuanceDate) {\n throw Error(`JWT issuance date is required but was not present`)\n }\n\n // Filter out the fields we don't want in credentialSubject\n const excludedFields = new Set(['vct', 'cnf', 'iss', 'iat', 'exp', 'nbf', 'jti', 'sub'])\n if (!credentialSubject) {\n credentialSubject = Object.entries(decodedPayload).reduce(\n (acc, [key, value]) => {\n if (\n !excludedFields.has(key) &&\n value !== undefined &&\n value !== '' &&\n !(typeof value === 'object' && value !== null && Object.keys(value).length === 0)\n ) {\n acc[key] = value\n }\n return acc\n },\n {} as Record<string, any>,\n )\n }\n const sdJwtVc = decodedPayload.vct && !decodedPayload.type\n const credential: Omit<IVerifiableCredential, 'issuer' | 'issuanceDate'> = {\n ...{ type: sdJwtVc ? [vct] : decodedPayload.type },\n ...{ '@context': sdJwtVc ? [] : decodedPayload['@context'] },\n credentialSubject: {\n ...credentialSubject,\n id: subId ?? jti,\n },\n ...(issuanceDate && (sdJwtVc ? { issuanceDate } : { validFrom: issuanceDateStr })),\n ...(expirationDate && (sdJwtVc ? { expirationDate } : { validUntil: expirationDate })),\n issuer: issuer,\n ...(cnf && { cnf }),\n ...(status && { status }),\n proof: {\n type: IProofType.SdJwtProof2024,\n created: nbfDateAsStr ?? issuanceDate,\n proofPurpose: IProofPurpose.authentication,\n verificationMethod: iss,\n jwt: decoded.compactSdJwtVc,\n },\n }\n\n return credential as IVerifiableCredential\n}\n\nconst jwtDateToISOString = ({\n jwtClaim,\n claimName,\n isRequired = false,\n}: {\n jwtClaim?: number\n claimName: string\n isRequired?: boolean\n}): string | undefined => {\n if (jwtClaim) {\n const claim = parseInt(jwtClaim.toString())\n // change JWT seconds to millisecond for the date\n return new Date(claim * (claim < 9999999999 ? 1000 : 1)).toISOString().replace(/\\.000Z/, 'Z')\n } else if (isRequired) {\n throw Error(`JWT claim ${claimName} is required but was not present`)\n }\n return undefined\n}\n","export enum JwkKeyType {\n EC = 'EC',\n RSA = 'RSA',\n oct = 'oct',\n OKP = 'OKP',\n}\n\nexport enum JoseSignatureAlgorithm {\n RS256 = 'RS256',\n RS384 = 'RS384',\n RS512 = 'RS512',\n ES256 = 'ES256',\n ES256K = 'ES256K',\n ES384 = 'ES384',\n ES512 = 'ES512',\n EdDSA = 'EdDSA',\n HS256 = 'HS256',\n HS384 = 'HS384',\n HS512 = 'HS512',\n PS256 = 'PS256',\n PS384 = 'PS384',\n PS512 = 'PS512',\n none = 'none',\n}\n\nexport enum JoseKeyOperation {\n SIGN = 'sign',\n VERIFY = 'verify',\n ENCRYPT = 'encrypt',\n DECRYPT = 'decrypt',\n WRAP_KEY = 'wrapKey',\n UNWRAP_KEY = 'unwrapKey',\n DERIVE_KEY = 'deriveKey',\n DERIVE_BITS = 'deriveBits',\n}\n\nexport enum JoseCurve {\n P_256 = 'P-256',\n P_384 = 'P-384',\n P_521 = 'P-521',\n X25519 = 'X25519',\n X448 = 'X448',\n EdDSA = 'EdDSA',\n Ed25519 = 'Ed25519',\n Ed448 = 'Ed448',\n secp256k1 = 'secp256k1',\n}\n","export enum ICoseKeyType {\n OKP = 1,\n EC2 = 2,\n RSA = 3,\n Symmetric = 4,\n Reserved = 0,\n}\n\nexport enum ICoseSignatureAlgorithm {\n ES256 = -7,\n ES256K = -47,\n ES384 = -35,\n ES512 = -36,\n EdDSA = -8,\n HS256_64 = 4,\n HS256 = 5,\n HS384 = 6,\n HS512 = 7,\n PS256 = -37,\n PS384 = -38,\n PS512 = -39,\n}\n\nexport enum ICoseKeyOperation {\n SIGN = 1,\n VERIFY = 2,\n ENCRYPT = 3,\n DECRYPT = 4,\n WRAP_KEY = 5,\n UNWRAP_KEY = 6,\n DERIVE_KEY = 7,\n DERIVE_BITS = 8,\n MAC_CREATE = 9,\n MAC_VERIFY = 10,\n}\n\nexport enum ICoseCurve {\n P_256 = 1,\n P_384 = 2,\n P_521 = 3,\n X25519 = 4,\n X448 = 5,\n Ed25519 = 6,\n Ed448 = 7,\n secp256k1 = -1,\n}\n","import type { CredentialFormat, PresentationFormat } from '../types'\n\nexport type CredentialEncoding = 'json' /*includes json-ld*/ | 'jwt' | 'cbor'\n\nexport type IssuerAttributeName = 'iss' | 'issuer' | 'issuerAuth'\nexport type SubjectAttributeName = 'subject' | 'id' | 'deviceMac' | 'TODO'\nexport type TypeAttributeName = 'type' | 'vct'\n\nexport type DataModel = 'W3C_VCDM' | 'IETF_SD_JWT' | 'ISO_MSO_MDOC'\n\nexport interface CredentialConstraint {\n credentialFormat: CredentialFormat\n presentationFormat: PresentationFormat\n maxSignatures: number\n encoding: CredentialEncoding\n dataModel: DataModel\n typeAttribute?: TypeAttributeName // optional since mdocs use namespace maps without an explicit type attribute\n issuerAttributes: [IssuerAttributeName]\n}\n\nexport enum StatusListCredentialIdMode {\n ISSUANCE = 'ISSUANCE',\n // PERSISTENCE = 'PERSISTENCE',\n NEVER = 'NEVER',\n}\n\nexport enum StatusListDriverType {\n AGENT_TYPEORM = 'agent_typeorm',\n /* AGENT_KV_STORE = 'agent_kv_store',\n GITHUB = 'github',\n AGENT_FILESYSTEM = 'agent_filesystem',*/\n}\n","import type { IssuerType } from '@veramo/core'\nimport type {\n Hasher,\n HasherSync,\n ICredential,\n IPresentation,\n IProof,\n IVerifiableCredential,\n IVerifiablePresentation,\n JwtDecodedVerifiableCredential,\n JwtDecodedVerifiablePresentation,\n MdocDeviceResponse,\n MdocDocument,\n MdocOid4vpMdocVpToken,\n OriginalVerifiableCredential,\n OriginalVerifiablePresentation,\n SdJwtDecodedVerifiableCredential,\n SdJwtDecodedVerifiableCredentialPayload,\n UniformVerifiablePresentation,\n W3CVerifiableCredential,\n W3CVerifiablePresentation,\n WrappedMdocCredential,\n WrappedSdJwtVerifiableCredential,\n WrappedSdJwtVerifiablePresentation,\n WrappedVerifiableCredential,\n WrappedVerifiablePresentation,\n WrappedW3CVerifiableCredential,\n} from '../types'\nimport {\n decodeMdocDeviceResponse,\n decodeMdocIssuerSigned,\n decodeSdJwtVc,\n decodeSdJwtVcAsync as decodeSdJwtVcAsyncFunc,\n defaultHasher,\n getMdocDecodedPayload,\n IProofPurpose,\n IProofType,\n isWrappedMdocCredential,\n isWrappedMdocPresentation,\n isWrappedW3CVerifiableCredential,\n isWrappedW3CVerifiablePresentation,\n mdocDecodedCredentialToUniformCredential,\n ObjectUtils,\n sdJwtDecodedCredentialToUniformCredential,\n} from '../utils'\nimport mdocPkg from '@sphereon/kmp-mdoc-core'\nimport { jwtDecode } from 'jwt-decode'\n\ntype DeviceResponseCbor = mdocPkg.com.sphereon.mdoc.data.device.DeviceResponseCbor\n\nexport const sha256 = (data: string | ArrayBuffer | SharedArrayBuffer): Uint8Array => {\n return defaultHasher(data, 'sha256')\n}\n\nexport class CredentialMapper {\n /**\n * Decodes a compact SD-JWT vc to it's decoded variant. This method can be used when the hasher implementation used is Async, and therefore not suitable for usage\n * with the other decode methods.\n */\n static decodeSdJwtVcAsync(compactSdJwtVc: string, hasher: Hasher): Promise<SdJwtDecodedVerifiableCredential> {\n return decodeSdJwtVcAsyncFunc(compactSdJwtVc, hasher ?? sha256)\n }\n\n /**\n * Decodes a Verifiable Presentation to a uniform format.\n *\n * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using\n * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods\n * instead of the compact SD-JWT.\n *\n * @param presentation\n * @param hasher Hasher implementation to use for SD-JWT decoding.\n */\n static decodeVerifiablePresentation(\n presentation: OriginalVerifiablePresentation,\n hasher?: HasherSync\n ): JwtDecodedVerifiablePresentation | IVerifiablePresentation | SdJwtDecodedVerifiableCredential | MdocOid4vpMdocVpToken | MdocDeviceResponse {\n if (CredentialMapper.isJwtEncoded(presentation)) {\n const payload = jwtDecode(presentation as string) as JwtDecodedVerifiablePresentation\n const header = jwtDecode(presentation as string, { header: true }) as Record<string, any>\n\n payload.vp.proof = {\n type: IProofType.JwtProof2020,\n created: payload.nbf,\n proofPurpose: IProofPurpose.authentication,\n verificationMethod: header['kid'] ?? payload.iss,\n jwt: presentation as string,\n }\n return payload\n } else if (CredentialMapper.isJwtDecodedPresentation(presentation)) {\n return presentation as JwtDecodedVerifiablePresentation\n } else if (CredentialMapper.isSdJwtEncoded(presentation)) {\n return decodeSdJwtVc(presentation, hasher ?? sha256)\n } else if (CredentialMapper.isSdJwtDecodedCredential(presentation)) {\n return presentation as SdJwtDecodedVerifiableCredential\n } else if (CredentialMapper.isMsoMdocOid4VPEncoded(presentation)) {\n return presentation as MdocOid4vpMdocVpToken\n } else if (CredentialMapper.isMsoMdocDecodedPresentation(presentation)) {\n return presentation as MdocDeviceResponse\n } else if (CredentialMapper.isJsonLdAsString(presentation)) {\n return JSON.parse(presentation as string) as IVerifiablePresentation\n } else {\n return presentation as IVerifiablePresentation\n }\n }\n\n /**\n * Decodes a Verifiable Credential to a uniform format.\n *\n * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using\n * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods\n * instead of the compact SD-JWT.\n *\n * @param credential\n * @param hasher Hasher implementation to use for SD-JWT decoding\n */\n static decodeVerifiableCredential(\n credential: OriginalVerifiableCredential,\n hasher?: HasherSync\n ): JwtDecodedVerifiableCredential | IVerifiableCredential | SdJwtDecodedVerifiableCredential {\n if (CredentialMapper.isJwtEncoded(credential)) {\n const payload = jwtDecode(credential as string) as JwtDecodedVerifiableCredential\n const header = jwtDecode(credential as string, { header: true }) as Record<string, any>\n payload.vc = payload.vc ?? {}\n payload.vc.proof = {\n type: IProofType.JwtProof2020,\n created: payload.nbf,\n proofPurpose: IProofPurpose.authentication,\n verificationMethod: header['kid'] ?? payload.iss,\n jwt: credential as string,\n }\n return payload\n } else if (CredentialMapper.isJwtDecodedCredential(credential)) {\n return credential\n } else if (CredentialMapper.isJsonLdAsString(credential)) {\n return JSON.parse(credential as string) as IVerifiableCredential\n } else if (CredentialMapper.isSdJwtEncoded(credential)) {\n return decodeSdJwtVc(credential, hasher ?? sha256)\n } else if (CredentialMapper.isSdJwtDecodedCredential(credential)) {\n return credential\n } else {\n return credential as IVerifiableCredential\n }\n }\n\n /**\n * Converts a presentation to a wrapped presentation.\n *\n * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using\n * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods\n * instead of the compact SD-JWT.\n *\n * @param originalPresentation\n * @param opts\n */\n static toWrappedVerifiablePresentation(\n originalPresentation: OriginalVerifiablePresentation,\n opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }\n ): WrappedVerifiablePresentation {\n // MSO_MDOC\n if (CredentialMapper.isMsoMdocDecodedPresentation(originalPresentation) || CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) {\n let deviceResponse: MdocDeviceResponse\n let originalType: OriginalType\n if (CredentialMapper.isMsoMdocOid4VPEncoded(originalPresentation)) {\n deviceResponse = decodeMdocDeviceResponse(originalPresentation)\n originalType = OriginalType.MSO_MDOC_ENCODED\n } else {\n deviceResponse = originalPresentation\n originalType = OriginalType.MSO_MDOC_DECODED\n }\n\n const mdocCredentials = deviceResponse.documents?.map(\n (doc) => CredentialMapper.toWrappedVerifiableCredential(doc, opts) as WrappedMdocCredential\n )\n if (!mdocCredentials || mdocCredentials.length === 0) {\n throw new Error('could not extract any mdoc credentials from mdoc device response')\n }\n\n return {\n type: originalType,\n format: 'mso_mdoc',\n original: originalPresentation,\n presentation: deviceResponse,\n decoded: deviceResponse,\n vcs: mdocCredentials,\n }\n }\n // SD-JWT\n if (CredentialMapper.isSdJwtDecodedCredential(originalPresentation) || CredentialMapper.isSdJwtEncoded(originalPresentation)) {\n let decodedPresentation: SdJwtDecodedVerifiableCredential\n if (CredentialMapper.isSdJwtEncoded(originalPresentation)) {\n decodedPresentation = decodeSdJwtVc(originalPresentation, opts?.hasher ?? sha256)\n } else {\n decodedPresentation = originalPresentation\n }\n return {\n type: CredentialMapper.isSdJwtDecodedCredential(originalPresentation) ? OriginalType.SD_JWT_VC_DECODED : OriginalType.SD_JWT_VC_ENCODED,\n format: 'dc+sd-jwt',\n original: originalPresentation,\n presentation: decodedPresentation,\n decoded: decodedPresentation.decodedPayload,\n // NOTE: we also include the SD-JWT VC as the VC, as the SD-JWT acts as both the VC and the VP\n vcs: [CredentialMapper.toWrappedVerifiableCredential(originalPresentation, opts) as WrappedSdJwtVerifiableCredential],\n }\n }\n\n // If the VP is not an encoded/decoded SD-JWT, we assume it will be a W3C VC\n const proof = CredentialMapper.getFirstProof(originalPresentation)\n const original =\n typeof originalPresentation !== 'string' && CredentialMapper.hasJWTProofType(originalPresentation) ? proof?.jwt : originalPresentation\n if (!original) {\n throw Error(\n 'Could not determine original presentation, probably it was a converted JWT presentation, that is now missing the JWT value in the proof'\n )\n }\n const decoded = CredentialMapper.decodeVerifiablePresentation(original) as IVerifiablePresentation | JwtDecodedVerifiablePresentation\n const isJwtEncoded: boolean = CredentialMapper.isJwtEncoded(original)\n const isJwtDecoded: boolean = CredentialMapper.isJwtDecodedPresentation(original)\n\n const type = isJwtEncoded ? OriginalType.JWT_ENCODED : isJwtDecoded ? OriginalType.JWT_DECODED : OriginalType.JSONLD\n const format = isJwtDecoded || isJwtEncoded ? 'jwt_vp' : ('ldp_vp' as const)\n\n let vp: OriginalVerifiablePresentation\n if (isJwtEncoded || isJwtDecoded) {\n vp = CredentialMapper.jwtDecodedPresentationToUniformPresentation(decoded as JwtDecodedVerifiablePresentation, false, opts)\n } else {\n vp = decoded as IVerifiablePresentation\n }\n if (!vp) {\n throw Error(`VP key not found`)\n }\n const noVCs = !('verifiableCredential' in vp) || !vp.verifiableCredential || vp.verifiableCredential.length === 0\n if (noVCs) {\n console.warn(`Presentation without verifiable credentials. That is rare! `)\n // throw Error(`VP needs to have at least one verifiable credential at this point`)\n }\n const vcs = noVCs\n ? []\n : (CredentialMapper.toWrappedVerifiableCredentials(\n vp.verifiableCredential ?? [] /*.map(value => value.original)*/,\n opts\n ) as WrappedW3CVerifiableCredential[])\n\n const presentation = {\n ...vp,\n verifiableCredential: vcs, // We overwrite the verifiableCredentials with wrapped versions, making it an InternalVerifiablePresentation. Note: we keep the singular key name of the vc data model\n } as UniformVerifiablePresentation\n return {\n type,\n format,\n original,\n decoded,\n presentation,\n vcs,\n }\n }\n\n /**\n * Converts a list of credentials to a list of wrapped credentials.\n *\n * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using\n * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods\n * instead of the compact SD-JWT.\n *\n * @param hasher Hasher implementation to use for SD-JWT decoding\n */\n static toWrappedVerifiableCredentials(\n verifiableCredentials: OriginalVerifiableCredential[],\n opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }\n ): WrappedVerifiableCredential[] {\n return verifiableCredentials.map((vc) => CredentialMapper.toWrappedVerifiableCredential(vc, opts))\n }\n\n /**\n * Converts a credential to a wrapped credential.\n *\n * When decoding SD-JWT credentials, a hasher implementation must be provided. The hasher implementation must be sync. When using\n * an async hasher implementation, use the decodeSdJwtVcAsync method instead and you can provide the decoded payload to methods\n * instead of the compact SD-JWT.\n *\n * @param hasher Hasher implementation to use for SD-JWT decoding\n */\n static toWrappedVerifiableCredential(\n verifiableCredential: OriginalVerifiableCredential,\n opts?: { maxTimeSkewInMS?: number; hasher?: HasherSync }\n ): WrappedVerifiableCredential {\n // MSO_MDOC\n if (CredentialMapper.isMsoMdocDecodedCredential(verifiableCredential) || CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) {\n let mdoc: MdocDocument\n if (CredentialMapper.isMsoMdocOid4VPEncoded(verifiableCredential)) {\n mdoc = decodeMd