diff --git a/demo-openid/src/Holder.ts b/demo-openid/src/Holder.ts index d16cdc7498..09187f59ab 100644 --- a/demo-openid/src/Holder.ts +++ b/demo-openid/src/Holder.ts @@ -5,6 +5,7 @@ import { W3cJwtVerifiableCredential, W3cJsonLdVerifiableCredential, DifPresentationExchangeService, + Mdoc, } from '@credo-ts/core' import { OpenId4VcHolderModule } from '@credo-ts/openid4vc' import { ariesAskar } from '@hyperledger/aries-askar-nodejs' @@ -56,6 +57,8 @@ export class Holder extends BaseAgent> const credential = response.credential if (credential instanceof W3cJwtVerifiableCredential || credential instanceof W3cJsonLdVerifiableCredential) { return this.agent.w3cCredentials.storeCredential({ credential }) + } else if (credential instanceof Mdoc) { + return this.agent.mdoc.store(credential) } else { return this.agent.sdJwtVc.store(credential.compact) } diff --git a/demo-openid/src/HolderInquirer.ts b/demo-openid/src/HolderInquirer.ts index f346e951e1..87714f5a7f 100644 --- a/demo-openid/src/HolderInquirer.ts +++ b/demo-openid/src/HolderInquirer.ts @@ -1,7 +1,7 @@ -import type { SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core' +import type { MdocRecord, SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core' import type { OpenId4VcSiopResolvedAuthorizationRequest, OpenId4VciResolvedCredentialOffer } from '@credo-ts/openid4vc' -import { DifPresentationExchangeService } from '@credo-ts/core' +import { DifPresentationExchangeService, Mdoc } from '@credo-ts/core' import console, { clear } from 'console' import { textSync } from 'figlet' import { prompt } from 'inquirer' @@ -181,11 +181,16 @@ export class HolderInquirer extends BaseInquirer { } } - private printCredential = (credential: W3cCredentialRecord | SdJwtVcRecord) => { + private printCredential = (credential: W3cCredentialRecord | SdJwtVcRecord | MdocRecord) => { if (credential.type === 'W3cCredentialRecord') { console.log(greenText(`W3cCredentialRecord with claim format ${credential.credential.claimFormat}`, true)) console.log(JSON.stringify(credential.credential.jsonCredential, null, 2)) console.log('') + } else if (credential.type === 'MdocRecord') { + console.log(greenText(`MdocRecord`, true)) + const namespaces = Mdoc.fromIssuerSignedHex(credential.issuerSignedHex).namespaces + console.log(JSON.stringify(namespaces, null, 2)) + console.log('') } else { console.log(greenText(`SdJwtVcRecord`, true)) const prettyClaims = this.holder.agent.sdJwtVc.fromCompact(credential.compactSdJwtVc).prettyClaims diff --git a/packages/core/package.json b/packages/core/package.json index 5904500d8c..071284f9a9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -40,6 +40,7 @@ "@sd-jwt/sd-jwt-vc": "^0.7.0", "@sd-jwt/types": "^0.7.0", "@sd-jwt/utils": "^0.7.0", + "@sphereon/kmp-mdl-mdoc": "0.1.0-SNAPSHOT.21", "@sphereon/pex": "^3.3.2", "@sphereon/pex-models": "^2.2.4", "@sphereon/ssi-types": "^0.28.0", @@ -54,7 +55,7 @@ "did-resolver": "^4.1.0", "jsonpath": "^1.1.1", "lru_map": "^0.4.1", - "luxon": "^3.3.0", + "luxon": "^3.5.0", "make-error": "^1.3.6", "object-inspect": "^1.10.3", "query-string": "^7.0.1", diff --git a/packages/core/src/agent/AgentModules.ts b/packages/core/src/agent/AgentModules.ts index 5b0727a94b..b3bf75f162 100644 --- a/packages/core/src/agent/AgentModules.ts +++ b/packages/core/src/agent/AgentModules.ts @@ -10,6 +10,7 @@ import { DidsModule } from '../modules/dids' import { DifPresentationExchangeModule } from '../modules/dif-presentation-exchange' import { DiscoverFeaturesModule } from '../modules/discover-features' import { GenericRecordsModule } from '../modules/generic-records' +import { MdocModule } from '../modules/mdoc' import { MessagePickupModule } from '../modules/message-pickup' import { OutOfBandModule } from '../modules/oob' import { ProofsModule } from '../modules/proofs' @@ -136,6 +137,7 @@ function getDefaultAgentModules() { cache: () => new CacheModule(), pex: () => new DifPresentationExchangeModule(), sdJwtVc: () => new SdJwtVcModule(), + mdoc: () => new MdocModule(), x509: () => new X509Module(), } as const } diff --git a/packages/core/src/agent/BaseAgent.ts b/packages/core/src/agent/BaseAgent.ts index e982bf81c4..742f08f142 100644 --- a/packages/core/src/agent/BaseAgent.ts +++ b/packages/core/src/agent/BaseAgent.ts @@ -14,6 +14,7 @@ import { CredentialsApi } from '../modules/credentials' import { DidsApi } from '../modules/dids' import { DiscoverFeaturesApi } from '../modules/discover-features' import { GenericRecordsApi } from '../modules/generic-records' +import { MdocApi } from '../modules/mdoc' import { MessagePickupApi } from '../modules/message-pickup/MessagePickupApi' import { OutOfBandApi } from '../modules/oob' import { ProofsApi } from '../modules/proofs' @@ -60,6 +61,7 @@ export abstract class BaseAgent> @@ -110,6 +112,7 @@ export abstract class BaseAgent = com.sphereon.mdoc.data.device.IssuerSignedItemJson +type MdocNamespaceData = Record +type MdocNamespace = Record + +export class Mdoc { + private issuerSignedCbor: IssuerSignedCbor + private issuerSignedJson: IssuerSignedJson + + private constructor(buffer: Buffer) { + // TODO: CONVERSION FROM CBOR TO JSON AND BACK CURRENTLY NOT COMPLETELY WORKING THEREFORE WE STORE BOTH FOR NOW + this.issuerSignedCbor = com.sphereon.mdoc.data.device.IssuerSignedCbor.Companion.cborDecode(Int8Array.from(buffer)) + this.issuerSignedJson = this.issuerSignedCbor.toJson() + } + + public get docType() { + // TODO: This will be a part of the { ... issuerSigned } structure + return 'org.iso.18013.5.1.mDL' + } + + // TODO: Use a different return type. Wait for sphereon + public get namespaces(): Record { + const namespaces: MdocNamespace = {} + const entries = this.issuerSignedJson.nameSpaces?.asJsMapView().entries() + + // eslint-disable-next-line no-constant-condition + while (true) { + const next = entries?.next() + if (next?.done) break + if (!next?.value) { + throw new MdocError('Missing value in Mdoc') + } + + // TODO: This is not completely working yet. The values are still not json if nested. + // TODO: wait for sphereon to complete + // Waiting for fix from sphereon + const mdocDataItem = next.value[1] + const mdocDataItemRecord: Record< + string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + com.sphereon.mdoc.data.device.IssuerSignedItemJson['elementValue'] + > = Object.fromEntries(mdocDataItem.map((dataItem) => [dataItem.elementIdentifier, dataItem.elementValue])) + + namespaces[next.value[0]] = mdocDataItemRecord + } + + return namespaces + } + + public get jwaSignatureAlgorithm() { + const alg = this.issuerSignedJson.issuerAuth.protectedHeader.alg + if (!alg) { + throw new MdocError(`Missing Signature Algorithm in Mdoc.`) + } + + const jwaAlgorithm = com.sphereon.crypto.SignatureAlgorithmMapping.Companion.toJose(alg) + .value as JwaSignatureAlgorithm + + if (!Object.values(JwaSignatureAlgorithm).includes(jwaAlgorithm)) { + throw new MdocError(`Invalid Signature Algorithm on MDoc Document. Alg '${alg}'`) + } + + return jwaAlgorithm + } + + public static fromIssuerSignedHex(hexEncodedMdoc: string) { + return new Mdoc(TypedArrayEncoder.fromHex(hexEncodedMdoc)) + } + + public static fromIssuerSignedBase64(issuerSignedBase64: string) { + return new Mdoc(TypedArrayEncoder.fromBase64(issuerSignedBase64)) + } + + public get issuerSignedHex() { + return TypedArrayEncoder.toHex(Buffer.from(this.issuerSignedCbor.toCbor().cborEncode())) + } + + public get issuerSignedBase64UrlEncoded() { + return TypedArrayEncoder.toBase64URL(Buffer.from(this.issuerSignedCbor.toCbor().cborEncode())) + } + + public async verify(agentContext: AgentContext, options?: { trustedCertificates?: [string, ...string[]] }) { + const { trustedCertificates } = options ?? {} + + const cryptoServiceJS = com.sphereon.crypto.CryptoServiceJS + + if (agentContext.contextCorrelationId !== 'default') { + // TODO: This way of of registering and working with the x509/cose services is subject to race-conditions + // TODO: This is a known issue and beeing worked on by sphereon + throw new MdocError('Multitenancy is currently not supported for Mdoc.') + } + + cryptoServiceJS.X509.register(new MdocX509CallbackService(agentContext, trustedCertificates)) + cryptoServiceJS.COSE.register(new MdocCoseCallbackService(agentContext)) + + const certificateChain = this.issuerSignedCbor.issuerAuth.toJson().unprotectedHeader?.x5chain + if (!certificateChain) { + return { + isValid: false, + error: `The issuer signed structure is missing the 'x5chain' property in the unprotected header.`, + } + } + + // TODO: CHECK why this is required + const _leafCertificate = TypedArrayEncoder.toBase64(TypedArrayEncoder.fromBase64(certificateChain[0])) + const leafCertificate = X509Service.getLeafCertificate(agentContext, { certificateChain: [_leafCertificate] }) + + const verificationResult = await com.sphereon.mdoc.ValidationsJS.fromIssuerAuthAsync( + this.issuerSignedCbor.issuerAuth, + // This key is used later on in the cose verification callback for signature verification + { opts: kotlin.collections.KtMap.fromJsMap(new Map([['publicKey', leafCertificate.publicKey]])) }, + trustedCertificates + ) + + if (verificationResult.error) { + return { + isValid: false, + error: verificationResult.verifications.join(' '), + } + } + + return { isValid: true } + } +} diff --git a/packages/core/src/modules/mdoc/MdocApi.ts b/packages/core/src/modules/mdoc/MdocApi.ts new file mode 100644 index 0000000000..7d36aeca77 --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocApi.ts @@ -0,0 +1,72 @@ +import type { MdocVerifyOptions } from './MdocOptions' +import type { MdocRecord } from './repository' +import type { Query, QueryOptions } from '../../storage/StorageService' + +import { AgentContext } from '../../agent' +import { injectable } from '../../plugins' + +import { Mdoc } from './Mdoc' +import { MdocService } from './MdocService' + +/** + * @public + */ +@injectable() +export class MdocApi { + private agentContext: AgentContext + private mdocService: MdocService + + public constructor(agentContext: AgentContext, mdocService: MdocService) { + this.agentContext = agentContext + this.mdocService = mdocService + } + + /** + * + * Verify an incoming mdoc. It will check whether everything is valid, but also returns parts of the validation. + * + * For example, you might still want to continue with a flow if not all the claims are included, but the signature is valid. + * + */ + public async verify(options: MdocVerifyOptions) { + return await this.mdocService.verify(this.agentContext, options) + } + + /** + * Create an Mdoc class from a hex encoded Mdoc Issuer-Signed structure + */ + public fromIssuerSignedHex(hexEncodedMdoc: string) { + return Mdoc.fromIssuerSignedHex(hexEncodedMdoc) + } + + /** + * Create an Mdoc class from a base64/base64url encoded Mdoc Issuer-Signed structure + */ + public fromIssuerSignedBase64(issuerSignedBase64: string) { + return Mdoc.fromIssuerSignedBase64(issuerSignedBase64) + } + + public async store(mdoc: Mdoc) { + return await this.mdocService.store(this.agentContext, mdoc) + } + + public async getById(id: string): Promise { + return await this.mdocService.getById(this.agentContext, id) + } + + public async getAll(): Promise> { + return await this.mdocService.getAll(this.agentContext) + } + + public async findAllByQuery(query: Query, queryOptions?: QueryOptions): Promise> { + return await this.mdocService.findByQuery(this.agentContext, query, queryOptions) + } + + public async deleteById(id: string) { + return await this.mdocService.deleteById(this.agentContext, id) + } + + public async update(mdocRecord: MdocRecord) { + return await this.mdocService.update(this.agentContext, mdocRecord) + } +} diff --git a/packages/core/src/modules/mdoc/MdocCoseCallbackService.ts b/packages/core/src/modules/mdoc/MdocCoseCallbackService.ts new file mode 100644 index 0000000000..7b5f48b6aa --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocCoseCallbackService.ts @@ -0,0 +1,74 @@ +import type { AgentContext } from '../../agent' + +import { type Nullable, com } from '@sphereon/kmp-mdl-mdoc' + +import { getJwkFromKey } from '../../crypto/jose/jwk/transform' +import { TypedArrayEncoder } from '../../utils/TypedArrayEncoder' + +import { MdocError } from './MdocError' + +type ICoseKeyCbor = com.sphereon.cbor.cose.ICoseKeyCbor +type ICoseCallbackServiceJS = com.sphereon.crypto.ICoseCryptoCallbackJS +type KeyInfo = com.sphereon.crypto.IKeyInfo +type CoseSign1Cbor = com.sphereon.cbor.cose.CoseSign1Cbor +type IKey = com.sphereon.cbor.cose.IKey +type IVerifySignatureResult = com.sphereon.crypto.IVerifySignatureResult + +const mdlJwk = com.sphereon.jose.jwk.Jwk + +/** + * This class can be used for Cose signing and sigature verification. + * Either have an instance per trustedCerts and verification invocation or use a single instance and provide the trusted certs in the method argument + * + * The class is also registered with the low-level mDL/mdoc Kotlin Multiplatform library + * Next to the specific function for the library it exports a more powerful version of the same verification method as well + */ +export class MdocCoseCallbackService implements ICoseCallbackServiceJS { + public constructor(private agentContext: AgentContext) {} + public async sign1( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + coseCborInput: CoseSign1Cbor, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + keyInfo: Nullable + ): Promise> { + throw new MdocError('Method not yet implemented') + } + + /** + * This method is the implementation used within the mDL/Mdoc library + */ + public async verify1( + input: CoseSign1Cbor, + keyInfo?: KeyInfo + ): Promise> { + const sign1Json = input.toJson() // Let's make it a bit easier on ourselves, instead of working with CBOR + const coseAlg = sign1Json.protectedHeader.alg + if (!coseAlg) { + return Promise.reject(Error('No alg protected header present')) + } + + if (!keyInfo?.opts) throw new MdocError('Mdoc Verification Callback missing keyInfo.') + const kid = keyInfo?.kid ?? sign1Json.protectedHeader.kid ?? sign1Json.unprotectedHeader?.kid + + const publicKey = keyInfo.opts?.asJsReadonlyMapView().get('publicKey') + if (!publicKey) new MdocError('Mdoc Verification Callback missing publicKey Jwk.') + + const publicKeyJwk = getJwkFromKey(publicKey).toJson() + const coseKey = mdlJwk.Companion.fromJsonObject(publicKeyJwk).jwkToCoseKeyJson() + const recalculatedToBeSigned = input.toBeSignedJson(coseKey, coseAlg) + + const valid = await this.agentContext.wallet.verify({ + key: publicKey, + signature: TypedArrayEncoder.fromBase64(sign1Json.signature), + data: TypedArrayEncoder.fromHex(recalculatedToBeSigned.hexValue), + }) + + return { + name: 'cose-verification', + message: valid ? 'cose-signature successfully validated' : 'cose-signature could not be validated', + keyInfo: keyInfo ?? ({ kid, key: coseKey.toCbor() } satisfies KeyInfo), + critical: !valid, + error: !valid, + } satisfies IVerifySignatureResult + } +} diff --git a/packages/core/src/modules/mdoc/MdocError.ts b/packages/core/src/modules/mdoc/MdocError.ts new file mode 100644 index 0000000000..ff9c8e49ab --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocError.ts @@ -0,0 +1,3 @@ +import { CredoError } from '../../error' + +export class MdocError extends CredoError {} diff --git a/packages/core/src/modules/mdoc/MdocModule.ts b/packages/core/src/modules/mdoc/MdocModule.ts new file mode 100644 index 0000000000..98b140581e --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocModule.ts @@ -0,0 +1,32 @@ +import type { Module, DependencyManager } from '../../plugins' + +import { AgentConfig } from '../../agent/AgentConfig' + +import { MdocApi } from './MdocApi' +import { MdocService } from './MdocService' +import { MdocRepository } from './repository' + +/** + * @public + */ +export class MdocModule implements Module { + public readonly api = MdocApi + + /** + * Registers the dependencies of the mdoc module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The 'Mdoc' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Services + dependencyManager.registerSingleton(MdocService) + + // Repositories + dependencyManager.registerSingleton(MdocRepository) + } +} diff --git a/packages/core/src/modules/mdoc/MdocOptions.ts b/packages/core/src/modules/mdoc/MdocOptions.ts new file mode 100644 index 0000000000..9f69eb993f --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocOptions.ts @@ -0,0 +1,6 @@ +import type { Mdoc } from './Mdoc' + +export type MdocVerifyOptions = { + mdoc: Mdoc + trustedCertificates?: [string, ...string[]] +} diff --git a/packages/core/src/modules/mdoc/MdocService.ts b/packages/core/src/modules/mdoc/MdocService.ts new file mode 100644 index 0000000000..bf9be4cb9e --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocService.ts @@ -0,0 +1,77 @@ +import type { MdocVerifyOptions } from './MdocOptions' +import type { Query, QueryOptions } from '../../storage/StorageService' + +import { injectable } from 'tsyringe' + +import { AgentContext } from '../../agent' +import { TypedArrayEncoder } from '../../utils' +import { X509ModuleConfig } from '../x509' + +import { Mdoc } from './Mdoc' +import { MdocError } from './MdocError' +import { MdocRecord, MdocRepository } from './repository' + +/** + * @internal + */ +@injectable() +export class MdocService { + private MdocRepository: MdocRepository + + public constructor(mdocRepository: MdocRepository) { + this.MdocRepository = mdocRepository + } + + public fromIssuerSignedHex(hexEncodedMdoc: string) { + return Mdoc.fromIssuerSignedHex(hexEncodedMdoc) + } + + public fromIssuerSignedBase64(issuerSignedBase64: string) { + const hexEncodedMdoc = TypedArrayEncoder.fromBase64(issuerSignedBase64).toString('hex') + + return Mdoc.fromIssuerSignedHex(hexEncodedMdoc) + } + + public async verify(agentContext: AgentContext, options: MdocVerifyOptions) { + const { mdoc } = options + const trustedCertificates = + options.trustedCertificates ?? agentContext.dependencyManager.resolve(X509ModuleConfig).trustedCertificates + + if (!trustedCertificates) { + throw new MdocError('Mdoc Verification failed. Missing trusted certificates.') + } + + return await mdoc.verify(agentContext, { trustedCertificates }) + } + + public async store(agentContext: AgentContext, mdoc: Mdoc) { + const mdocRecord = new MdocRecord({ mdoc }) + await this.MdocRepository.save(agentContext, mdocRecord) + + return mdocRecord + } + + public async getById(agentContext: AgentContext, id: string): Promise { + return await this.MdocRepository.getById(agentContext, id) + } + + public async getAll(agentContext: AgentContext): Promise> { + return await this.MdocRepository.getAll(agentContext) + } + + public async findByQuery( + agentContext: AgentContext, + query: Query, + queryOptions?: QueryOptions + ): Promise> { + return await this.MdocRepository.findByQuery(agentContext, query, queryOptions) + } + + public async deleteById(agentContext: AgentContext, id: string) { + await this.MdocRepository.deleteById(agentContext, id) + } + + public async update(agentContext: AgentContext, mdocRecord: MdocRecord) { + await this.MdocRepository.update(agentContext, mdocRecord) + } +} diff --git a/packages/core/src/modules/mdoc/MdocX509CallbackService.ts b/packages/core/src/modules/mdoc/MdocX509CallbackService.ts new file mode 100644 index 0000000000..042502347c --- /dev/null +++ b/packages/core/src/modules/mdoc/MdocX509CallbackService.ts @@ -0,0 +1,79 @@ +import type { com, Nullable } from '@sphereon/kmp-mdl-mdoc' + +import { type AgentContext } from '../../agent' +import { X509ModuleConfig } from '../x509' +import { X509Certificate } from '../x509/X509Certificate' +import { X509Service } from '../x509/X509Service' + +import { MdocError } from './MdocError' + +type IX509CallbackServiceJS = com.sphereon.crypto.IX509ServiceJS + +type IKey = com.sphereon.cbor.cose.IKey + +type IX509VerificationResult = com.sphereon.crypto.IX509VerificationResult + +/** + * This class can be used for X509 validations. + * Either have an instance per trustedCerts and verification invocation or use a single instance and provide the trusted certs in the method argument + * + * The class is also registered with the low-level mDL/mdoc Kotlin Multiplatform library + * Next to the specific function for the library it exports a more powerful version of the same verification method as well + */ +export class MdocX509CallbackService implements IX509CallbackServiceJS { + private trustedCertificates: [string, ...string[]] + public constructor(private agentContext: AgentContext, trustedCertificates?: [string, ...string[]]) { + const _trustedCertificates = + trustedCertificates ?? agentContext.dependencyManager.resolve(X509ModuleConfig).trustedCertificates + + if (!_trustedCertificates) { + throw new MdocError('Missing trusted certificates for Mdoc validation.') + } + this.trustedCertificates = _trustedCertificates + } + + /** + * This method is the implementation used within the mDL/Mdoc library + */ + public async verifyCertificateChainJS( + chainDER: Nullable + ): Promise> { + if (!chainDER) { + return { + name: 'x509-verification invalid parameters', + message: 'Missing ChainDER parameter when verifying the Certificate chain.', + critical: true, + error: true, + } satisfies IX509VerificationResult + } + + try { + const certificateChain = chainDER.map((value) => + X509Certificate.fromRawCertificate(new Uint8Array(value)).toString('base64url') + ) + + await X509Service.validateCertificateChain(this.agentContext, { certificateChain }) + + return { + name: 'x509-verification', + message: 'x509-chain successfully validated', + critical: false, + error: false, + } satisfies IX509VerificationResult + } catch (error) { + return { + name: 'x509-verification', + message: + error instanceof Error + ? `Modoc x509 certificate chain validation failed. ${error.message}` + : 'An unknown error occurred during x509 certificate chain validation.', + critical: true, + error: true, + } satisfies IX509VerificationResult + } + } + + public getTrustedCerts = () => { + return this.trustedCertificates + } +} diff --git a/packages/core/src/modules/mdoc/__tests__/mdoc.fixtures.ts b/packages/core/src/modules/mdoc/__tests__/mdoc.fixtures.ts new file mode 100644 index 0000000000..61ee6fb994 --- /dev/null +++ b/packages/core/src/modules/mdoc/__tests__/mdoc.fixtures.ts @@ -0,0 +1,65 @@ +export const sprindFunkeTestVectorBase64Url = + 'omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQRA2BhZBDumZ2RvY1R5cGV3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjFndmVyc2lvbmMxLjBsdmFsaWRpdHlJbmZvo2ZzaWduZWR0MjAyNC0wNi0yNFQwNjo1MDo0MFppdmFsaWRGcm9tdDIwMjQtMDYtMjRUMDY6NTA6NDBaanZhbGlkVW50aWx0MjAyNC0wNy0wOFQwNjo1MDo0MFpsdmFsdWVEaWdlc3RzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMbYAWCDJVfFwuYp2QoZROAvEN2pyUZ1KM8pEWRZXfdWrF1HkigFYIHhpl7kR5NAjeLSFJd0LsjMB9_ZeOBi-pYiOSwG78rrEAlggEih2FMRoq01sCrA8gZ-r_pUqi7add99aSg_l9iuV7w8DWCD9umaT-ULFoZSewraVNXFFWf3iNm5rgj75OQAy7n-1HQRYIL8xH7_OLXmsTruVMI1AInTjtDyPiDkk3ZaljsXFMaeYBVgg2-7WIwtpcZgVI3ZpKiFOqf8cV_R8G20adAqk3xLmaR8GWCCMFjcNb1Yp0rw86h1OOYCPzIhE-Dt5yWCQ7BTpNbZBuwdYIEzmGyjypgomuuwlwyp44zLi6sXT11ZNoyDAMKEsNP0pCFggI2ENhbCnOrZsVvqNE1GJe13ygY7MMU_Hv7l7j60Y5BgJWCBDZb6ztiG-09jmZNNc3Qi4e1OhyqtNmrOxzuzCtMYKcgpYIDGYllJw4PxQlyaeiI-a0qaeD9C3qh2hKXtvYYol928zC1gg4etokah75K55-qzJ6_FtE2KtAF9gy3gzcTeirdZ3LHwMWCDnCnqeX1M1iJe3LH2qc0kJOXQHYUEubpqVi2c4wtt3xQ1YIL7dVtgkdG9n2pDvrBtgY21i7X7YyiVCe-p61mtghwjnDlggQk4FkmKScm6oCwHtt5Og5E_1SQfuWpFIMdj0x8ZCS0wPWCBGMDXYqqBPDqeqBoFn3IKJSZWcdMj7KyU1ZtNOZ3OE6hBYIJyzjluOe_VlYSQw1aIBcrsnnF2czy5ypChycRfi0nrOEVggKOd_n9xKuZDdnak-vQ1zrIzSWLxJIlPgJMpLEn2FuLYSWCBHx1eoCb1ydVj_EGIKUOYPCyEjAgP5HxN-J_zSZUwkKBNYIN0hCZPdhjF4pU-LVEoQi7FdOSF3lrQ8EimA7C31NcVhFFggxtk6j0328cyjnwNoWKCUgvg1Uk37Bktpzb4atlRT5VIVWCAMujq43dRJg7XilJJL0z-hxQoLUpkzO2tq6H6LazG0uW1kZXZpY2VLZXlJbmZvoWlkZXZpY2VLZXmkAQIgASFYIMrI7GWNvKwCXqwcJmkBMyIRAXejiET9PRAFCMhJEfo9IlggEvXLy65sT8QyzLnWsC7aIM1eem2029awDcWI7WO0ES9vZGlnZXN0QWxnb3JpdGhtZ1NIQS0yNTZYQLVKBk4WMWUjTFWSwUuz7vCPNCAqw5x7HIBHVr1H_gC5WOEXxBaFlnxHYBjBguFSfLe5e-7t82ySdef7uvo6d2NqbmFtZVNwYWNlc6F3ZXUuZXVyb3BhLmVjLmV1ZGkucGlkLjGW2BhYVqRmcmFuZG9tUPYpQ7wOENpcyi6n1L56UdhoZGlnZXN0SUQAbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMRgxk_vnHlF0GwDT1_ULxJoZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhbpGZyYW5kb21QKjeWt5G4r5-qtZytkvPCY2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZkdBQkxFUnFlbGVtZW50SWRlbnRpZmllcnFmYW1pbHlfbmFtZV9iaXJ0aNgYWFOkZnJhbmRvbVBDbqFvUf9mgbrDQOa3wxwcaGRpZ2VzdElEA2xlbGVtZW50VmFsdWVlRVJJS0FxZWxlbWVudElkZW50aWZpZXJqZ2l2ZW5fbmFtZdgYWFSkZnJhbmRvbVC0poiPe3Qx58JWmtP7Q_WGaGRpZ2VzdElEBGxlbGVtZW50VmFsdWUZB6xxZWxlbWVudElkZW50aWZpZXJuYWdlX2JpcnRoX3llYXLYGFhPpGZyYW5kb21Qu7cn53_6IG1TiAz9anV2VGhkaWdlc3RJRAVsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xONgYWE-kZnJhbmRvbVCRPYwpMh16--3IgrBqvPiHaGRpZ2VzdElEBmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzIx2BhYVqRmcmFuZG9tUGu5N18O3ztKBJRIqXuXprFoZGlnZXN0SUQHbGVsZW1lbnRWYWx1ZWVLw5ZMTnFlbGVtZW50SWRlbnRpZmllcm1yZXNpZGVudF9jaXR52BhYbKRmcmFuZG9tUDKXb5L9OGRMoOqY4ixLrj5oZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZaJldmFsdWViREVrY291bnRyeU5hbWVnR2VybWFueXFlbGVtZW50SWRlbnRpZmllcmtuYXRpb25hbGl0edgYWFmkZnJhbmRvbVD4nB3KeJEBfi7oTQaUgKmcaGRpZ2VzdElECWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWFWkZnJhbmRvbVDzJdpDC6MZvIaVDJ_psS7JaGRpZ2VzdElECmxlbGVtZW50VmFsdWVmQkVSTElOcWVsZW1lbnRJZGVudGlmaWVya2JpcnRoX3BsYWNl2BhYVaRmcmFuZG9tUKEIada4bfyv5GeAbFb3reZoZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcm9pc3N1aW5nX2NvdW50cnnYGFhPpGZyYW5kb21Qqbo3TPNv6ilm7tvlR4l_GGhkaWdlc3RJRAxsZWxlbWVudFZhbHVl9HFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl82NdgYWGykZnJhbmRvbVC_nvMTClyTddZfwm_WviXAaGRpZ2VzdElEDWxlbGVtZW50VmFsdWWiZG5hbm8aNQgmzGtlcG9jaFNlY29uZBpmeRdAcWVsZW1lbnRJZGVudGlmaWVybWlzc3VhbmNlX2RhdGXYGFhqpGZyYW5kb21QPqCKymVJhGPADlN7tILk2mhkaWdlc3RJRA5sZWxlbWVudFZhbHVlomRuYW5vGjUIJsxrZXBvY2hTZWNvbmQaZouMQHFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZdgYWGOkZnJhbmRvbVC0Cd-E5IjcJYTHKNzujqXlaGRpZ2VzdElED2xlbGVtZW50VmFsdWVwSEVJREVTVFJB4bqeRSAxN3FlbGVtZW50SWRlbnRpZmllcm9yZXNpZGVudF9zdHJlZXTYGFhPpGZyYW5kb21QBSfulxP_wSm8WUJ31jD9U2hkaWdlc3RJRBBsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNtgYWF2kZnJhbmRvbVDAyvF8NuW7ZU4yWPFlZEQ9aGRpZ2VzdElEEWxlbGVtZW50VmFsdWVlNTExNDdxZWxlbWVudElkZW50aWZpZXJ0cmVzaWRlbnRfcG9zdGFsX2NvZGXYGFhYpGZyYW5kb21QH_0ki1hqwWblAMFbrwMO2GhkaWdlc3RJRBJsZWxlbWVudFZhbHVlajE5NjQtMDgtMTJxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWFekZnJhbmRvbVBaUAbNICOqTrrbEaDKqbtSaGRpZ2VzdElEE2xlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJxaXNzdWluZ19hdXRob3JpdHnYGFhPpGZyYW5kb21QtyDyyKiExuZFhmsIS1M122hkaWdlc3RJRBRsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFGkZnJhbmRvbVAIbRM0JOd2WfpsMlmrMWMaaGRpZ2VzdElEFWxlbGVtZW50VmFsdWUYO3FlbGVtZW50SWRlbnRpZmllcmxhZ2VfaW5feWVhcnM' + +export const sprindFunkeX509TrustedCertificate = + 'MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf/7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2/J0WVeghyw+mIwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr+ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW/U/5S5vAEC5XxcOanusOBroBbU=' + +export const iso18013_5_IssuerAuthTestVector = + '8443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87e' + + 'b84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355' + + '040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a302131123010' + + '06035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082' + + 'a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d' + + '57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120' + + '417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b65' + + '78616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603' + + '551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff040403020780' + + '30150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d04030203480030450221009771' + + '7ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d' + + '43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f64696765' + + '7374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e313830' + + '31332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf015820' + + '67e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5' + + 'd869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e' + + '41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac' + + '2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae' + + '77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857d' + + 'd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7' + + '312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c' + + '98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30' + + 'faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485' + + '146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c' + + '5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24' + + 'c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98' + + 'c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d646576696365' + + '4b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dc' + + 'd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03' + + 'a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e66' + + '6fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc0743230' + + '32302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a' + + '33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832' + + '044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e'.replace(' ', '') + +export const iso18013_5_SignatureStructureTestVector = + '846a5369676e61747572653143a10126405903a2d81859039da66776657273696f6e63312e3' + + '06f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e697' + + '36f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254a' + + 'dcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372dd' + + 'b78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74' + + 'e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cc' + + 'a786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d06582' + + '07d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbee' + + 'ffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121' + + 'a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df' + + '6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd03678' + + '2f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087' + + 'b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83' + + 'd25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7' + + '000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d223310' + + '5511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6' + + '465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a' + + '33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228' + + '288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c69646' + + '97479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726' + + 'f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d3' + + '0315431333a33303a30325a'.replace(' ', '') diff --git a/packages/core/src/modules/mdoc/__tests__/mdoc.issuerAuth.test.ts b/packages/core/src/modules/mdoc/__tests__/mdoc.issuerAuth.test.ts new file mode 100644 index 0000000000..2b561f9291 --- /dev/null +++ b/packages/core/src/modules/mdoc/__tests__/mdoc.issuerAuth.test.ts @@ -0,0 +1,99 @@ +import { com } from '@sphereon/kmp-mdl-mdoc' + +import { Agent, TypedArrayEncoder } from '../../..' +import { getInMemoryAgentOptions } from '../../../../tests' +import { MdocCoseCallbackService } from '../MdocCoseCallbackService' + +import { + iso18013_5_IssuerAuthTestVector, + iso18013_5_SignatureStructureTestVector, + sprindFunkeTestVectorBase64Url, +} from './mdoc.fixtures' + +import CoseSign1Cbor = com.sphereon.cbor.cose.CoseSign1Cbor +import CoseSignatureAlgorithm = com.sphereon.cbor.cose.CoseSignatureAlgorithm +import Jwk = com.sphereon.jose.jwk.Jwk +import IssuerSignedCbor = com.sphereon.mdoc.data.device.IssuerSignedCbor + +const agent = new Agent(getInMemoryAgentOptions('mdoc-issuer-auth-test-agent', {})) + +describe('mdoc-issuerauth test', (): void => { + const coseCrypto = new MdocCoseCallbackService(agent.context) + + it('should decode and encode ISO Test Vector', async () => { + const coseSign = CoseSign1Cbor.Companion.cborDecode( + Int8Array.from(TypedArrayEncoder.fromHex(iso18013_5_IssuerAuthTestVector)) + ) + expect(coseSign).toBeDefined() + + expect(iso18013_5_IssuerAuthTestVector).toEqual(TypedArrayEncoder.toHex(Uint8Array.from(coseSign.cborEncode()))) + expect(iso18013_5_SignatureStructureTestVector).toEqual( + TypedArrayEncoder.toHex(Uint8Array.from(coseSign.toSignature1Structure().cborEncode())) + ) + expect(iso18013_5_SignatureStructureTestVector).toEqual( + coseSign.toBeSignedJson(null, CoseSignatureAlgorithm.ES256).hexValue + ) + }) + + it('jwk to coseKeyCbor transformation and back', () => { + const jwkJson = { + kty: 'EC', + kid: '11', + crv: 'P-256', + x: 'usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8', + y: 'IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4', + } + + const jwk = Jwk.Companion.fromJsonObject(jwkJson) + // TODO: + // expect(jwk.toJsonObject()).toEqual(jwkJson) + const coseKeyCbor = jwk.jwkToCoseKeyCbor() + expect(coseKeyCbor).toBeDefined() + }) + + it('should verify IETF Test Vector', async () => { + const ietfTestVector = + '8443a10126a10442313154546869732069732074686520636f6e74656e742e58408eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36' + const ietfSignature = + '8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36' + + const issuerAuth = CoseSign1Cbor.Companion.cborDecode( + Int8Array.from(TypedArrayEncoder.fromHex(ietfTestVector)) + ) as CoseSign1Cbor< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any + > + + expect(TypedArrayEncoder.toHex(Uint8Array.from(issuerAuth.signature.value))).toEqual(ietfSignature) + + const verificationResult = await coseCrypto.verify1(issuerAuth, { + key: Jwk.Companion.fromJsonObject({ + kty: 'EC', + kid: '11', + crv: 'P-256', + x: 'usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8', + y: 'IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4', + }).jwkToCoseKeyCbor(), + }) + + expect(verificationResult).toMatchObject({ + name: 'cose-verification', + message: 'cose-signature successfully validated', + critical: false, + error: false, + }) + }) + + it('should verify sprind funke test vector', async () => { + const issuerSigned = IssuerSignedCbor.Companion.cborDecode( + Int8Array.from(TypedArrayEncoder.fromBase64(sprindFunkeTestVectorBase64Url)) + ) + await expect(coseCrypto.verify1(issuerSigned.issuerAuth)).resolves.toMatchObject({ + critical: false, + error: false, + message: 'cose-signature successfully validated', + }) + }) +}) diff --git a/packages/core/src/modules/mdoc/__tests__/mdoc.service.test.ts b/packages/core/src/modules/mdoc/__tests__/mdoc.service.test.ts new file mode 100644 index 0000000000..bdb9fc51c6 --- /dev/null +++ b/packages/core/src/modules/mdoc/__tests__/mdoc.service.test.ts @@ -0,0 +1,39 @@ +import { Agent } from '../../..' +import { getInMemoryAgentOptions } from '../../../../tests' +import { Mdoc } from '../Mdoc' + +import { sprindFunkeX509TrustedCertificate, sprindFunkeTestVectorBase64Url } from './mdoc.fixtures' + +const agent = new Agent(getInMemoryAgentOptions('mdoc-test-agent', {})) + +describe('mdoc service test', () => { + test('can get issuer-auth protected-header alg', async () => { + const mdoc = Mdoc.fromIssuerSignedBase64(sprindFunkeTestVectorBase64Url) + expect(mdoc.jwaSignatureAlgorithm).toBe('ES256') + }) + + test('can decode claims from namespaces', async () => { + const mdoc = Mdoc.fromIssuerSignedBase64(sprindFunkeTestVectorBase64Url) + const namespaces = mdoc.namespaces + expect(Object.entries(namespaces)).toHaveLength(1) + + expect(namespaces).toBeDefined() + const eudiPidNamespace = namespaces['eu.europa.ec.eudi.pid.1'] + expect(eudiPidNamespace).toBeDefined() + // TODO: ADD checks once sphereno fixed the namespace structure + //expect(Object.keys(eudiPidNamespace)).toHaveLength(22) + //expect(eudiPidNamespace['family_name']).toEqual('MUSTERMANN') + }) + + test('can verify sprindFunkeTestVector Issuer Signed structure', async () => { + const mdoc = Mdoc.fromIssuerSignedBase64(sprindFunkeTestVectorBase64Url) + //const decoded = decode(TypedArrayEncoder.fromBase64(sprindFunkeTestVectorBase64Url)) + //const tryit = decoded.issuerAuth[2] + //const decodeAgain = decode(tryit) + //const decodeAgain2 = decode(decodeAgain.value) + const verify = await mdoc.verify(agent.context, { + trustedCertificates: [sprindFunkeX509TrustedCertificate], + }) + expect(verify.isValid).toBeTruthy() + }) +}) diff --git a/packages/core/src/modules/mdoc/index.ts b/packages/core/src/modules/mdoc/index.ts new file mode 100644 index 0000000000..411f310ed5 --- /dev/null +++ b/packages/core/src/modules/mdoc/index.ts @@ -0,0 +1,7 @@ +export * from './MdocApi' +export * from './MdocModule' +export * from './MdocService' +export * from './MdocError' +export * from './MdocOptions' +export * from './repository' +export * from './Mdoc' diff --git a/packages/core/src/modules/mdoc/repository/MdocRecord.ts b/packages/core/src/modules/mdoc/repository/MdocRecord.ts new file mode 100644 index 0000000000..875afeab7e --- /dev/null +++ b/packages/core/src/modules/mdoc/repository/MdocRecord.ts @@ -0,0 +1,58 @@ +import type { TagsBase } from '../../../storage/BaseRecord' +import type { Constructable } from '../../../utils/mixins' + +import { type JwaSignatureAlgorithm } from '../../../crypto' +import { BaseRecord } from '../../../storage/BaseRecord' +import { JsonTransformer } from '../../../utils' +import { uuid } from '../../../utils/uuid' +import { Mdoc } from '../Mdoc' + +export type DefaultMdocRecordTags = { + docType: string + + /** + * + * The alg is the alg used to sign the Mdoc + */ + alg: JwaSignatureAlgorithm +} + +export type MdocRecordStorageProps = { + id?: string + createdAt?: Date + tags?: TagsBase + mdoc: Mdoc +} + +export class MdocRecord extends BaseRecord { + public static readonly type = 'MdocRecord' + public readonly type = MdocRecord.type + public issuerSignedHex!: string + + public constructor(props: MdocRecordStorageProps) { + super() + + if (props) { + this.id = props.id ?? uuid() + this.createdAt = props.createdAt ?? new Date() + this.issuerSignedHex = props.mdoc.issuerSignedHex + this._tags = props.tags ?? {} + } + } + + public getTags() { + const mdoc = Mdoc.fromIssuerSignedHex(this.issuerSignedHex) + const alg = mdoc.jwaSignatureAlgorithm + const docType = mdoc.docType + + return { + ...this._tags, + docType, + alg, + } + } + + public clone(): this { + return JsonTransformer.fromJSON(JsonTransformer.toJSON(this), this.constructor as Constructable) + } +} diff --git a/packages/core/src/modules/mdoc/repository/MdocRepository.ts b/packages/core/src/modules/mdoc/repository/MdocRepository.ts new file mode 100644 index 0000000000..885ece6db0 --- /dev/null +++ b/packages/core/src/modules/mdoc/repository/MdocRepository.ts @@ -0,0 +1,17 @@ +import { EventEmitter } from '../../../agent/EventEmitter' +import { InjectionSymbols } from '../../../constants' +import { inject, injectable } from '../../../plugins' +import { Repository } from '../../../storage/Repository' +import { StorageService } from '../../../storage/StorageService' + +import { MdocRecord } from './MdocRecord' + +@injectable() +export class MdocRepository extends Repository { + public constructor( + @inject(InjectionSymbols.StorageService) storageService: StorageService, + eventEmitter: EventEmitter + ) { + super(MdocRecord, storageService, eventEmitter) + } +} diff --git a/packages/core/src/modules/mdoc/repository/index.ts b/packages/core/src/modules/mdoc/repository/index.ts new file mode 100644 index 0000000000..f211d1f7ae --- /dev/null +++ b/packages/core/src/modules/mdoc/repository/index.ts @@ -0,0 +1,2 @@ +export * from './MdocRecord' +export * from './MdocRepository' diff --git a/packages/openid4vc/package.json b/packages/openid4vc/package.json index fc72ee6d16..239164f06f 100644 --- a/packages/openid4vc/package.json +++ b/packages/openid4vc/package.json @@ -27,11 +27,11 @@ }, "dependencies": { "@credo-ts/core": "workspace:*", - "@sphereon/did-auth-siop": "0.16.1-next.3", - "@sphereon/oid4vc-common": "0.16.1-next.3", - "@sphereon/oid4vci-client": "0.16.1-next.3", - "@sphereon/oid4vci-common": "0.16.1-next.3", - "@sphereon/oid4vci-issuer": "0.16.1-next.3", + "@sphereon/did-auth-siop": "0.16.1-next.8", + "@sphereon/oid4vc-common": "0.16.1-next.8", + "@sphereon/oid4vci-client": "0.16.1-next.8", + "@sphereon/oid4vci-common": "0.16.1-next.8", + "@sphereon/oid4vci-issuer": "0.16.1-next.8", "@sphereon/ssi-types": "0.28.0", "class-transformer": "^0.5.1", "rxjs": "^7.8.0" diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts index ef0f3041c6..45aab99c9e 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts @@ -22,6 +22,7 @@ import type { AuthorizationDetails, AuthorizationDetailsJwtVcJson, AuthorizationDetailsJwtVcJsonLdAndLdpVc, + AuthorizationDetailsMsoMdoc, AuthorizationDetailsSdJwtVc, CredentialResponse, Jwt, @@ -51,6 +52,8 @@ import { inject, injectable, parseDid, + MdocService, + Mdoc, } from '@credo-ts/core' import { CreateDPoPClientOpts, CreateDPoPJwtPayloadProps, SigningAlgo } from '@sphereon/oid4vc-common' import { @@ -81,14 +84,17 @@ import { OpenId4VciNotificationMetadata, openId4VciSupportedCredentialFormats } export class OpenId4VciHolderService { private logger: Logger private w3cCredentialService: W3cCredentialService + private mdocService: MdocService private jwsService: JwsService public constructor( @inject(InjectionSymbols.Logger) logger: Logger, w3cCredentialService: W3cCredentialService, + mdocService: MdocService, jwsService: JwsService ) { this.w3cCredentialService = w3cCredentialService + this.mdocService = mdocService this.jwsService = jwsService this.logger = logger } @@ -152,7 +158,7 @@ export class OpenId4VciHolderService { private getAuthDetailsFromOfferedCredential( offeredCredential: OpenId4VciCredentialSupported, authDetailsLocation: string | undefined - ): AuthorizationDetails | undefined { + ): AuthorizationDetails { const { format } = offeredCredential const type = 'openid_credential' @@ -178,6 +184,14 @@ export class OpenId4VciHolderService { vct: offeredCredential.vct, claims: offeredCredential.claims, } satisfies AuthorizationDetailsSdJwtVc + } else if (format === OpenId4VciCredentialFormatProfile.MsoMdoc) { + return { + type, + format, + locations, + claims: offeredCredential.claims, + doctype: offeredCredential.doctype, + } satisfies AuthorizationDetailsMsoMdoc } else { throw new CredoError(`Cannot create authorization_details. Unsupported credential format '${format}'.`) } @@ -654,6 +668,7 @@ export class OpenId4VciHolderService { switch (credentialToRequest.configuration.format) { case OpenId4VciCredentialFormatProfile.JwtVcJson: case OpenId4VciCredentialFormatProfile.JwtVcJsonLd: + case OpenId4VciCredentialFormatProfile.MsoMdoc: case OpenId4VciCredentialFormatProfile.SdJwtVc: signatureAlgorithm = options.possibleProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => proofSigningAlgsSupported.includes(signatureAlgorithm) @@ -772,6 +787,19 @@ export class OpenId4VciHolderService { } return { credential, notificationMetadata } + } else if (format === OpenId4VciCredentialFormatProfile.MsoMdoc) { + if (typeof credentialResponse.successBody.credential !== 'string') { + throw new CredoError('Received Mdoc credential in an unsupported format.') + } + const mdoc = Mdoc.fromIssuerSignedBase64(credentialResponse.successBody.credential) + const result = await this.mdocService.verify(agentContext, { mdoc }) + if (!result.isValid) { + agentContext.config.logger.error('Failed to validate credential', { result }) + const errorMessage = typeof result.error ?? 'Unknown' + throw new CredoError(`Failed to validate credential, error = ${errorMessage}`) + } + + return { credential: mdoc, notificationMetadata } } throw new CredoError(`Unsupported credential format ${credentialResponse.successBody.format}`) diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts index 0bd7ad0e8d..bc75187add 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts @@ -21,12 +21,14 @@ export type OpenId4VciSupportedCredentialFormats = | OpenId4VciCredentialFormatProfile.JwtVcJsonLd | OpenId4VciCredentialFormatProfile.SdJwtVc | OpenId4VciCredentialFormatProfile.LdpVc + | OpenId4VciCredentialFormatProfile.MsoMdoc export const openId4VciSupportedCredentialFormats: OpenId4VciSupportedCredentialFormats[] = [ OpenId4VciCredentialFormatProfile.JwtVcJson, OpenId4VciCredentialFormatProfile.JwtVcJsonLd, OpenId4VciCredentialFormatProfile.SdJwtVc, OpenId4VciCredentialFormatProfile.LdpVc, + OpenId4VciCredentialFormatProfile.MsoMdoc, ] export interface OpenId4VciNotificationMetadata { diff --git a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts index b26b35952a..1a8f05d511 100644 --- a/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts +++ b/packages/openid4vc/src/openid4vc-issuer/OpenId4VcIssuerService.ts @@ -704,7 +704,7 @@ export class OpenId4VcIssuerService { signCallback: this.getSdJwtVcCredentialSigningCallback(agentContext, signOptions), } } else { - throw new CredoError(`Unsupported credential format`) + throw new CredoError(`Unsupported credential format ${signOptions.format}`) } } } diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index bdc182cc04..9b6f1d66c6 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -449,6 +449,9 @@ export class OpenId4VcSiopVerifierService { responseTypesSupported: [ResponseType.VP_TOKEN], subject_syntax_types_supported: supportedDidMethods.map((m) => `did:${m}`), vpFormatsSupported: { + mso_mdoc: { + alg: supportedAlgs, + }, jwt_vc: { alg: supportedAlgs, }, diff --git a/packages/openid4vc/src/shared/issuerMetadataUtils.ts b/packages/openid4vc/src/shared/issuerMetadataUtils.ts index 8b7cfc08c2..9396dd0fdd 100644 --- a/packages/openid4vc/src/shared/issuerMetadataUtils.ts +++ b/packages/openid4vc/src/shared/issuerMetadataUtils.ts @@ -5,7 +5,7 @@ import type { OpenId4VciCredentialSupportedWithId, } from './models' import type { AgentContext, JwaSignatureAlgorithm } from '@credo-ts/core' -import type { CredentialOfferFormat } from '@sphereon/oid4vci-common' +import type { CredentialOfferFormatV1_0_11 } from '@sphereon/oid4vci-common' import { CredoError } from '@credo-ts/core' @@ -39,6 +39,8 @@ export function getTypesFromCredentialSupported( ) } return credentialSupported.vct ? [credentialSupported.vct] : undefined + } else if (credentialSupported.format === 'mso_mdoc') { + return [credentialSupported.doctype] } throw Error(`Unable to extract types from credentials supported. Unknown format ${credentialSupported.format}`) @@ -57,7 +59,9 @@ export function credentialConfigurationSupportedToCredentialSupported( order: config.order, } - if (config.format === 'jwt_vc_json' || config.format === 'jwt_vc') { + if (config.format === 'mso_mdoc') { + return { ...baseConfig, format: 'mso_mdoc', doctype: config.doctype, claims: config.claims } + } else if (config.format === 'jwt_vc_json' || config.format === 'jwt_vc') { return { ...baseConfig, format: config.format, @@ -125,7 +129,14 @@ export function credentialSupportedToCredentialConfigurationSupported( order: credentialSupported.order, } - if (credentialSupported.format === 'jwt_vc_json' || credentialSupported.format === 'jwt_vc') { + if (credentialSupported.format === 'mso_mdoc') { + return { + ...baseCredentialConfigurationSupported, + doctype: credentialSupported.doctype, + format: credentialSupported.format, + claims: credentialSupported.claims, + } + } else if (credentialSupported.format === 'jwt_vc_json' || credentialSupported.format === 'jwt_vc') { return { ...baseCredentialConfigurationSupported, format: credentialSupported.format, @@ -190,7 +201,7 @@ export function credentialsSupportedV11ToV13( */ export function getOfferedCredentials( agentContext: AgentContext, - offeredCredentials: Array, + offeredCredentials: Array, credentialsSupportedOrConfigurations: OpenId4VciCredentialConfigurationsSupported | OpenId4VciCredentialSupported[] ): { credentialsSupported: OpenId4VciCredentialSupportedWithId[] diff --git a/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts b/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts index 628e65c12e..4d74512986 100644 --- a/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts +++ b/packages/openid4vc/src/shared/models/OpenId4VciCredentialFormatProfile.ts @@ -3,4 +3,5 @@ export enum OpenId4VciCredentialFormatProfile { JwtVcJsonLd = 'jwt_vc_json-ld', LdpVc = 'ldp_vc', SdJwtVc = 'vc+sd-jwt', + MsoMdoc = 'mso_mdoc', } diff --git a/packages/openid4vc/src/shared/transform.ts b/packages/openid4vc/src/shared/transform.ts index bf2cebf80e..daf42c3d3c 100644 --- a/packages/openid4vc/src/shared/transform.ts +++ b/packages/openid4vc/src/shared/transform.ts @@ -14,6 +14,7 @@ import { W3cJwtVerifiableCredential, W3cJsonLdVerifiableCredential, JsonEncoder, + Mdoc, } from '@credo-ts/core' export function getSphereonVerifiableCredential( @@ -26,6 +27,8 @@ export function getSphereonVerifiableCredential( return JsonTransformer.toJSON(verifiableCredential) as SphereonW3cVerifiableCredential } else if (verifiableCredential instanceof W3cJwtVerifiableCredential) { return verifiableCredential.serializedJwt + } else if (verifiableCredential instanceof Mdoc) { + throw new CredoError('Issuance side for Mdoc not yet implemented.') } else { return verifiableCredential.compact } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7a575c934..a660f57758 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -447,6 +447,9 @@ importers: '@sd-jwt/utils': specifier: ^0.7.0 version: 0.7.1 + '@sphereon/kmp-mdl-mdoc': + specifier: 0.1.0-SNAPSHOT.21 + version: 0.1.0-SNAPSHOT.21 '@sphereon/pex': specifier: ^3.3.2 version: 3.3.3 @@ -490,8 +493,8 @@ importers: specifier: ^0.4.1 version: 0.4.1 luxon: - specifier: ^3.3.0 - version: 3.4.4 + specifier: ^3.5.0 + version: 3.5.0 make-error: specifier: ^1.3.6 version: 1.3.6 @@ -684,20 +687,20 @@ importers: specifier: workspace:* version: link:../core '@sphereon/did-auth-siop': - specifier: 0.16.1-next.3 - version: 0.16.1-next.3 + specifier: 0.16.1-next.8 + version: 0.16.1-next.8 '@sphereon/oid4vc-common': - specifier: 0.16.1-next.3 - version: 0.16.1-next.3 + specifier: 0.16.1-next.8 + version: 0.16.1-next.8 '@sphereon/oid4vci-client': - specifier: 0.16.1-next.3 - version: 0.16.1-next.3 + specifier: 0.16.1-next.8 + version: 0.16.1-next.8 '@sphereon/oid4vci-common': - specifier: 0.16.1-next.3 - version: 0.16.1-next.3 + specifier: 0.16.1-next.8 + version: 0.16.1-next.8 '@sphereon/oid4vci-issuer': - specifier: 0.16.1-next.3 - version: 0.16.1-next.3 + specifier: 0.16.1-next.8 + version: 0.16.1-next.8 '@sphereon/ssi-types': specifier: 0.28.0 version: 0.28.0 @@ -2112,6 +2115,14 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@js-joda/core@5.6.3': + resolution: {integrity: sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA==} + + '@js-joda/timezone@2.3.0': + resolution: {integrity: sha512-DHXdNs0SydSqC5f0oRJPpTcNfnpRojgBqMCFupQFv6WgeZAjU3DBx+A7JtaGPP3dHrP2Odi2N8Vf+uAm/8ynCQ==} + peerDependencies: + '@js-joda/core': '>=1.11.0' + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -2383,27 +2394,30 @@ packages: resolution: {integrity: sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw==} engines: {node: '>= 8'} - '@sphereon/did-auth-siop@0.16.1-next.3': - resolution: {integrity: sha512-PjE1n5oUPpLU7KCZ3pK1OOo+/DL+ASsJew5I2awt0HT7+mOEbXPhIWmnKjIcqPnXrw8O59m8uYi5MUrtASrIpg==} + '@sphereon/did-auth-siop@0.16.1-next.8': + resolution: {integrity: sha512-chEn2ZoiOgtTw3rEh9M1Bhns5h3dzTQIFjTserQWhR4migO10KcTgnnAYJL5/VjE65nDPZZFzqb7SLAL//fyzg==} engines: {node: '>=18'} '@sphereon/did-uni-client@0.6.3': resolution: {integrity: sha512-g7LD7ofbE36slHN7Bhr5dwUrj6t0BuZeXBYJMaVY/pOeL1vJxW1cZHbZqu0NSfOmzyBg4nsYVlgTjyi/Aua2ew==} - '@sphereon/oid4vc-common@0.16.1-next.3': - resolution: {integrity: sha512-0bdCLsUtqzmn/Zks0RZNuO8H3s5Zc71x/o1WDXE4263REvO8zuzI4N6viNQZdM8vUxR99GnADQCxWCY2ZoVO8g==} + '@sphereon/kmp-mdl-mdoc@0.1.0-SNAPSHOT.21': + resolution: {integrity: sha512-UcgIZfnU10UNSaETrM+u6C9NHfWZyUba+mD+pDDdq2kbTD0LkYmjUBkcwABexMC6L/4avW/2spYss4OkmRbinA==} + + '@sphereon/oid4vc-common@0.16.1-next.8': + resolution: {integrity: sha512-e/DPY0zbVrCSDN6sPQApkNHncgMn2hJBbiSnkFgkJFXvBpob3RgnUn/CebO/VzClGT6ua0EOkSs4jMe6MhRimw==} engines: {node: '>=18'} - '@sphereon/oid4vci-client@0.16.1-next.3': - resolution: {integrity: sha512-b5xtDlQcEsg0W0In1lywUcvf3jJy/NhfvknbtYjRVtObhA2K/pRsb1yS+p54Vfc02enA+A/a2lVb6zI2peyF3w==} + '@sphereon/oid4vci-client@0.16.1-next.8': + resolution: {integrity: sha512-9cqlA3mLwhjS1ucTPEyXi/NmVcJk90z3Ock21gwQeSdGMzqAD2U6nUz6dbzOyaMyrLtQLtiQalYXY8BeTq16ug==} engines: {node: '>=18'} - '@sphereon/oid4vci-common@0.16.1-next.3': - resolution: {integrity: sha512-+0Cm/qWgQ2efs+vSFwf37Zji1k+oSs3pI7yuN0dGQF7iYG3whq+iNRfIKR5M9rYg8H4bITg0PSTrcFXz7VuWww==} + '@sphereon/oid4vci-common@0.16.1-next.8': + resolution: {integrity: sha512-yQF22jfFHxHqGBYg+vngeaXWSj8wuky9OK/T/obwQDUUS8pRav+RNRsB0ydX0ZtNskReORWk4A/QpVv3RBkgqQ==} engines: {node: '>=18'} - '@sphereon/oid4vci-issuer@0.16.1-next.3': - resolution: {integrity: sha512-92D+dEqvEkyI6XKkjbZXSmZlqLNdl3vAd8BNDWM1+E2X337lFnT9cE0pF432YdOzvS/ZwdlmftGjq2ehi1nOgg==} + '@sphereon/oid4vci-issuer@0.16.1-next.8': + resolution: {integrity: sha512-cFHK62lLqx3ozJn0MfFy7jeU9ICCQb/pJBUEDcmYcdpDJ7LKHL9ism0ib3ytVlmc656oXwoXk109DaqOi199/A==} engines: {node: '>=18'} peerDependencies: awesome-qr: ^2.1.5-rc.0 @@ -2424,6 +2438,9 @@ packages: '@sphereon/ssi-types@0.28.0': resolution: {integrity: sha512-NkTkrsBoQUZzJutlk5XD3snBxL9kfsxKdQvBbGUEaUDOiW8siTNUoJuQFeA+bI0eJY99up95bmMKdJeDc1VDfg==} + '@sphereon/ssi-types@0.29.0': + resolution: {integrity: sha512-fq4rx5UBRlfCRTqY0jKu4Fr5PKwWi20/FDVeceQrnk9QMnAnDPh4XhasVx5A1RYgCWrxdcgQiVsykUH7Uy2gVA==} + '@sphereon/ssi-types@0.9.0': resolution: {integrity: sha512-umCr/syNcmvMMbQ+i/r/mwjI1Qw2aFPp9AwBTvTo1ailAVaaJjJGPkkVz1K9/2NZATNdDiQ3A8yGzdVJoKh9pA==} @@ -4145,6 +4162,9 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + format-util@1.0.5: + resolution: {integrity: sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -5158,8 +5178,8 @@ packages: lru_map@0.4.1: resolution: {integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==} - luxon@3.4.4: - resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + luxon@3.5.0: + resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} engines: {node: '>=12'} make-dir@2.1.0: @@ -9206,6 +9226,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@js-joda/core@5.6.3': {} + + '@js-joda/timezone@2.3.0(@js-joda/core@5.6.3)': + dependencies: + '@js-joda/core': 5.6.3 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.8 @@ -9770,11 +9796,11 @@ snapshots: '@sovpro/delimited-stream@1.1.0': {} - '@sphereon/did-auth-siop@0.16.1-next.3': + '@sphereon/did-auth-siop@0.16.1-next.8': dependencies: '@astronautlabs/jsonpath': 1.1.2 '@sphereon/did-uni-client': 0.6.3 - '@sphereon/oid4vc-common': 0.16.1-next.3 + '@sphereon/oid4vc-common': 0.16.1-next.8 '@sphereon/pex': 3.3.3 '@sphereon/pex-models': 2.2.4 '@sphereon/ssi-types': 0.22.0 @@ -9797,9 +9823,15 @@ snapshots: transitivePeerDependencies: - encoding - '@sphereon/oid4vc-common@0.16.1-next.3': + '@sphereon/kmp-mdl-mdoc@0.1.0-SNAPSHOT.21': dependencies: - '@sphereon/ssi-types': 0.28.0 + '@js-joda/core': 5.6.3 + '@js-joda/timezone': 2.3.0(@js-joda/core@5.6.3) + format-util: 1.0.5 + + '@sphereon/oid4vc-common@0.16.1-next.8': + dependencies: + '@sphereon/ssi-types': 0.29.0 jwt-decode: 4.0.0 sha.js: 2.4.11 uint8arrays: 3.1.1 @@ -9807,21 +9839,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@sphereon/oid4vci-client@0.16.1-next.3': + '@sphereon/oid4vci-client@0.16.1-next.8': dependencies: - '@sphereon/oid4vc-common': 0.16.1-next.3 - '@sphereon/oid4vci-common': 0.16.1-next.3 - '@sphereon/ssi-types': 0.28.0 + '@sphereon/oid4vc-common': 0.16.1-next.8 + '@sphereon/oid4vci-common': 0.16.1-next.8 + '@sphereon/ssi-types': 0.29.0 cross-fetch: 3.1.8 debug: 4.3.5 transitivePeerDependencies: - encoding - supports-color - '@sphereon/oid4vci-common@0.16.1-next.3': + '@sphereon/oid4vci-common@0.16.1-next.8': dependencies: - '@sphereon/oid4vc-common': 0.16.1-next.3 - '@sphereon/ssi-types': 0.28.0 + '@sphereon/oid4vc-common': 0.16.1-next.8 + '@sphereon/ssi-types': 0.29.0 cross-fetch: 3.1.8 jwt-decode: 4.0.0 uint8arrays: 3.1.1 @@ -9830,11 +9862,11 @@ snapshots: - encoding - supports-color - '@sphereon/oid4vci-issuer@0.16.1-next.3': + '@sphereon/oid4vci-issuer@0.16.1-next.8': dependencies: - '@sphereon/oid4vc-common': 0.16.1-next.3 - '@sphereon/oid4vci-common': 0.16.1-next.3 - '@sphereon/ssi-types': 0.28.0 + '@sphereon/oid4vc-common': 0.16.1-next.8 + '@sphereon/oid4vci-common': 0.16.1-next.8 + '@sphereon/ssi-types': 0.29.0 uuid: 9.0.1 transitivePeerDependencies: - encoding @@ -9871,6 +9903,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@sphereon/ssi-types@0.29.0': + dependencies: + '@sd-jwt/decode': 0.6.1 + debug: 4.3.5 + events: 3.3.0 + jwt-decode: 3.1.2 + transitivePeerDependencies: + - supports-color + '@sphereon/ssi-types@0.9.0': dependencies: jwt-decode: 3.1.2 @@ -11985,6 +12026,8 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + format-util@1.0.5: {} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -13231,7 +13274,7 @@ snapshots: lru_map@0.4.1: {} - luxon@3.4.4: {} + luxon@3.5.0: {} make-dir@2.1.0: dependencies: