From 6642ea0879cc931efcfd09fbde38a76d94fe47d6 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sun, 24 Nov 2024 13:01:56 +0100 Subject: [PATCH] fix: address pex fixes (#2104) Signed-off-by: Timo Glastra --- packages/askar/package.json | 4 +- packages/askar/src/wallet/AskarBaseWallet.ts | 2 +- packages/core/package.json | 5 +- .../DifPresentationExchangeService.ts | 6 +- .../DifPresentationExchangeService.test.ts | 374 ++++++++++++++++++ .../dif-presentation-exchange/models/index.ts | 2 +- .../utils/credentialSelection.ts | 269 +++++++------ .../utils/presentationSelection.ts | 6 +- .../utils/transform.ts | 6 +- ...entationExchangeProofFormatService.test.ts | 2 +- pnpm-lock.yaml | 97 ++--- 11 files changed, 557 insertions(+), 216 deletions(-) create mode 100644 packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts diff --git a/packages/askar/package.json b/packages/askar/package.json index dbadea5da1..48730d5638 100644 --- a/packages/askar/package.json +++ b/packages/askar/package.json @@ -34,7 +34,7 @@ "tsyringe": "^4.8.0" }, "devDependencies": { - "@animo-id/expo-secure-environment": "^0.0.1-alpha.0", + "@animo-id/expo-secure-environment": "^0.1.0-alpha.11", "@hyperledger/aries-askar-nodejs": "^0.2.3", "@hyperledger/aries-askar-shared": "^0.2.3", "@types/bn.js": "^5.1.0", @@ -46,7 +46,7 @@ }, "peerDependencies": { "@hyperledger/aries-askar-shared": "^0.2.3", - "@animo-id/expo-secure-environment": "^0.0.1-alpha.0" + "@animo-id/expo-secure-environment": "^0.1.0-alpha.11" }, "peerDependenciesMeta": { "@animo-id/expo-secure-environment": { diff --git a/packages/askar/src/wallet/AskarBaseWallet.ts b/packages/askar/src/wallet/AskarBaseWallet.ts index 03c63d8e4c..731fdf1ecc 100644 --- a/packages/askar/src/wallet/AskarBaseWallet.ts +++ b/packages/askar/src/wallet/AskarBaseWallet.ts @@ -206,7 +206,7 @@ export abstract class AskarBaseWallet implements Wallet { // Generate a hardware-backed P-256 keypair secureEnvironment.generateKeypair(kid) - const publicKeyBytes = secureEnvironment.getPublicBytesForKeyId(kid) + const publicKeyBytes = await secureEnvironment.getPublicBytesForKeyId(kid) const publicKeyBase58 = TypedArrayEncoder.toBase58(publicKeyBytes) await this.storeSecureEnvironmentKeyById({ diff --git a/packages/core/package.json b/packages/core/package.json index 12e3fed83d..b4f8e333b3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -42,7 +42,7 @@ "@sd-jwt/sd-jwt-vc": "^0.7.0", "@sd-jwt/types": "^0.7.0", "@sd-jwt/utils": "^0.7.0", - "@sphereon/pex": "5.0.0-unstable.25", + "@animo-id/pex": "4.1.1-alpha.0", "@sphereon/pex-models": "^2.3.1", "@sphereon/ssi-types": "0.30.2-next.135", "@stablelib/ed25519": "^1.0.2", @@ -54,7 +54,7 @@ "class-transformer": "0.5.1", "class-validator": "0.14.1", "did-resolver": "^4.1.0", - "jsonpath": "^1.1.1", + "@astronautlabs/jsonpath": "^1.1.2", "lru_map": "^0.4.1", "luxon": "^3.5.0", "make-error": "^1.3.6", @@ -70,7 +70,6 @@ }, "devDependencies": { "@types/events": "^3.0.0", - "@types/jsonpath": "^0.2.4", "@types/luxon": "^3.2.0", "@types/object-inspect": "^1.8.0", "@types/uuid": "^9.0.1", diff --git a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts index 69a6aade89..05be0137d0 100644 --- a/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts +++ b/packages/core/src/modules/dif-presentation-exchange/DifPresentationExchangeService.ts @@ -14,7 +14,7 @@ import type { VerificationMethod } from '../dids' import type { SdJwtVcRecord } from '../sd-jwt-vc' import type { W3cCredentialRecord } from '../vc' import type { IAnonCredsDataIntegrityService } from '../vc/data-integrity/models/IAnonCredsDataIntegrityService' -import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@sphereon/pex' +import type { PresentationSignCallBackParams, Validated, VerifiablePresentationResult } from '@animo-id/pex' import type { InputDescriptorV2 } from '@sphereon/pex-models' import type { SdJwtDecodedVerifiableCredential, @@ -22,8 +22,8 @@ import type { W3CVerifiablePresentation, } from '@sphereon/ssi-types' -import { PEVersion, PEX, PresentationSubmissionLocation, Status } from '@sphereon/pex' -import { PartialSdJwtDecodedVerifiableCredential } from '@sphereon/pex/dist/main/lib' +import { PEVersion, PEX, PresentationSubmissionLocation, Status } from '@animo-id/pex' +import { PartialSdJwtDecodedVerifiableCredential } from '@animo-id/pex/dist/main/lib' import { injectable } from 'tsyringe' import { Hasher, getJwkFromKey } from '../../crypto' diff --git a/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts b/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts new file mode 100644 index 0000000000..ad77f6af14 --- /dev/null +++ b/packages/core/src/modules/dif-presentation-exchange/__tests__/DifPresentationExchangeService.test.ts @@ -0,0 +1,374 @@ +import type { DifPresentationExchangeDefinitionV2 } from '../models' + +import { Subject } from 'rxjs' + +import { InMemoryStorageService } from '../../../../../../tests/InMemoryStorageService' +import { InMemoryWallet } from '../../../../../../tests/InMemoryWallet' +import { agentDependencies, getAgentContext } from '../../../../tests' +import { AgentContext } from '../../../agent' +import { InjectionSymbols } from '../../../constants' +import { Mdoc, MdocRecord, MdocRepository } from '../../mdoc' +import { sprindFunkeTestVectorBase64Url } from '../../mdoc/__tests__/mdoc.fixtures' +import { SdJwtVcRecord, SdJwtVcRepository } from '../../sd-jwt-vc' +import { SignatureSuiteToken, W3cCredentialService, W3cCredentialsModuleConfig } from '../../vc' +import { DifPresentationExchangeService } from '../DifPresentationExchangeService' + +const agentContext = getAgentContext({ + registerInstances: [ + [InjectionSymbols.StorageService, new InMemoryStorageService()], + [InjectionSymbols.AgentDependencies, agentDependencies], + [InjectionSymbols.Stop$, new Subject()], + [SignatureSuiteToken, 'default'], + [W3cCredentialsModuleConfig, new W3cCredentialsModuleConfig()], + ], + wallet: new InMemoryWallet(), +}) +agentContext.dependencyManager.registerInstance(AgentContext, agentContext) +const sdJwtVcRecord = new SdJwtVcRecord({ + compactSdJwtVc: + 'eyJ4NWMiOlsiTUlJQ2REQ0NBaHVnQXdJQkFnSUJBakFLQmdncWhrak9QUVFEQWpDQmlERUxNQWtHQTFVRUJoTUNSRVV4RHpBTkJnTlZCQWNNQmtKbGNteHBiakVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneEVUQVBCZ05WQkFzTUNGUWdRMU1nU1VSRk1UWXdOQVlEVlFRRERDMVRVRkpKVGtRZ1JuVnVhMlVnUlZWRVNTQlhZV3hzWlhRZ1VISnZkRzkwZVhCbElFbHpjM1ZwYm1jZ1EwRXdIaGNOTWpRd05UTXhNRGd4TXpFM1doY05NalV3TnpBMU1EZ3hNekUzV2pCc01Rc3dDUVlEVlFRR0V3SkVSVEVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneENqQUlCZ05WQkFzTUFVa3hNakF3QmdOVkJBTU1LVk5RVWtsT1JDQkdkVzVyWlNCRlZVUkpJRmRoYkd4bGRDQlFjbTkwYjNSNWNHVWdTWE56ZFdWeU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRU9GQnE0WU1LZzR3NWZUaWZzeXR3QnVKZi83RTdWaFJQWGlObTUyUzNxMUVUSWdCZFh5REsza1Z4R3hnZUhQaXZMUDN1dU12UzZpREVjN3FNeG12ZHVLT0JrRENCalRBZEJnTlZIUTRFRmdRVWlQaENrTEVyRFhQTFcyL0owV1ZlZ2h5dyttSXdEQVlEVlIwVEFRSC9CQUl3QURBT0JnTlZIUThCQWY4RUJBTUNCNEF3TFFZRFZSMFJCQ1l3SklJaVpHVnRieTV3YVdRdGFYTnpkV1Z5TG1KMWJtUmxjMlJ5ZFdOclpYSmxhUzVrWlRBZkJnTlZIU01FR0RBV2dCVFVWaGpBaVRqb0RsaUVHTWwyWXIrcnU4V1F2akFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUFiZjVUemtjUXpoZldvSW95aTFWTjdkOEk5QnNGS20xTVdsdVJwaDJieUdRSWdLWWtkck5mMnhYUGpWU2JqVy9VLzVTNXZBRUM1WHhjT2FudXNPQnJvQmJVPSIsIk1JSUNlVENDQWlDZ0F3SUJBZ0lVQjVFOVFWWnRtVVljRHRDaktCL0gzVlF2NzJnd0NnWUlLb1pJemowRUF3SXdnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUI0WERUSTBNRFV6TVRBMk5EZ3dPVm9YRFRNME1EVXlPVEEyTkRnd09Wb3dnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWUd6ZHdGRG5jNytLbjVpYkF2Q09NOGtlNzdWUXhxZk1jd1pMOElhSUErV0NST2NDZm1ZL2dpSDkycU1ydTVwL2t5T2l2RTBSQy9JYmRNT052RG9VeWFObU1HUXdIUVlEVlIwT0JCWUVGTlJXR01DSk9PZ09XSVFZeVhaaXY2dTd4WkMrTUI4R0ExVWRJd1FZTUJhQUZOUldHTUNKT09nT1dJUVl5WFppdjZ1N3haQytNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnR0dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR0VtN3drWktIdC9hdGI0TWRGblhXNnlybndNVVQydTEzNmdkdGwxMFk2aEFpQnVURnF2Vll0aDFyYnh6Q1AweFdaSG1RSzlrVnl4bjhHUGZYMjdFSXp6c3c9PSJdLCJraWQiOiJNSUdVTUlHT3BJR0xNSUdJTVFzd0NRWURWUVFHRXdKRVJURVBNQTBHQTFVRUJ3d0dRbVZ5YkdsdU1SMHdHd1lEVlFRS0RCUkNkVzVrWlhOa2NuVmphMlZ5WldrZ1IyMWlTREVSTUE4R0ExVUVDd3dJVkNCRFV5QkpSRVV4TmpBMEJnTlZCQU1NTFZOUVVrbE9SQ0JHZFc1clpTQkZWVVJKSUZkaGJHeGxkQ0JRY205MGIzUjVjR1VnU1hOemRXbHVaeUJEUVFJQkFnPT0iLCJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiVS01ZlVXLU5EM1laajZTcUdyQXV4NXJWYWZOalhqZ2hvMmRUUmpQX3hOTSJdfSwiX3NkIjpbIjlFaUpQNEw2NDI0bEtTVGs5NHpIOWhaWVc5UjNuS1R3V0V5TVBJN2dvWHciLCJHVlhRWEtFMmpWR1d0VEF6T1d5ck85TTZySW1qYkZJWGFnRkMyWElMbGhJIiwiUUV2bHpNd0ozZS1tOEtpWEk5bGx2bnVQblh5UHRXN2VCSF9GcXFVTnk3WSIsImljWkpTRkFqLVg3T29Sam5vRFRReXFwU1dNQUVuaTcydWZDZmFFWC1uQkUiLCJsUHJqb3BqbEN5bFdHWVo0cmh4S1RUTUsxS3p1Sm5ISUtybzNwUUhlUXF3IiwicjJORHZtRFY3QmU3TlptVFR0VE9fekdZX3RTdWdYVXoxeDJBXzZuOFhvdyIsInJPbjFJUkpUQWtEV1pSTGc3MUYzaDVsbFpPc1ZPMl9aemlOUy1majNEUFUiXSwiYWRkcmVzcyI6eyJfc2QiOlsiQnI1aVZtZnZlaTloQ01mMktVOGRFVjFER2hrdUtsQ1pUeGFEQ0FMb3NJbyIsIkx6czJpR09SNHF0clhhYmdwMzFfcjFFUFNmazlaUDJQRElJUTRQaHlPT00iLCJadUV5cG41Y0s0WVpWdHdkeGFoWXJqMjZ1MFI2UmxpOVVJWlNjUGhoWTB3Iiwidi1rMzl2VGI5NFI5a25VWTZtbzlXUVdEQkNJS3lya0J4bExTQVl3T2MyNCJdfSwiaXNzdWluZ19jb3VudHJ5IjoiREUiLCJ2Y3QiOiJodHRwczovL2V4YW1wbGUuYm1pLmJ1bmQuZGUvY3JlZGVudGlhbC9waWQvMS4wIiwiaXNzdWluZ19hdXRob3JpdHkiOiJERSIsIl9zZF9hbGciOiJzaGEtMjU2IiwiaXNzIjoiaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL2MxIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IkhzS194Tl95SVU4eWlqdW9BWlhsbndFRU00ZlhZenVNRmd5TTE5SmRYMUkiLCJ5IjoiQUR2NnplVDl3YmgxU0ZxMG14TkcxMUZueC05eFdSRFcwR18xN1dSRXpRSSJ9fSwiZXhwIjoxNzMzNTcxMzI3LCJpYXQiOjE3MzIzNjE3MjcsImFnZV9lcXVhbF9vcl9vdmVyIjp7Il9zZCI6WyJLRDF0U0hnYWotZi1qbkZURkRDMW1sZ0RwNzhMZE1KcHlqWnRRU0k4a1ZnIiwiTDRjTTMtZU1mRHg0Znc2UEw3OVRTVFBnM042VXdzOGNPc3JOYmNqaEEtYyIsImRYUFBQX2lmNFM3XzBzcXZXNTBwZEdlMWszbS1wMnM3M1JicDlncThGaDAiLCJtYnllcU05YUkzRkVvWmFoODA5eTN0dlRCV1NvZTBMSlRUYTlONGNjdmlZIiwicm1zd0dEZnhvS0ZFYlFsNzZ4S1ZVT0hrX0MyQlVpVnQ5RDlvMTFrMmZNSSIsInZsY2Y4WTNhQnNTeEZBeVZfYk9NTndvX3FTT1pHc3ViSVZiY0FVSWVBSGMiXX19.gruqjNOuJBgHXEnG9e60wOoqiyEaL1K9pdL215a0ffZCjtIZ_kICDrO5vBiTrEmvjjd6w_N_thEYLhzob77Epg~WyJWRXlWQWF0LXoyNU8tbkQ0MVBaOGdnIiwiZmFtaWx5X25hbWUiLCJNVVNURVJNQU5OIl0~WyJLcnRPei1lRk9hMU9JYmpmUHUxcHRBIiwiZ2l2ZW5fbmFtZSIsIkVSSUtBIl0~WyJQQUVjSHp0NWk5bFFzNUZlRmFGUS1RIiwiYmlydGhkYXRlIiwiMTk2NC0wOC0xMiJd~', +}) +const mdocRecord = new MdocRecord({ + mdoc: Mdoc.fromBase64Url(sprindFunkeTestVectorBase64Url), +}) +const sdJwtVcRepository = agentContext.dependencyManager.resolve(SdJwtVcRepository) +const mdocRepository = agentContext.dependencyManager.resolve(MdocRepository) +const pexService = new DifPresentationExchangeService(agentContext.dependencyManager.resolve(W3cCredentialService)) + +const presentationDefinition: DifPresentationExchangeDefinitionV2 = { + id: '1ad8ea6e-ec51-4e14-b316-dd76a6275480', + name: 'PID and MDL - Rent a Car (vc+sd-jwt)', + purpose: 'To secure your car reservations and finalize the transaction, we require the following attributes', + input_descriptors: [ + { + id: 'bf8669f4-0cf3-4d16-b72b-b47eb702a7cd', + format: { + 'vc+sd-jwt': { + 'sd-jwt_alg_values': ['ES256'], + 'kb-jwt_alg_values': ['ES256'], + }, + }, + group: ['A'], + constraints: { + limit_disclosure: 'required', + fields: [ + { path: ['$.document_number'] }, + { path: ['$.portrait'] }, + { path: ['$.issue_date'] }, + { path: ['$.expiry_date'] }, + { path: ['$.issuing_country'] }, + { path: ['$.issuing_authority'] }, + { path: ['$.driving_priviliges'] }, + { + path: ['$.vct'], + filter: { + type: 'string', + enum: ['https://example.eudi.ec.europa.eu/mdl/1'], + }, + }, + ], + }, + }, + { + id: '99fce09b-a0d3-415b-b8a7-3eab8829babc', + format: { + 'vc+sd-jwt': { + 'sd-jwt_alg_values': ['ES256'], + 'kb-jwt_alg_values': ['ES256'], + }, + }, + group: ['B'], + constraints: { + limit_disclosure: 'required', + fields: [ + { path: ['$.given_name'] }, + { path: ['$.family_name'] }, + { path: ['$.birthdate'] }, + { + path: ['$.vct'], + filter: { + type: 'string', + enum: ['https://example.bmi.bund.de/credential/pid/1.0', 'urn:eu.europa.ec.eudi:pid:1'], + }, + }, + { + path: ['$.iss'], + filter: { + type: 'string', + enum: [ + 'https://demo.pid-issuer.bundesdruckerei.de/c', + 'https://demo.pid-issuer.bundesdruckerei.de/c1', + 'https://demo.pid-issuer.bundesdruckerei.de/b1', + ], + }, + }, + ], + }, + }, + { + id: 'eu.europa.ec.eudi.pid.1', + format: { + mso_mdoc: { + alg: ['ES256'], + }, + }, + group: ['C'], + constraints: { + limit_disclosure: 'required', + fields: [ + { + intent_to_retain: false, + path: ["$['eu.europa.ec.eudi.pid.1']['birth_date']"], + }, + ], + }, + }, + { + id: 'org.iso.18013.5.1.mDL', + format: { + mso_mdoc: { + alg: ['ES256'], + }, + }, + group: ['D'], + constraints: { + limit_disclosure: 'required', + fields: [ + { + intent_to_retain: false, + path: ["$['org.iso.18013.5.1']['given_name']"], + }, + ], + }, + }, + ], +} + +describe('DifPresentationExchangeService', () => { + beforeAll(async () => { + await sdJwtVcRepository.save(agentContext, sdJwtVcRecord) + await mdocRepository.save(agentContext, mdocRecord) + }) + + test('handles request where two credentials are requested but only one available', async () => { + const credentialsForRequest = await pexService.getCredentialsForRequest(agentContext, presentationDefinition) + expect(credentialsForRequest).toEqual({ + requirements: [ + { + rule: 'pick', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: 'bf8669f4-0cf3-4d16-b72b-b47eb702a7cd', + name: undefined, + purpose: undefined, + verifiableCredentials: [], + }, + ], + isRequirementSatisfied: false, + }, + { + rule: 'pick', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: '99fce09b-a0d3-415b-b8a7-3eab8829babc', + name: undefined, + purpose: undefined, + verifiableCredentials: [ + { + credentialRecord: await sdJwtVcRepository.getById(agentContext, sdJwtVcRecord.id), + disclosedPayload: { + address: {}, + age_equal_or_over: {}, + birthdate: '1964-08-12', + cnf: { + jwk: { + crv: 'P-256', + kty: 'EC', + x: 'HsK_xN_yIU8yijuoAZXlnwEEM4fXYzuMFgyM19JdX1I', + y: 'ADv6zeT9wbh1SFq0mxNG11Fnx-9xWRDW0G_17WREzQI', + }, + }, + exp: 1733571327, + family_name: 'MUSTERMANN', + given_name: 'ERIKA', + iat: 1732361727, + iss: 'https://demo.pid-issuer.bundesdruckerei.de/c1', + issuing_authority: 'DE', + issuing_country: 'DE', + place_of_birth: {}, + vct: 'https://example.bmi.bund.de/credential/pid/1.0', + }, + type: 'vc+sd-jwt', + }, + ], + }, + ], + isRequirementSatisfied: true, + }, + { + isRequirementSatisfied: true, + needsCount: 1, + rule: 'pick', + submissionEntry: [ + { + inputDescriptorId: 'eu.europa.ec.eudi.pid.1', + name: undefined, + purpose: undefined, + verifiableCredentials: [ + { + credentialRecord: await mdocRepository.getById(agentContext, mdocRecord.id), + disclosedPayload: { + 'eu.europa.ec.eudi.pid.1': { + birth_date: '1984-01-26', + }, + }, + type: 'mso_mdoc', + }, + ], + }, + ], + }, + { + rule: 'pick', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: 'org.iso.18013.5.1.mDL', + name: undefined, + purpose: undefined, + verifiableCredentials: [], + }, + ], + isRequirementSatisfied: false, + }, + ], + areRequirementsSatisfied: false, + name: 'PID and MDL - Rent a Car (vc+sd-jwt)', + purpose: 'To secure your car reservations and finalize the transaction, we require the following attributes', + }) + }) + + test('handles request with submission requirements where two credentials are requested but only one available', async () => { + const credentialsForRequest = await pexService.getCredentialsForRequest(agentContext, { + ...presentationDefinition, + submission_requirements: [ + { + rule: 'pick', + count: 1, + from: 'A', + }, + { + rule: 'all', + from: 'B', + }, + { + rule: 'pick', + count: 1, + from: 'C', + }, + { + rule: 'all', + from: 'D', + }, + ], + }) + expect(credentialsForRequest).toEqual({ + requirements: [ + { + rule: 'pick', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: 'bf8669f4-0cf3-4d16-b72b-b47eb702a7cd', + name: undefined, + purpose: undefined, + verifiableCredentials: [], + }, + ], + isRequirementSatisfied: false, + }, + { + rule: 'all', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: '99fce09b-a0d3-415b-b8a7-3eab8829babc', + name: undefined, + purpose: undefined, + verifiableCredentials: [ + { + credentialRecord: await sdJwtVcRepository.getById(agentContext, sdJwtVcRecord.id), + disclosedPayload: { + address: {}, + age_equal_or_over: {}, + birthdate: '1964-08-12', + cnf: { + jwk: { + crv: 'P-256', + kty: 'EC', + x: 'HsK_xN_yIU8yijuoAZXlnwEEM4fXYzuMFgyM19JdX1I', + y: 'ADv6zeT9wbh1SFq0mxNG11Fnx-9xWRDW0G_17WREzQI', + }, + }, + exp: 1733571327, + family_name: 'MUSTERMANN', + given_name: 'ERIKA', + iat: 1732361727, + iss: 'https://demo.pid-issuer.bundesdruckerei.de/c1', + issuing_authority: 'DE', + issuing_country: 'DE', + place_of_birth: {}, + vct: 'https://example.bmi.bund.de/credential/pid/1.0', + }, + type: 'vc+sd-jwt', + }, + ], + }, + ], + isRequirementSatisfied: true, + }, + { + isRequirementSatisfied: true, + needsCount: 1, + rule: 'pick', + submissionEntry: [ + { + inputDescriptorId: 'eu.europa.ec.eudi.pid.1', + name: undefined, + purpose: undefined, + verifiableCredentials: [ + { + credentialRecord: await mdocRepository.getById(agentContext, mdocRecord.id), + disclosedPayload: { + 'eu.europa.ec.eudi.pid.1': { + birth_date: '1984-01-26', + }, + }, + type: 'mso_mdoc', + }, + ], + }, + ], + }, + { + rule: 'all', + needsCount: 1, + submissionEntry: [ + { + inputDescriptorId: 'org.iso.18013.5.1.mDL', + name: undefined, + purpose: undefined, + verifiableCredentials: [], + }, + ], + isRequirementSatisfied: false, + }, + ], + areRequirementsSatisfied: false, + name: 'PID and MDL - Rent a Car (vc+sd-jwt)', + purpose: 'To secure your car reservations and finalize the transaction, we require the following attributes', + }) + }) +}) diff --git a/packages/core/src/modules/dif-presentation-exchange/models/index.ts b/packages/core/src/modules/dif-presentation-exchange/models/index.ts index 1a86a09526..8447a360bb 100644 --- a/packages/core/src/modules/dif-presentation-exchange/models/index.ts +++ b/packages/core/src/modules/dif-presentation-exchange/models/index.ts @@ -4,7 +4,7 @@ import type { SdJwtVc } from '../../sd-jwt-vc' import type { W3cVerifiableCredential, W3cVerifiablePresentation } from '../../vc' import type { PresentationDefinitionV1, PresentationDefinitionV2, PresentationSubmission } from '@sphereon/pex-models' -import { PresentationSubmissionLocation } from '@sphereon/pex' +import { PresentationSubmissionLocation } from '@animo-id/pex' // Re-export some types from sphereon library, but under more explicit names export type DifPresentationExchangeDefinition = PresentationDefinitionV1 | PresentationDefinitionV2 diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts index d63b795cc9..c6aef4c717 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/credentialSelection.ts @@ -4,18 +4,21 @@ import type { DifPexCredentialsForRequestSubmissionEntry, SubmissionEntryCredential, } from '../models' -import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@sphereon/pex' +import type { IPresentationDefinition, SelectResults, SubmissionRequirementMatch, PEX } from '@animo-id/pex' +import type { + SubmissionRequirementMatchFrom, + SubmissionRequirementMatchInputDescriptor, +} from '@animo-id/pex/dist/main/lib/evaluation/core' import type { InputDescriptorV1, InputDescriptorV2, SubmissionRequirement } from '@sphereon/pex-models' +import { Status } from '@animo-id/pex' +import { SubmissionRequirementMatchType } from '@animo-id/pex/dist/main/lib/evaluation/core' +import { JSONPath } from '@astronautlabs/jsonpath' import { decodeSdJwtSync, getClaimsSync } from '@sd-jwt/decode' -import { Status } from '@sphereon/pex' -import { SubmissionRequirementMatchType } from '@sphereon/pex/dist/main/lib/evaluation/core' import { Rules } from '@sphereon/pex-models' -import { default as jp } from 'jsonpath' import { Hasher } from '../../../crypto' import { CredoError } from '../../../error' -import { deepEquality } from '../../../utils' import { MdocRecord } from '../../mdoc' import { Mdoc } from '../../mdoc/Mdoc' import { MdocDeviceResponse } from '../../mdoc/MdocDeviceResponse' @@ -31,106 +34,46 @@ export async function getCredentialsForRequest( presentationDefinition: IPresentationDefinition, credentialRecords: Array ): Promise { - const encodedCredentials = credentialRecords - .filter((c): c is Exclude => c instanceof MdocRecord === false) - .map((c) => getSphereonOriginalVerifiableCredential(c)) - - const { mdocPresentationDefinition, nonMdocPresentationDefinition } = - MdocDeviceResponse.partitionPresentationDefinition(presentationDefinition) - - const selectResultsRaw = pex.selectFrom(nonMdocPresentationDefinition, encodedCredentials) + const encodedCredentials = credentialRecords.map(getSphereonOriginalVerifiableCredential) + const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials) const selectResults: CredentialRecordSelectResults = { ...selectResultsRaw, - areRequiredCredentialsPresent: - nonMdocPresentationDefinition.input_descriptors.length === 0 && - mdocPresentationDefinition.input_descriptors.length > 0 - ? Status.INFO - : selectResultsRaw.areRequiredCredentialsPresent, - // Map the encoded credential to their respective w3c credential record - verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded): SubmissionEntryCredential => { - const credentialRecordIndex = encodedCredentials.findIndex((encoded) => { - if ( - typeof selectedEncoded === 'string' && - selectedEncoded.includes('~') && - typeof encoded === 'string' && - encoded.includes('~') - ) { - // FIXME: pex applies SD-JWT, so we actually can't match the record anymore :( - // We take the first part of the sd-jwt, as that will never change, and should - // be unique on it's own - const [encodedJwt] = encoded.split('~') - const [selectedEncodedJwt] = selectedEncoded.split('~') - - return encodedJwt === selectedEncodedJwt - } else { - return deepEquality(selectedEncoded, encoded) - } - }) - - if (credentialRecordIndex === -1) { - throw new DifPresentationExchangeError('Unable to find credential in credential records.') - } - - const credentialRecord = credentialRecords[credentialRecordIndex] - if (credentialRecord instanceof SdJwtVcRecord) { - // selectedEncoded always string when SdJwtVcRecord - // Get the decoded payload from the the selected credential, this already has SD applied - const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash) - const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash) - - return { - type: ClaimFormat.SdJwtVc, - credentialRecord, - disclosedPayload: prettyClaims as Record, + matches: selectResultsRaw.matches ?? [], + // Map the encoded credential to their respective credential record + verifiableCredential: + selectResultsRaw.verifiableCredential?.map((selectedEncoded, index): SubmissionEntryCredential => { + const credentialRecordIndex = selectResultsRaw.vcIndexes?.[index] + if (credentialRecordIndex === undefined || credentialRecordIndex === -1) { + throw new DifPresentationExchangeError('Unable to find credential in credential records.') } - } else if (credentialRecord instanceof W3cCredentialRecord) { - return { - type: credentialRecord.credential.claimFormat, - credentialRecord, + const credentialRecord = credentialRecords[credentialRecordIndex] + if (credentialRecord instanceof SdJwtVcRecord) { + // selectedEncoded always string when SdJwtVcRecord + // Get the decoded payload from the the selected credential, this already has SD applied + const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded as string, Hasher.hash) + const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash) + + return { + type: ClaimFormat.SdJwtVc, + credentialRecord, + disclosedPayload: prettyClaims as Record, + } + } else if (credentialRecord instanceof MdocRecord) { + return { + type: ClaimFormat.MsoMdoc, + credentialRecord, + disclosedPayload: {}, + } + } else if (credentialRecord instanceof W3cCredentialRecord) { + return { + type: credentialRecord.credential.claimFormat, + credentialRecord, + } + } else { + throw new CredoError(`Unrecognized credential record type`) } - } else { - throw new CredoError(`Unrecognized credential record type`) - } - }), - } - - const mdocRecords = credentialRecords.filter((c) => c instanceof MdocRecord) - for (const mdocInputDescriptor of mdocPresentationDefinition.input_descriptors) { - if (!selectResults.verifiableCredential) selectResults.verifiableCredential = [] - if (!selectResults.matches) selectResults.matches = [] - - const mdocRecordsMatchingId = mdocRecords.filter( - (mdocRecord) => mdocRecord.getTags().docType === mdocInputDescriptor.id - ) - const submissionRequirementMatch: SubmissionRequirementMatch = { - id: mdocInputDescriptor.id, - type: SubmissionRequirementMatchType.InputDescriptor, - name: mdocInputDescriptor.id, - rule: Rules.Pick, - vc_path: [], - } - - for (const mdocRecordMatchingId of mdocRecordsMatchingId) { - selectResults.verifiableCredential.push({ - type: ClaimFormat.MsoMdoc, - credentialRecord: mdocRecordMatchingId, - disclosedPayload: MdocDeviceResponse.limitDisclosureToInputDescriptor({ - mdoc: Mdoc.fromBase64Url(mdocRecordMatchingId.base64Url), - inputDescriptor: mdocInputDescriptor as InputDescriptorV2, - }), - }) - - submissionRequirementMatch.vc_path.push( - `$.verifiableCredential[${selectResults.verifiableCredential.length - 1}]` - ) - } - - if (submissionRequirementMatch.vc_path.length >= 1) { - selectResults.matches.push(submissionRequirementMatch) - } else { - selectResultsRaw.areRequiredCredentialsPresent = 'error' - } + }) ?? [], } const presentationSubmission: DifPexCredentialsForRequest = { @@ -150,6 +93,47 @@ export async function getCredentialsForRequest( presentationSubmission.requirements = getSubmissionRequirements(presentationDefinition, selectResults) } + const allEntries = presentationSubmission.requirements.flatMap((requirement) => requirement.submissionEntry) + + const inputDescriptorsForMdocCredential = new Map>() + for (const entry of allEntries) + for (const verifiableCredential of entry.verifiableCredentials) { + if (verifiableCredential.type !== ClaimFormat.MsoMdoc) continue + + const set = inputDescriptorsForMdocCredential.get(verifiableCredential) ?? new Set() + set.add(entry.inputDescriptorId) + inputDescriptorsForMdocCredential.set(verifiableCredential, set) + } + + // NOTE: it might be better to apply disclosure per credential/match (as that's also how mdoc does this) + // however this doesn't work very well in wallets, as you usually won't show the same credential twice with + // different disclosed attributes + // Apply limit disclosure for all mdocs + for (const [verifiableCredential, inputDescriptorIds] of inputDescriptorsForMdocCredential.entries()) { + if (verifiableCredential.type !== ClaimFormat.MsoMdoc) continue + + const inputDescriptorsForCredential = presentationDefinition.input_descriptors.filter(({ id }) => + inputDescriptorIds.has(id) + ) + + const mdoc = Mdoc.fromBase64Url(verifiableCredential.credentialRecord.base64Url) + verifiableCredential.disclosedPayload = MdocDeviceResponse.limitDisclosureToInputDescriptor({ + inputDescriptor: { + id: mdoc.docType, + format: { + mso_mdoc: { + alg: [], + }, + }, + constraints: { + limit_disclosure: 'required', + fields: inputDescriptorsForCredential.flatMap((i) => i.constraints?.fields ?? []), + }, + }, + mdoc: Mdoc.fromBase64Url(verifiableCredential.credentialRecord.base64Url), + }) + } + // There may be no requirements if we filter out all optional ones. To not makes things too complicated, we see it as an error // for now if a request is made that has no required requirements (but only e.g. min: 0, which means we don't need to disclose anything) // I see this more as the fault of the presentation definition, as it should have at least some requirements. @@ -158,7 +142,8 @@ export async function getCredentialsForRequest( 'Presentation Definition does not require any credentials. Optional credentials are not included in the presentation submission.' ) } - if (selectResults.areRequiredCredentialsPresent === 'error') { + + if (selectResults.areRequiredCredentialsPresent === Status.ERROR) { return presentationSubmission } @@ -178,9 +163,16 @@ function getSubmissionRequirements( ): Array { const submissionRequirements: Array = [] + const matches = selectResults.matches as SubmissionRequirementMatchFrom[] + if (!matches.every((match) => match.type === SubmissionRequirementMatchType.SubmissionRequirement && match.from)) { + throw new DifPresentationExchangeError( + `Expected all matches to be of type '${SubmissionRequirementMatchType.SubmissionRequirement}' with 'from' key.` + ) + } + // There are submission requirements, so we need to select the input_descriptors // based on the submission requirements - for (const submissionRequirement of presentationDefinition.submission_requirements ?? []) { + presentationDefinition.submission_requirements?.forEach((submissionRequirement, submissionRequirementIndex) => { // Check: if the submissionRequirement uses `from_nested`, as we don't support this yet if (submissionRequirement.from_nested) { throw new DifPresentationExchangeError( @@ -193,23 +185,30 @@ function getSubmissionRequirements( throw new DifPresentationExchangeError("Missing 'from' in submission requirement match") } + const match = matches.find((match) => match.id === submissionRequirementIndex) + if (!match) { + throw new Error(`Unable to find a match for submission requirement with index '${submissionRequirementIndex}'`) + } + if (submissionRequirement.rule === Rules.All) { const selectedSubmission = getSubmissionRequirementRuleAll( submissionRequirement, presentationDefinition, - selectResults + selectResults.verifiableCredential, + match ) submissionRequirements.push(selectedSubmission) } else { const selectedSubmission = getSubmissionRequirementRulePick( submissionRequirement, presentationDefinition, - selectResults + selectResults.verifiableCredential, + match ) submissionRequirements.push(selectedSubmission) } - } + }) // Submission may have requirement that doesn't require a credential to be submitted (e.g. min: 0) // We use minimization strategy, and thus only disclose the minimum amount of information @@ -224,9 +223,15 @@ function getSubmissionRequirementsForAllInputDescriptors( ): Array { const submissionRequirements: Array = [] - for (const inputDescriptor of inputDescriptors) { - const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + const matches = selectResults.matches as SubmissionRequirementMatchInputDescriptor[] + if (!matches.every((match) => match.type === SubmissionRequirementMatchType.InputDescriptor)) { + throw new DifPresentationExchangeError( + `Expected all matches to be of type '${SubmissionRequirementMatchType.InputDescriptor}' when.` + ) + } + for (const inputDescriptor of inputDescriptors) { + const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults.verifiableCredential, matches) submissionRequirements.push({ rule: Rules.Pick, needsCount: 1, // Every input descriptor is a distinct requirement, so the count is always 1, @@ -241,7 +246,8 @@ function getSubmissionRequirementsForAllInputDescriptors( function getSubmissionRequirementRuleAll( submissionRequirement: SubmissionRequirement, presentationDefinition: IPresentationDefinition, - selectResults: CredentialRecordSelectResults + verifiableCredentials: SubmissionEntryCredential[], + match: SubmissionRequirementMatchFrom ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) @@ -258,9 +264,9 @@ function getSubmissionRequirementRuleAll( for (const inputDescriptor of presentationDefinition.input_descriptors) { // We only want to get the submission if the input descriptor belongs to the group - if (!inputDescriptor.group?.includes(submissionRequirement.from)) continue + if (!inputDescriptor.group?.includes(match.from)) continue - const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors) // Rule ALL, so for every input descriptor that matches in this group, we need to add it selectedSubmission.needsCount += 1 @@ -280,7 +286,8 @@ function getSubmissionRequirementRuleAll( function getSubmissionRequirementRulePick( submissionRequirement: SubmissionRequirement, presentationDefinition: IPresentationDefinition, - selectResults: CredentialRecordSelectResults + verifiableCredentials: SubmissionEntryCredential[], + match: SubmissionRequirementMatchFrom ) { // Check if there's a 'from'. If not the structure is not as we expect it if (!submissionRequirement.from) { @@ -303,9 +310,9 @@ function getSubmissionRequirementRulePick( for (const inputDescriptor of presentationDefinition.input_descriptors) { // We only want to get the submission if the input descriptor belongs to the group - if (!inputDescriptor.group?.includes(submissionRequirement.from)) continue + if (!inputDescriptor.group?.includes(match.from)) continue - const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults) + const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors) if (submission.verifiableCredentials.length >= 1) { satisfiedSubmissions.push(submission) @@ -336,47 +343,34 @@ function getSubmissionRequirementRulePick( function getSubmissionForInputDescriptor( inputDescriptor: InputDescriptorV1 | InputDescriptorV2, - selectResults: CredentialRecordSelectResults + verifiableCredentials: SubmissionEntryCredential[], + matches: SubmissionRequirementMatchInputDescriptor[] ): DifPexCredentialsForRequestSubmissionEntry { - // https://github.com/Sphereon-Opensource/PEX/issues/116 - // If the input descriptor doesn't contain a name, the name of the match will be the id of the input descriptor that satisfied it - const matchesForInputDescriptor = selectResults.matches?.filter( - (m) => - m.name === inputDescriptor.id || - // FIXME: this is not collision proof as the name doesn't have to be unique - m.name === inputDescriptor.name - ) + const matchesForInputDescriptor = matches.filter((m) => m.id === inputDescriptor.id) const submissionEntry: DifPexCredentialsForRequestSubmissionEntry = { inputDescriptorId: inputDescriptor.id, name: inputDescriptor.name, purpose: inputDescriptor.purpose, - verifiableCredentials: [], + verifiableCredentials: matchesForInputDescriptor.flatMap((matchForInputDescriptor) => + extractCredentialsFromInputDescriptorMatch(matchForInputDescriptor, verifiableCredentials) + ), } // return early if no matches. if (!matchesForInputDescriptor?.length) return submissionEntry - // FIXME: This can return multiple credentials for multiple input_descriptors, - // which I think is a bug in the PEX library - // Extract all credentials from the match - const verifiableCredentials = matchesForInputDescriptor.flatMap((matchForInputDescriptor) => - extractCredentialsFromMatch(matchForInputDescriptor, selectResults.verifiableCredential) - ) - - submissionEntry.verifiableCredentials = verifiableCredentials - return submissionEntry } -function extractCredentialsFromMatch( - match: SubmissionRequirementMatch, - availableCredentials?: SubmissionEntryCredential[] +function extractCredentialsFromInputDescriptorMatch( + match: SubmissionRequirementMatchInputDescriptor, + availableCredentials: SubmissionEntryCredential[] ) { const verifiableCredentials: SubmissionEntryCredential[] = [] for (const vcPath of match.vc_path) { - const [verifiableCredential] = jp.query( + const [verifiableCredential] = JSONPath.query( { verifiableCredential: availableCredentials }, vcPath ) as SubmissionEntryCredential[] @@ -390,5 +384,6 @@ function extractCredentialsFromMatch( * Custom SelectResults that includes the Credo records instead of the encoded verifiable credential */ type CredentialRecordSelectResults = Omit & { - verifiableCredential?: SubmissionEntryCredential[] + verifiableCredential: SubmissionEntryCredential[] + matches: SubmissionRequirementMatch[] } diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/presentationSelection.ts b/packages/core/src/modules/dif-presentation-exchange/utils/presentationSelection.ts index b223fcd65b..79306b27fe 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/presentationSelection.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/presentationSelection.ts @@ -6,7 +6,7 @@ import type { VerifiablePresentation, } from '../models' -import { default as jp } from 'jsonpath' +import { JSONPath } from '@astronautlabs/jsonpath' import { CredoError } from '../../../error' import { MdocDeviceResponse } from '../../mdoc' @@ -21,7 +21,7 @@ export function extractPresentationsWithDescriptorsFromSubmission( definition: DifPresentationExchangeDefinition ) { return submission.descriptor_map.map((descriptor) => { - const [presentation] = jp.query(presentations, descriptor.path) as [VerifiablePresentation | undefined] + const [presentation] = JSONPath.query(presentations, descriptor.path) as [VerifiablePresentation | undefined] const inputDescriptor = definition.input_descriptors.find(({ id }) => id === descriptor.id) if (!presentation) { @@ -61,7 +61,7 @@ export function extractPresentationsWithDescriptorsFromSubmission( ) } - const [verifiableCredential] = jp.query( + const [verifiableCredential] = JSONPath.query( // Path is `$.vp.verifiableCredential[]` in case of jwt vp presentation.claimFormat === ClaimFormat.JwtVp ? { vp: presentation } : presentation, descriptor.path_nested.path diff --git a/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts index 87bcafdfbb..7fa2ba018e 100644 --- a/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts +++ b/packages/core/src/modules/dif-presentation-exchange/utils/transform.ts @@ -10,15 +10,17 @@ import type { import { Jwt } from '../../../crypto' import { JsonTransformer } from '../../../utils' -import { MdocDeviceResponse } from '../../mdoc' +import { MdocDeviceResponse, MdocRecord } from '../../mdoc' import { SdJwtVcApi } from '../../sd-jwt-vc' import { W3cCredentialRecord, W3cJsonLdVerifiablePresentation, W3cJwtVerifiablePresentation } from '../../vc' export function getSphereonOriginalVerifiableCredential( - credentialRecord: W3cCredentialRecord | SdJwtVcRecord + credentialRecord: W3cCredentialRecord | SdJwtVcRecord | MdocRecord ): SphereonOriginalVerifiableCredential { if (credentialRecord instanceof W3cCredentialRecord) { return credentialRecord.credential.encoded as SphereonOriginalVerifiableCredential + } else if (credentialRecord instanceof MdocRecord) { + return credentialRecord.base64Url } else { return credentialRecord.compactSdJwtVc } diff --git a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts index e02bc8b08e..e951708d34 100644 --- a/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts +++ b/packages/core/src/modules/proofs/formats/dif-presentation-exchange/__tests__/PresentationExchangeProofFormatService.test.ts @@ -2,7 +2,7 @@ import type { DifPresentationExchangeDefinitionV1 } from '../../../../dif-presen import type { ProofFormatService } from '../../ProofFormatService' import type { DifPresentationExchangeProofFormat } from '../DifPresentationExchangeProofFormat' -import { PresentationSubmissionLocation } from '@sphereon/pex' +import { PresentationSubmissionLocation } from '@animo-id/pex' import { getInMemoryAgentOptions } from '../../../../../../tests' import { Agent } from '../../../../../agent/Agent' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4d902bb55..07e820f4cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -330,8 +330,8 @@ importers: version: 4.8.0 devDependencies: '@animo-id/expo-secure-environment': - specifier: ^0.0.1-alpha.0 - version: 0.0.1-alpha.0(expo@51.0.29(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))(react-native@0.71.19(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(react@18.3.1))(react@18.3.1) + specifier: ^0.1.0-alpha.11 + version: 0.1.0-alpha.11(expo@51.0.29(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))(react-native@0.71.19(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(react@18.3.1))(react@18.3.1) '@hyperledger/aries-askar-nodejs': specifier: ^0.2.3 version: 0.2.3 @@ -439,6 +439,12 @@ importers: '@animo-id/mdoc': specifier: 0.2.39 version: 0.2.39 + '@animo-id/pex': + specifier: 4.1.1-alpha.0 + version: 4.1.1-alpha.0 + '@astronautlabs/jsonpath': + specifier: ^1.1.2 + version: 1.1.2 '@digitalcredentials/jsonld': specifier: ^6.0.0 version: 6.0.0(expo@51.0.29(@babel/core@7.25.2)(@babel/preset-env@7.25.8(@babel/core@7.25.2)))(react-native@0.71.19(@babel/core@7.25.2)(@babel/preset-env@7.25.8(@babel/core@7.25.2))(react@18.3.1))(web-streams-polyfill@3.3.3) @@ -487,9 +493,6 @@ importers: '@sd-jwt/utils': specifier: ^0.7.0 version: 0.7.2 - '@sphereon/pex': - specifier: 5.0.0-unstable.25 - version: 5.0.0-unstable.25 '@sphereon/pex-models': specifier: ^2.3.1 version: 2.3.1 @@ -523,9 +526,6 @@ importers: did-resolver: specifier: ^4.1.0 version: 4.1.0 - jsonpath: - specifier: ^1.1.1 - version: 1.1.1 lru_map: specifier: ^0.4.1 version: 0.4.1 @@ -566,9 +566,6 @@ importers: '@types/events': specifier: ^3.0.0 version: 3.0.3 - '@types/jsonpath': - specifier: ^0.2.4 - version: 0.2.4 '@types/luxon': specifier: ^3.2.0 version: 3.4.2 @@ -927,8 +924,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@animo-id/expo-secure-environment@0.0.1-alpha.0': - resolution: {integrity: sha512-5ldT6osZjTxJgxi9Qr5hm8VN1FTsXkNhwBdEcu7qMUvnd2LhKSOyKlUCUAOfDNuhFqXDU1K6Cmy3P7kJ6dEK/Q==} + '@animo-id/expo-secure-environment@0.1.0-alpha.11': + resolution: {integrity: sha512-cAwsK8QWZc4ywxH6r0sqrH5yGdp5bKkw3fUr41CSs/TDZ42FIMeIK+tEdc78Bp+szykEywnnZpaAKdoTAd3ebw==} peerDependencies: expo: '*' react: '*' @@ -946,6 +943,10 @@ packages: '@animo-id/oid4vci@0.1.4': resolution: {integrity: sha512-0Tc8RFpY5dW/MOVA2Fz8f7giM/eIAk3G3OZUjy5POqg5Tkiv92CcaOxMk84JBNAMOks3LpKjf9PAiNh6pCDzeQ==} + '@animo-id/pex@4.1.1-alpha.0': + resolution: {integrity: sha512-6ieHhH9UE9DLFOJegMCabG3qUFlQk4TLhBefxInpyjx2Ly6kuloVMScJYcnQTs/E6nuHGMd7ebUaKy4+0+ZbOA==} + engines: {node: '>=18'} + '@animo-id/react-native-bbs-signatures@0.1.0': resolution: {integrity: sha512-7qvsiWhGfUev8ngE8YzF6ON9PtCID5LiYVYM4EC5eyj80gCdhx3R46CI7K1qbqIlGsoTYQ/Xx5Ubo5Ji9eaUEA==} peerDependencies: @@ -2946,10 +2947,6 @@ packages: resolution: {integrity: sha512-CZc+kr8cJqPsFSpg4kHyamr5oB5xLVP2E5eJ0pbetOfOE2uSxqk0/A8zGazcPhU1zZILrO51hD4vW/hJRgtKJQ==} engines: {node: '>=18'} - '@sphereon/pex@5.0.0-unstable.25': - resolution: {integrity: sha512-EUWfGa6t20PPkYf+zbfWXhc1sSWiFNywbRah8R6grJPU738pfwWpZPunSEY3x0CoxAVaSVXn91wZ/sxmgPCFkA==} - engines: {node: '>=18'} - '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.130': resolution: {integrity: sha512-I+0VjitRjisABWm8RtTPQG57tFwfUS13Wud30OvBoADRxnaA0guUrkS82AYtV6YD0TBHdrd0D6a0RCJwK9SvDg==} @@ -3158,9 +3155,6 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/jsonpath@0.2.4': - resolution: {integrity: sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==} - '@types/keygrip@1.0.6': resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} @@ -4584,11 +4578,6 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@1.2.2: - resolution: {integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==} - engines: {node: '>=0.4.0'} - hasBin: true - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -5777,9 +5766,6 @@ packages: resolution: {integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==} engines: {node: '>=14'} - jsonpath@1.1.1: - resolution: {integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==} - jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -7801,9 +7787,6 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - underscore@1.12.1: - resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} - undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -8207,7 +8190,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@animo-id/expo-secure-environment@0.0.1-alpha.0(expo@51.0.29(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))(react-native@0.71.19(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(react@18.3.1))(react@18.3.1)': + '@animo-id/expo-secure-environment@0.1.0-alpha.11(expo@51.0.29(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))(react-native@0.71.19(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(react@18.3.1))(react@18.3.1)': dependencies: '@peculiar/asn1-ecc': 2.3.13 '@peculiar/asn1-schema': 2.3.13 @@ -8242,6 +8225,22 @@ snapshots: transitivePeerDependencies: - typescript + '@animo-id/pex@4.1.1-alpha.0': + dependencies: + '@astronautlabs/jsonpath': 1.1.2 + '@sd-jwt/decode': 0.7.2 + '@sd-jwt/present': 0.7.2 + '@sd-jwt/types': 0.7.2 + '@sphereon/pex-models': 2.3.1 + '@sphereon/ssi-types': 0.30.2-next.135 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + jwt-decode: 3.1.2 + nanoid: 3.3.7 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + '@animo-id/react-native-bbs-signatures@0.1.0(react-native@0.71.19(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 @@ -12536,22 +12535,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sphereon/pex@5.0.0-unstable.25': - dependencies: - '@astronautlabs/jsonpath': 1.1.2 - '@sd-jwt/decode': 0.7.2 - '@sd-jwt/present': 0.7.2 - '@sd-jwt/types': 0.7.2 - '@sphereon/pex-models': 2.3.1 - '@sphereon/ssi-types': 0.30.2-next.135 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - jwt-decode: 3.1.2 - nanoid: 3.3.7 - uint8arrays: 3.1.1 - transitivePeerDependencies: - - supports-color - '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.130(ts-node@10.9.2(@types/node@18.18.8)(typescript@5.5.4))': dependencies: '@ethersproject/networks': 5.7.1 @@ -13022,8 +13005,6 @@ snapshots: '@types/json5@0.0.29': {} - '@types/jsonpath@0.2.4': {} - '@types/keygrip@1.0.6': {} '@types/koa-compose@3.2.8': @@ -14755,7 +14736,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 @@ -14767,7 +14748,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -14788,7 +14769,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -14880,8 +14861,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - esprima@1.2.2: {} - esprima@4.0.1: {} esquery@1.6.0: @@ -16556,12 +16535,6 @@ snapshots: transitivePeerDependencies: - web-streams-polyfill - jsonpath@1.1.1: - dependencies: - esprima: 1.2.2 - static-eval: 2.0.2 - underscore: 1.12.1 - jsonpointer@5.0.1: {} jwt-decode@3.1.2: {} @@ -18893,8 +18866,6 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - underscore@1.12.1: {} - undici-types@5.26.5: {} undici@6.20.1: {}