From acd5e1335d49e51166232afc2df010d17b9a8dcf Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 30 Aug 2024 15:40:02 +0200 Subject: [PATCH 1/6] temp Signed-off-by: Timo Glastra --- .../__tests__/PresentationExchange.spec.ts | 39 ++-- .../lib/authorization-response/OpenID4VP.ts | 41 +++-- .../PresentationExchange.ts | 168 ++++++------------ packages/siop-oid4vp/lib/types/Errors.ts | 1 + packages/siop-oid4vp/package.json | 2 +- pnpm-lock.yaml | 12 +- 6 files changed, 110 insertions(+), 153 deletions(-) diff --git a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts index 67703968..88910689 100644 --- a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts @@ -221,7 +221,7 @@ describe('presentation exchange manager tests', () => { const vcs = getVCs() vcs[0].issuer = { id: 'did:example:totallyDifferentIssuer' } await expect( - PresentationExchange.validatePresentationAgainstDefinition( + PresentationExchange.validatePresentationsAgainstDefinition( pd[0].definition, CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], @@ -272,15 +272,14 @@ describe('presentation exchange manager tests', () => { const vcs = getVCs() vcs[0].issuer = { id: 'did:example:totallyDifferentIssuer' } await expect( - PresentationExchange.validatePresentationAgainstDefinition( - pd[0].definition, + PresentationExchange.validatePresentationsAgainstDefinition(pd[0].definition, [ CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation', 'PresentationSubmission'], presentation_submission, verifiableCredential: vcs, } as W3CVerifiablePresentation), - ), + ]), ).rejects.toThrow(SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD) }) @@ -297,7 +296,22 @@ describe('presentation exchange manager tests', () => { const payload = await getPayloadVID1Val() const vcs = getVCs() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) - const result = await PresentationExchange.validatePresentationAgainstDefinition( + console.log( + JSON.stringify( + { + pd, + vp: { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation', 'PresentationSubmission'], + presentation_submission, + verifiableCredential: vcs, + }, + }, + null, + 2, + ), + ) + const result = await PresentationExchange.validatePresentationsAgainstDefinition( pd[0].definition, CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], @@ -315,15 +329,14 @@ describe('presentation exchange manager tests', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: vcs }) const payload: AuthorizationRequestPayload = await getPayloadVID1Val() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) - await PresentationExchange.validatePresentationAgainstDefinition( - pd[0].definition, + await PresentationExchange.validatePresentationsAgainstDefinition(pd[0].definition, [ CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation', 'PresentationSubmission'], presentation_submission, verifiableCredential: vcs, } as W3CVerifiablePresentation), - ) + ]) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) const presentationSignCallback: PresentationSignCallback = async (_args) => ({ ...(_args.presentation as IPresentation), @@ -373,8 +386,8 @@ describe('presentation exchange manager tests', () => { const result = await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) expect(result.errors?.length).toBe(0) expect(result.matches?.length).toBe(1) - expect(result.matches[0].vc_path.length).toBe(1) - expect(result.matches[0].vc_path[0]).toBe('$.verifiableCredential[0]') + expect(result.matches?.[0].vc_path.length).toBe(1) + expect(result.matches?.[0].vc_path[0]).toBe('$.verifiableCredential[0]') }) it('pass if no PresentationDefinition is found', async () => { @@ -446,11 +459,13 @@ describe('presentation exchange manager tests', () => { await expect( PresentationExchange.validatePresentationsAgainstDefinitions( pd, - [CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation)], + CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation), () => { throw new Error('Verification failed') }, - {}, + { + presentationSubmission: verifiablePresentationResult.presentationSubmission, + }, ), ).rejects.toThrow(SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID) }) diff --git a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts index 0810a61e..97e101a1 100644 --- a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts +++ b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts @@ -95,13 +95,15 @@ export const verifyPresentations = async ( // If there are no presentations, and the `assertValidVerifiablePresentations` did not fail // it means there's no oid4vp response and also not requested - if (presentations.length === 0) { + if (Array.isArray(presentations) && presentations.length === 0) { return null } - const nonces = new Set(presentations.map(extractNonceFromWrappedVerifiablePresentation)) - if (presentations.length > 0 && nonces.size !== 1) { - throw Error(`${nonces.size} nonce values found for ${presentations.length}. Should be 1`) + const presentationsArray = Array.isArray(presentations) ? presentations : [presentations] + + const nonces = new Set(presentationsArray.map(extractNonceFromWrappedVerifiablePresentation)) + if (presentationsArray.length > 0 && nonces.size !== 1) { + throw Error(`${nonces.size} nonce values found for ${presentationsArray.length}. Should be 1`) } // Nonce may be undefined @@ -117,25 +119,24 @@ export const verifyPresentations = async ( if (!verifyOpts.verification.revocationOpts?.revocationVerificationCallback) { throw Error(`Please provide a revocation callback as revocation checking of credentials and presentations is not disabled`) } - for (const vp of presentations) { + for (const vp of presentationsArray) { await verifyRevocation(vp, verifyOpts.verification.revocationOpts.revocationVerificationCallback, revocationVerification) } } - return { nonce, presentations, presentationDefinitions, submissionData: presentationSubmission } + return { nonce, presentations: presentationsArray, presentationDefinitions, submissionData: presentationSubmission } } export const extractPresentationsFromAuthorizationResponse = async ( response: AuthorizationResponse, opts?: { hasher?: Hasher }, -): Promise => { - const wrappedVerifiablePresentations: WrappedVerifiablePresentation[] = [] - if (response.payload.vp_token) { - const presentations = Array.isArray(response.payload.vp_token) ? response.payload.vp_token : [response.payload.vp_token] - for (const presentation of presentations) { - wrappedVerifiablePresentations.push(CredentialMapper.toWrappedVerifiablePresentation(presentation, { hasher: opts?.hasher })) - } +): Promise => { + if (!response.payload.vp_token) return [] + + if (Array.isArray(response.payload.vp_token)) { + return response.payload.vp_token.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: opts?.hasher })) } - return wrappedVerifiablePresentations + + return CredentialMapper.toWrappedVerifiablePresentation(response.payload.vp_token, { hasher: opts?.hasher }) } export const createPresentationSubmission = async ( @@ -264,7 +265,7 @@ export const putPresentationSubmissionInLocation = async ( export const assertValidVerifiablePresentations = async (args: { presentationDefinitions: PresentationDefinitionWithLocation[] - presentations: WrappedVerifiablePresentation[] + presentations: Array | WrappedVerifiablePresentation verificationCallback: PresentationVerificationCallback opts?: { limitDisclosureSignatureSuites?: string[] @@ -283,16 +284,18 @@ export const assertValidVerifiablePresentations = async (args: { PresentationExchange.assertValidPresentationDefinitionWithLocations(args.presentationDefinitions) const presentationsWithFormat = args.presentations - if (args.presentationDefinitions && args.presentationDefinitions.length && (!presentationsWithFormat || presentationsWithFormat.length === 0)) { + if ( + args.presentationDefinitions && + args.presentationDefinitions.length && + (!presentationsWithFormat || (Array.isArray(presentationsWithFormat) && presentationsWithFormat.length === 0)) + ) { throw new Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP) } else if ( (!args.presentationDefinitions || args.presentationDefinitions.length === 0) && presentationsWithFormat && - presentationsWithFormat.length > 0 + ((Array.isArray(presentationsWithFormat) && presentationsWithFormat.length > 0) || !Array.isArray(presentationsWithFormat)) ) { throw new Error(SIOPErrors.AUTH_REQUEST_DOESNT_EXPECT_VP) - } else if (args.presentationDefinitions && presentationsWithFormat && args.presentationDefinitions.length != presentationsWithFormat.length) { - throw new Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP) } else if (args.presentationDefinitions && !args.opts.presentationSubmission) { throw new Error(`No presentation submission present. Please use presentationSubmission opt argument!`) } else if (args.presentationDefinitions && presentationsWithFormat) { diff --git a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts index 2aa96631..c46c4965 100644 --- a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts +++ b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts @@ -1,5 +1,4 @@ import { - EvaluationResults, IPresentationDefinition, KeyEncoding, PEX, @@ -9,6 +8,7 @@ import { VerifiablePresentationFromOpts, VerifiablePresentationResult, } from '@sphereon/pex' +import { PresentationEvaluationResults } from '@sphereon/pex/dist/main/lib/evaluation' import { Format, PresentationDefinitionV1, PresentationDefinitionV2, PresentationSubmission } from '@sphereon/pex-models' import { CredentialMapper, @@ -16,7 +16,6 @@ import { IProofPurpose, IProofType, OriginalVerifiableCredential, - OriginalVerifiablePresentation, W3CVerifiablePresentation, WrappedVerifiablePresentation, } from '@sphereon/ssi-types' @@ -116,46 +115,6 @@ export class PresentationExchange { return selectResults } - /** - * validatePresentationAgainstDefinition function is called mainly by the RP - * after receiving the VP from the OP - * @param presentationDefinition object containing PD - * @param verifiablePresentation - * @param opts - */ - public static async validatePresentationAgainstDefinition( - presentationDefinition: IPresentationDefinition, - verifiablePresentation: OriginalVerifiablePresentation | WrappedVerifiablePresentation, - opts?: { - limitDisclosureSignatureSuites?: string[] - restrictToFormats?: Format - restrictToDIDMethods?: string[] - presentationSubmission?: PresentationSubmission - hasher?: Hasher - }, - ): Promise { - const wvp: WrappedVerifiablePresentation = - typeof verifiablePresentation === 'object' && 'original' in verifiablePresentation - ? (verifiablePresentation as WrappedVerifiablePresentation) - : CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentation as OriginalVerifiablePresentation) - if (!presentationDefinition) { - throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID) - } else if ( - !wvp || - !wvp.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(wvp) && - (!wvp.presentation.verifiableCredential || wvp.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) - } - // console.log(`Presentation (validate): ${JSON.stringify(verifiablePresentation)}`); - const evaluationResults: EvaluationResults = new PEX({ hasher: opts?.hasher }).evaluatePresentation(presentationDefinition, wvp.original, opts) - if (evaluationResults.errors.length) { - throw new Error(`message: ${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}, details: ${JSON.stringify(evaluationResults.errors)}`) - } - return evaluationResults - } - public static assertValidPresentationSubmission(presentationSubmission: PresentationSubmission) { const validationResult = PEX.validateSubmission(presentationSubmission) if (validationResult[0].message != 'ok') { @@ -290,8 +249,8 @@ export class PresentationExchange { static async validatePresentationsAgainstDefinitions( definitions: PresentationDefinitionWithLocation[], - vpPayloads: WrappedVerifiablePresentation[], - verifyPresentationCallback: PresentationVerificationCallback | undefined, + vpPayloads: Array | WrappedVerifiablePresentation, + verifyPresentationCallback?: PresentationVerificationCallback | undefined, opts?: { limitDisclosureSignatureSuites?: string[] restrictToFormats?: Format @@ -300,7 +259,7 @@ export class PresentationExchange { hasher?: Hasher }, ) { - if (!definitions || !vpPayloads || !definitions.length || definitions.length !== vpPayloads.length) { + if (!definitions || !vpPayloads || (Array.isArray(vpPayloads) && vpPayloads.length === 0) || !definitions.length) { throw new Error(SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD) } await Promise.all( @@ -310,10 +269,10 @@ export class PresentationExchange { ) } - private static async validatePresentationsAgainstDefinition( + static async validatePresentationsAgainstDefinition( definition: IPresentationDefinition, - vpPayloads: WrappedVerifiablePresentation[], - verifyPresentationCallback: PresentationVerificationCallback | undefined, + vpPayloads: Array | WrappedVerifiablePresentation, + verifyPresentationCallback?: PresentationVerificationCallback | undefined, opts?: { limitDisclosureSignatureSuites?: string[] restrictToFormats?: Format @@ -323,28 +282,51 @@ export class PresentationExchange { }, ) { const pex = new PEX({ hasher: opts?.hasher }) + const vpPayloadsArray = Array.isArray(vpPayloads) ? vpPayloads : [vpPayloads] - async function filterOutCorrectPresentation() { - //TODO: add support for multiple VPs here - const matchingVps = vpPayloads.map(async (vpw: WrappedVerifiablePresentation): Promise => { - const presentationSubmission = - opts?.presentationSubmission ?? - (CredentialMapper.isWrappedW3CVerifiablePresentation(vpw) ? vpw.presentation.presentation_submission : undefined) - const presentation = vpw.presentation - if (!definition) { - throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) - } else if ( - !vpw.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(vpw) && - (!vpw.presentation.verifiableCredential || vpw.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) + let evaluationResults: PresentationEvaluationResults | undefined = undefined + if (opts?.presentationSubmission) { + evaluationResults = pex.evaluatePresentation( + definition, + Array.isArray(vpPayloads) ? vpPayloads.map((wvp) => wvp.original) : vpPayloads.original, + opts, + ) + } else { + for (const wvp of vpPayloadsArray) { + if (CredentialMapper.isWrappedW3CVerifiablePresentation(wvp) && wvp.presentation.presentation_submission) { + const presentationSubmission = wvp.presentation.presentation_submission + evaluationResults = pex.evaluatePresentation(definition, wvp.original, { ...opts, presentationSubmission }) + const submission = evaluationResults.value + + // Found valid submission + if (evaluationResults.areRequiredCredentialsPresent && submission && submission.definition_id === definition.id) break } - // The verifyPresentationCallback function is mandatory for RP only, - // So the behavior here is to bypass it if not present - if (verifyPresentationCallback) { + } + } + + if (!evaluationResults) { + throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) + } + + if (!evaluationResults.areRequiredCredentialsPresent || evaluationResults.errors.length || !evaluationResults.value) { + throw new Error(`message: ${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}, details: ${JSON.stringify(evaluationResults.errors)}`) + } + + if (evaluationResults.value.definition_id !== definition.id) { + throw new Error( + `${SIOPErrors.PRESENTATION_SUBMISSION_DEFINITION_ID_DOES_NOT_MATCHING_DEFINITION_ID}. submission.definition_id: ${evaluationResults.value.definition_id}, definition.id: ${definition.id}`, + ) + } + + const presentationsToVerify = Array.isArray(evaluationResults.presentation) ? evaluationResults.presentation : [evaluationResults.presentation] + // The verifyPresentationCallback function is mandatory for RP only, + // So the behavior here is to bypass it if not present + if (verifyPresentationCallback) { + // Verify the signature of all VPs + await Promise.all( + presentationsToVerify.map(async (presentation) => { try { - const verificationResult = await verifyPresentationCallback(vpw.original as W3CVerifiablePresentation, presentationSubmission) + const verificationResult = await verifyPresentationCallback(presentation as W3CVerifiablePresentation, evaluationResults.value) if (!verificationResult.verified) { throw new Error( SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID + verificationResult.reason ? `. ${verificationResult.reason}` : '', @@ -353,56 +335,12 @@ export class PresentationExchange { } catch (error: unknown) { throw new Error(SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID) } - } - // console.log(`Presentation (filter): ${JSON.stringify(presentation)}`); - - const evaluationResults = pex.evaluatePresentation(definition, vpw.original, { - ...opts, - presentationSubmission, - }) - const submission = evaluationResults.value - if (!presentation || !submission) { - throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) - } - - // No match - if (submission.definition_id !== definition.id) { - return undefined - } - - return vpw - }) - - // Wait for all results to finish and filter out undefined (no match) values - return (await Promise.all(matchingVps)).filter((vp) => vp !== undefined) + }), + ) } - const checkedPresentations = await filterOutCorrectPresentation() - - if (checkedPresentations.length !== 1) { - throw new Error(`${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}`) - } - const checkedPresentation = checkedPresentations[0] - const presentation = checkedPresentation.presentation - // console.log(`Presentation (checked): ${JSON.stringify(checkedPresentation.presentation)}`); - if ( - !checkedPresentation.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(checkedPresentation) && - (!checkedPresentation.presentation.verifiableCredential || checkedPresentation.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) - } - const presentationSubmission = - opts?.presentationSubmission ?? (CredentialMapper.isW3cPresentation(presentation) ? presentation.presentation_submission : undefined) - const evaluationResults = pex.evaluatePresentation(definition, checkedPresentation.original, { - ...opts, - presentationSubmission, - }) PresentationExchange.assertValidPresentationSubmission(evaluationResults.value) - await PresentationExchange.validatePresentationAgainstDefinition(definition, checkedPresentation, { - ...opts, - presentationSubmission, - hasher: opts?.hasher, - }) + + return evaluationResults } } diff --git a/packages/siop-oid4vp/lib/types/Errors.ts b/packages/siop-oid4vp/lib/types/Errors.ts index f8f1a242..4bc763ff 100644 --- a/packages/siop-oid4vp/lib/types/Errors.ts +++ b/packages/siop-oid4vp/lib/types/Errors.ts @@ -51,6 +51,7 @@ enum SIOPErrors { VERIFY_BAD_PARAMS = 'Verify bad parameters', VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID = 'The signature of the verifiable presentation is not valid', VERIFIABLE_PRESENTATION_VERIFICATION_FUNCTION_MISSING = 'The verifiable presentation verification function is missing', + PRESENTATION_SUBMISSION_DEFINITION_ID_DOES_NOT_MATCHING_DEFINITION_ID = "The 'definition_id' in the presentation submission does not match the id of the presentation definition.", } export default SIOPErrors diff --git a/packages/siop-oid4vp/package.json b/packages/siop-oid4vp/package.json index 810190e1..ffc794ce 100644 --- a/packages/siop-oid4vp/package.json +++ b/packages/siop-oid4vp/package.json @@ -16,7 +16,7 @@ "@astronautlabs/jsonpath": "^1.1.2", "@sphereon/did-uni-client": "^0.6.2", "@sphereon/oid4vc-common": "workspace:*", - "@sphereon/pex": "^3.3.2", + "@sphereon/pex": "^4.0.1", "@sphereon/pex-models": "^2.2.4", "@sphereon/ssi-types": "0.22.0", "@sphereon/wellknown-dids-client": "^0.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 588e4e99..fd64a979 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -442,8 +442,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/pex': - specifier: ^3.3.2 - version: 3.3.3 + specifier: ^4.0.1 + version: 4.0.1 '@sphereon/pex-models': specifier: ^2.2.4 version: 2.2.4 @@ -1857,7 +1857,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.7.3': resolution: {integrity: sha512-uMGHbAhApqXR2sd1KPhgvpbOhBBnspad8msEqHleT2PHXwKIwTUDzBGO9+jdOAWwCx2MJfw3+asYjzoD3DN9Bg==} @@ -2510,8 +2510,8 @@ packages: '@sphereon/pex-models@2.2.4': resolution: {integrity: sha512-pGlp+wplneE1+Lk3U48/2htYKTbONMeG5/x7vhO6AnPUOsnOXeJdftPrBYWVSzz/JH5GJptAc6+pAyYE1zMu4Q==} - '@sphereon/pex@3.3.3': - resolution: {integrity: sha512-CXwdEcMTUh2z/5AriBn3OuShEG06l2tgiIr7qDJthnkez8DQ3sZo2vr4NEQWKKAL+DeAWAI4FryQGO4KuK7yfg==} + '@sphereon/pex@4.0.1': + resolution: {integrity: sha512-3XnX/YJpxR8ueMO+qZLcn14dYFE4OHqi3i1qMLwPo/7jFFLrTb70uDG1++D+MqS7YTwYQbMtJA9KzzWcKTEg4w==} engines: {node: '>=18'} '@sphereon/ssi-express-support@0.29.0': @@ -11778,7 +11778,7 @@ snapshots: '@sphereon/pex-models@2.2.4': {} - '@sphereon/pex@3.3.3': + '@sphereon/pex@4.0.1': dependencies: '@astronautlabs/jsonpath': 1.1.2 '@sd-jwt/decode': 0.6.1 From 5d5b0d7277c121a51f2db1acc10c475d4a8b4d9d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 16 Sep 2024 11:58:46 +0200 Subject: [PATCH 2/6] feat: support exchanges with multiple vps Signed-off-by: Timo Glastra --- .../__tests__/PresentationExchange.spec.ts | 148 ++++++++++++++---- .../siop-oid4vp/lib/__tests__/SdJwt.spec.ts | 58 ++----- .../siop-oid4vp/lib/__tests__/TestUtils.ts | 36 ++++- .../PresentationExchange.ts | 13 +- packages/siop-oid4vp/package.json | 4 +- pnpm-lock.yaml | 10 +- 6 files changed, 185 insertions(+), 84 deletions(-) diff --git a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts index 88910689..c4f56be4 100644 --- a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts @@ -1,5 +1,6 @@ import { SigningAlgo } from '@sphereon/oid4vc-common' -import { PresentationDefinitionV1 } from '@sphereon/pex-models' +import { PEX } from '@sphereon/pex' +import { PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models' import { CredentialMapper, IPresentation, IProofType, IVerifiableCredential } from '@sphereon/ssi-types' import { W3CVerifiablePresentation } from '@sphereon/ssi-types/src/types/w3c-vc' import nock from 'nock' @@ -20,7 +21,7 @@ import { } from '..' import { SIOPErrors } from '../types' -import { mockedGetEnterpriseAuthToken } from './TestUtils' +import { mockedGetEnterpriseAuthToken, pexHasher } from './TestUtils' import { VERIFIER_LOGO_FOR_CLIENT, VERIFIER_NAME_FOR_CLIENT, @@ -32,7 +33,56 @@ import { const HOLDER_DID = 'did:example:ebfeb1f712ebc6f1c276e12ec21' const EXAMPLE_PD_URL = 'http://my_own_pd.com/pd/' -async function getPayloadVID1Val(): Promise { +const KB_JWT = `${Buffer.from(JSON.stringify({ typ: 'kb+jwt' })).toString('base64url')}.${Buffer.from(JSON.stringify({ sd_hash: 'real_hash', iat: 900, nonce: 'nonce' })).toString('base64url')}.signature` +const SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9kcml2ZXJzLWxpY2Vuc2UuZ292L2NhbGlmb3JuaWEiLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.signature~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' + + KB_JWT +const SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9kcml2ZXJzLWxpY2Vuc2UuZ292L3dhc2hpbmd0b24iLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.signature~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' + + KB_JWT + +const PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES = { + id: '32f54163-7166-48f1-93d8-ff217bdb0653', + name: 'Conference Entry Requirements', + purpose: 'We can only allow people associated with Washington State business representatives into conference areas', + format: { + 'vc+sd-jwt': {}, + }, + input_descriptors: [ + { + id: 'wa_driver_license', + constraints: { + limit_disclosure: 'required', + fields: [ + { + path: ['$.vct'], + filter: { + type: 'string', + const: 'https://drivers-license.gov/washington', + }, + }, + ], + }, + }, + { + id: 'ca_driver_license', + constraints: { + limit_disclosure: 'required', + fields: [ + { + path: ['$.vct'], + filter: { + type: 'string', + const: 'https://drivers-license.gov/california', + }, + }, + ], + }, + }, + ], +} satisfies PresentationDefinitionV2 + +async function getPayloadVID1Val({ pd }: { pd?: PresentationDefinitionV2 } = {}): Promise { const mockEntity = await mockedGetEnterpriseAuthToken('ACME Corp') const state = getState() return { @@ -70,7 +120,7 @@ async function getPayloadVID1Val(): Promise { acr: null, }, vp_token: { - presentation_definition: { + presentation_definition: pd ?? { id: 'Insurance Plans', input_descriptors: [ { @@ -296,21 +346,6 @@ describe('presentation exchange manager tests', () => { const payload = await getPayloadVID1Val() const vcs = getVCs() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) - console.log( - JSON.stringify( - { - pd, - vp: { - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiablePresentation', 'PresentationSubmission'], - presentation_submission, - verifiableCredential: vcs, - }, - }, - null, - 2, - ), - ) const result = await PresentationExchange.validatePresentationsAgainstDefinition( pd[0].definition, CredentialMapper.toWrappedVerifiablePresentation({ @@ -405,7 +440,7 @@ describe('presentation exchange manager tests', () => { expect(definition['input_descriptors'][0].schema.length).toBe(2) }) - it('should validate a list of VerifiablePresentations against a list of PresentationDefinitions', async () => { + it('should validate a single VerifiablePresentations against a list of PresentationDefinitions', async () => { const payload: AuthorizationRequestPayload = await getPayloadVID1Val() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) const vcs = getVCs() @@ -424,16 +459,73 @@ describe('presentation exchange manager tests', () => { }, }) const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, vcs, presentationSignCallback, {}) - try { - await PresentationExchange.validatePresentationsAgainstDefinitions( + await PresentationExchange.validatePresentationsAgainstDefinitions( + pd, + CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation), + undefined, + { + presentationSubmission: verifiablePresentationResult.presentationSubmission, + }, + ) + }) + + it('should validate a list of VerifiablePresentations against a list of PresentationDefinitions', async () => { + const payload: AuthorizationRequestPayload = await getPayloadVID1Val({ + pd: PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES, + }) + const pd = await PresentationExchange.findValidPresentationDefinitions(payload) + + // PEX/OID4VP doesn't have all functionality yet to create multi-vp submissions + // but it can make a submission based on already created presentations + const vps = [SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE, SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE] + + const pex = new PEX({ hasher: pexHasher }) + const evaluated = pex.evaluatePresentation(pd[0].definition, vps, { generatePresentationSubmission: true }) + if (!evaluated.value) throw new Error('No presentation submission was generated') + + await PresentationExchange.validatePresentationsAgainstDefinitions( + pd, + vps.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: pexHasher })), + undefined, + { + presentationSubmission: evaluated.value, + hasher: pexHasher, + }, + ) + }) + + it("'validatePresentationsAgainstDefinitions' should fail if not all presentations required for a presentation definition are present", async () => { + const payload: AuthorizationRequestPayload = await getPayloadVID1Val({ + pd: PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES, + }) + const pd = await PresentationExchange.findValidPresentationDefinitions(payload) + + const vps = [ + SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE, + // Do not include required washington drivers license + // SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE + ] + + // Manually created submission as we can't generate an invalid submission with PEX + const presentationSubmission = { + id: 'Z4JeKevZqmGFAfKmghwdf', + definition_id: '32f54163-7166-48f1-93d8-ff217bdb0653', + descriptor_map: [{ id: 'ca_driver_license', format: 'vc+sd-jwt', path: '$[0]' }], + } + + await expect( + PresentationExchange.validatePresentationsAgainstDefinitions( pd, - [CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation)], + vps.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: pexHasher })), undefined, - {}, - ) - } catch (e) { - console.log(e) - } + { + presentationSubmission, + hasher: pexHasher, + }, + ), + ).rejects.toThrow( + 'Could not find VerifiableCredentials matching presentationDefinition object in the provided VC list, details: [{"status":"error","tag":"SubmissionDoesNotSatisfyDefinition","message":"Expected all input descriptors (2) to be satisfifed in submission, but found 1. Missing wa_driver_license"}]', + ) }) it("'validatePresentationsAgainstDefinitions' should fail if provided VP verification callback fails", async () => { diff --git a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts index e67b7e22..2912c742 100644 --- a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts @@ -1,7 +1,5 @@ -import { createHash } from 'node:crypto' - import { SigningAlgo } from '@sphereon/oid4vc-common' -import { IPresentationDefinition, SdJwtDecodedVerifiableCredentialWithKbJwtInput } from '@sphereon/pex' +import { IPresentationDefinition } from '@sphereon/pex' import { OriginalVerifiableCredential } from '@sphereon/ssi-types' import { @@ -9,7 +7,6 @@ import { PassBy, PresentationDefinitionWithLocation, PresentationExchange, - PresentationSignCallback, PresentationVerificationCallback, PropertyTarget, ResponseIss, @@ -24,7 +21,7 @@ import { import { getVerifyJwtCallback, internalSignature } from './DidJwtTestUtils' import { getResolver } from './ResolverTestUtils' -import { mockedGetEnterpriseAuthToken, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' +import { mockedGetEnterpriseAuthToken, pexHasher, sdJwtVcPresentationSignCallback, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' import { VERIFIER_LOGO_FOR_CLIENT, VERIFIER_NAME_FOR_CLIENT, @@ -33,7 +30,6 @@ import { VERIFIERZ_PURPOSE_TO_VERIFY_NL, } from './data/mockedData' -const hasher = (data: string) => createHash('sha256').update(data).digest() jest.setTimeout(30000) const EXAMPLE_REDIRECT_URL = 'https://acme.com/hello' @@ -42,36 +38,6 @@ const HOLDER_DID = 'did:example:ebfeb1f712ebc6f1c276e12ec21' const SD_JWT_VC = 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9oaWdoLWFzc3VyYW5jZS5jb20vU3RhdGVCdXNpbmVzc0xpY2Vuc2UiLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.HAcudVInhNpXkTPQGNosjKTFRJWgKj90NpfloRaDQchGd4zxc1ChWTCCPXzUXTBypASKrzgjZCiXlTr0bzmLAg~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' -const presentationSignCallback: PresentationSignCallback = async (_args) => { - const kbJwt = (_args.presentation as SdJwtDecodedVerifiableCredentialWithKbJwtInput).kbJwt - - // In real life scenario, the KB-JWT must be signed - // As the KB-JWT is a normal JWT, the user does not need an sd-jwt implementation in the presentation sign callback - // NOTE: should the presentation just be the KB-JWT header + payload instead of the whole decoded SD JWT? - expect(kbJwt).toEqual({ - header: { - typ: 'kb+jwt', - }, - payload: { - sd_hash: expect.any(String), - iat: expect.any(Number), - nonce: expect.any(String), - }, - }) - - const header = { - ...kbJwt.header, - alg: 'ES256K', - } - const payload = { - ...kbJwt.payload, - aud: '123', - } - - const kbJwtCompact = `${Buffer.from(JSON.stringify(header)).toString('base64url')}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}.signature` - return SD_JWT_VC + kbJwtCompact -} - function getPresentationDefinition(): IPresentationDefinition { return { id: '32f54163-7166-48f1-93d8-ff217bdb0653', @@ -140,7 +106,7 @@ describe('RP and OP interaction should', () => { const rp = RP.builder({ requestVersion: SupportedVersion.SIOPv2_ID1 }) .withClientId(rpMockEntity.did) .withScope('test') - .withHasher(hasher) + .withHasher(pexHasher) .withResponseType([ResponseType.ID_TOKEN, ResponseType.VP_TOKEN]) .withRedirectUri(EXAMPLE_REDIRECT_URL) .withPresentationDefinition({ definition: getPresentationDefinition() }, [PropertyTarget.REQUEST_OBJECT, PropertyTarget.AUTHORIZATION_REQUEST]) @@ -170,9 +136,9 @@ describe('RP and OP interaction should', () => { .build() const op = OP.builder() - .withPresentationSignCallback(presentationSignCallback) + .withPresentationSignCallback(sdJwtVcPresentationSignCallback) .withExpiresIn(1000) - .withHasher(hasher) + .withHasher(pexHasher) .withCreateJwtCallback(internalSignature(opMockEntity.hexPrivateKey, opMockEntity.did, `${opMockEntity.did}#controller`, SigningAlgo.ES256K)) .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) .withRegistration({ @@ -212,13 +178,13 @@ describe('RP and OP interaction should', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: getVCs(), - hasher, + hasher: pexHasher, }) const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions( parsedAuthReqURI.authorizationRequestPayload, ) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) - const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), presentationSignCallback, { + const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), sdJwtVcPresentationSignCallback, { proofOptions: { nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg', }, @@ -255,7 +221,7 @@ describe('RP and OP interaction should', () => { requestVersion: SupportedVersion.SIOPv2_D12_OID4VP_D18, }) .withClientId(rpMockEntity.did) - .withHasher(hasher) + .withHasher(pexHasher) .withResponseType([ResponseType.VP_TOKEN]) .withRedirectUri(EXAMPLE_REDIRECT_URL) .withPresentationDefinition({ definition: getPresentationDefinition() }, [PropertyTarget.REQUEST_OBJECT, PropertyTarget.AUTHORIZATION_REQUEST]) @@ -283,9 +249,9 @@ describe('RP and OP interaction should', () => { .withSupportedVersions(SupportedVersion.SIOPv2_ID1) .build() const op = OP.builder() - .withPresentationSignCallback(presentationSignCallback) + .withPresentationSignCallback(sdJwtVcPresentationSignCallback) .withExpiresIn(1000) - .withHasher(hasher) + .withHasher(pexHasher) .withCreateJwtCallback(internalSignature(opMockEntity.hexPrivateKey, opMockEntity.did, `${opMockEntity.did}#controller`, SigningAlgo.ES256K)) .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) .withRegistration({ @@ -326,13 +292,13 @@ describe('RP and OP interaction should', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: getVCs(), - hasher, + hasher: pexHasher, }) const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions( parsedAuthReqURI.authorizationRequestPayload, ) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) - const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), presentationSignCallback, { + const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), sdJwtVcPresentationSignCallback, { proofOptions: { nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg', }, diff --git a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts index a3f5e364..8e7c831a 100644 --- a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts +++ b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts @@ -1,8 +1,9 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import crypto from 'crypto' +import crypto, { createHash } from 'crypto' import { JwtPayload, parseJWT, SigningAlgo, uuidv4 } from '@sphereon/oid4vc-common' +import { SdJwtDecodedVerifiableCredentialWithKbJwtInput } from '@sphereon/pex' import { IProofType } from '@sphereon/ssi-types' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -19,6 +20,7 @@ import { DiscoveryMetadataPayload, KeyCurve, KeyType, + PresentationSignCallback, ResponseIss, ResponseType, RPRegistrationMetadataPayload, @@ -281,3 +283,35 @@ export const metadata: { return assertValidMetadata(this.opMetadata, this.rpMetadata) }, } + +export const pexHasher = (data: string) => createHash('sha256').update(data).digest() + +export const sdJwtVcPresentationSignCallback: PresentationSignCallback = async (_args) => { + const presentation = _args.presentation as SdJwtDecodedVerifiableCredentialWithKbJwtInput + + // In real life scenario, the KB-JWT must be signed + // As the KB-JWT is a normal JWT, the user does not need an sd-jwt implementation in the presentation sign callback + // NOTE: should the presentation just be the KB-JWT header + payload instead of the whole decoded SD JWT? + expect(presentation.kbJwt).toEqual({ + header: { + typ: 'kb+jwt', + }, + payload: { + sd_hash: expect.any(String), + iat: expect.any(Number), + nonce: expect.any(String), + }, + }) + + const header = { + ...presentation.kbJwt.header, + alg: 'ES256K', + } + const payload = { + ...presentation.kbJwt.payload, + aud: '123', + } + + const kbJwtCompact = `${Buffer.from(JSON.stringify(header)).toString('base64url')}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}.signature` + return presentation.compactSdJwtVc + kbJwtCompact +} diff --git a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts index c46c4965..a5d1cb59 100644 --- a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts +++ b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts @@ -289,13 +289,22 @@ export class PresentationExchange { evaluationResults = pex.evaluatePresentation( definition, Array.isArray(vpPayloads) ? vpPayloads.map((wvp) => wvp.original) : vpPayloads.original, - opts, + { + ...opts, + // We always have external presentation submissions here. Some older versions of OID4VP allow for submission in presentation, + // but in that case the submission will not be provided + presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL, + }, ) } else { for (const wvp of vpPayloadsArray) { if (CredentialMapper.isWrappedW3CVerifiablePresentation(wvp) && wvp.presentation.presentation_submission) { const presentationSubmission = wvp.presentation.presentation_submission - evaluationResults = pex.evaluatePresentation(definition, wvp.original, { ...opts, presentationSubmission }) + evaluationResults = pex.evaluatePresentation(definition, wvp.original, { + ...opts, + presentationSubmission, + presentationSubmissionLocation: PresentationSubmissionLocation.PRESENTATION, + }) const submission = evaluationResults.value // Found valid submission diff --git a/packages/siop-oid4vp/package.json b/packages/siop-oid4vp/package.json index 9be3d18e..2fbb8206 100644 --- a/packages/siop-oid4vp/package.json +++ b/packages/siop-oid4vp/package.json @@ -17,7 +17,7 @@ "@astronautlabs/jsonpath": "^1.1.2", "@sphereon/did-uni-client": "^0.6.2", "@sphereon/oid4vc-common": "workspace:*", - "@sphereon/pex": "^4.1.0", + "@sphereon/pex": "4.1.1-unstable.0", "@sphereon/pex-models": "^2.3.1", "@sphereon/kmp-mdl-mdoc": "0.2.0-SNAPSHOT.22", "@sphereon/ssi-types": "0.22.0", @@ -112,4 +112,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a528e56..0809a348 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -445,8 +445,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/pex': - specifier: ^4.1.0 - version: 4.1.0 + specifier: 4.1.1-unstable.0 + version: 4.1.1-unstable.0 '@sphereon/pex-models': specifier: ^2.3.1 version: 2.3.1 @@ -2525,8 +2525,8 @@ packages: '@sphereon/pex-models@2.3.1': resolution: {integrity: sha512-SByU4cJ0XYA6VZQ/L6lsSiRcFtBPHbFioCeQ4GP7/W/jQ+PSBD7uK2oTnKQ9/0iEiMK/6JYqhKgLs4a9UX3UTQ==} - '@sphereon/pex@4.1.0': - resolution: {integrity: sha512-FVaaStVW8ewiAVfbpLmjRuAmEA/7xwifY5m1r6RaignMpPLK3uBt+Urae/Y78lcuJttj7WvRMzddgNO3RtsO+Q==} + '@sphereon/pex@4.1.1-unstable.0': + resolution: {integrity: sha512-new7Eem41l7/HY3yvTOXrSxJLRer4hBRzIb6WCDNz7RfrzhuuQzW6UqudNABoEGpIW34E9UHkz0q8gclJrxprQ==} engines: {node: '>=18'} '@sphereon/ssi-express-support@0.29.1-unstable.208': @@ -11808,7 +11808,7 @@ snapshots: '@sphereon/pex-models@2.3.1': {} - '@sphereon/pex@4.1.0': + '@sphereon/pex@4.1.1-unstable.0': dependencies: '@astronautlabs/jsonpath': 1.1.2 '@sd-jwt/decode': 0.6.1 From f9b1bdfe5681ed81bc0287f066f4f8792c36a715 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 17 Sep 2024 17:41:09 +0200 Subject: [PATCH 3/6] fix: test for nonce Signed-off-by: Timo Glastra --- .../lib/authorization-response/AuthorizationResponse.ts | 4 ++-- packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts index 6ff45798..113eed54 100644 --- a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts +++ b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts @@ -5,7 +5,7 @@ import { assertValidVerifyAuthorizationRequestOpts } from '../authorization-requ import { IDToken } from '../id-token' import { AuthorizationResponsePayload, ResponseType, SIOPErrors, VerifiedAuthorizationRequest, VerifiedAuthorizationResponse } from '../types' -import { assertValidVerifiablePresentations, extractPresentationsFromAuthorizationResponse, verifyPresentations } from './OpenID4VP' +import { assertValidVerifiablePresentations, extractNonceFromWrappedVerifiablePresentation, extractPresentationsFromAuthorizationResponse, verifyPresentations } from './OpenID4VP' import { assertValidResponseOpts } from './Opts' import { createResponsePayload } from './Payload' import { AuthorizationResponseOpts, PresentationDefinitionWithLocation, VerifyAuthorizationResponseOpts } from './types' @@ -207,7 +207,7 @@ export class AuthorizationResponse { const presentations = await extractPresentationsFromAuthorizationResponse(this, opts) // We do not verify them, as that is done elsewhere. So we simply can take the first nonce if (!nonce) { - nonce = presentations[0].decoded.nonce + nonce = extractNonceFromWrappedVerifiablePresentation(Array.isArray(presentations) ? presentations[0] : presentations) } } const idTokenPayload = await this.idToken?.payload() diff --git a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts index b79d82a6..bf1f8bd1 100644 --- a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts +++ b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts @@ -32,7 +32,7 @@ import { VPTokenLocation, } from './types' -function extractNonceFromWrappedVerifiablePresentation(wrappedVp: WrappedVerifiablePresentation): string | undefined { +export function extractNonceFromWrappedVerifiablePresentation(wrappedVp: WrappedVerifiablePresentation): string | undefined { // SD-JWT uses kb-jwt for the nonce if (CredentialMapper.isWrappedSdJwtVerifiablePresentation(wrappedVp)) { // TODO: replace this once `kbJwt.payload` is available on the decoded sd-jwt (pr in ssi-sdk) From adda8b4800ee5c48cd7435a63bad0cf65d754cef Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Tue, 17 Sep 2024 17:41:29 +0200 Subject: [PATCH 4/6] formatting Signed-off-by: Timo Glastra --- .../lib/authorization-response/AuthorizationResponse.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts index 113eed54..793609b9 100644 --- a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts +++ b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts @@ -5,7 +5,12 @@ import { assertValidVerifyAuthorizationRequestOpts } from '../authorization-requ import { IDToken } from '../id-token' import { AuthorizationResponsePayload, ResponseType, SIOPErrors, VerifiedAuthorizationRequest, VerifiedAuthorizationResponse } from '../types' -import { assertValidVerifiablePresentations, extractNonceFromWrappedVerifiablePresentation, extractPresentationsFromAuthorizationResponse, verifyPresentations } from './OpenID4VP' +import { + assertValidVerifiablePresentations, + extractNonceFromWrappedVerifiablePresentation, + extractPresentationsFromAuthorizationResponse, + verifyPresentations, +} from './OpenID4VP' import { assertValidResponseOpts } from './Opts' import { createResponsePayload } from './Payload' import { AuthorizationResponseOpts, PresentationDefinitionWithLocation, VerifyAuthorizationResponseOpts } from './types' From 15ff109023779b57c33dc844c1b7e1522576f377 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 18 Sep 2024 15:58:53 +0200 Subject: [PATCH 5/6] chore: update pex Signed-off-by: Timo Glastra --- .../siop-oid4vp/lib/helpers/Revocation.ts | 4 +- .../AuthorizationResponseOpts.schema.ts | 7 +- pnpm-lock.yaml | 638 +++++++++++++++++- 3 files changed, 626 insertions(+), 23 deletions(-) diff --git a/packages/siop-oid4vp/lib/helpers/Revocation.ts b/packages/siop-oid4vp/lib/helpers/Revocation.ts index 2c38e2de..7d8ba738 100644 --- a/packages/siop-oid4vp/lib/helpers/Revocation.ts +++ b/packages/siop-oid4vp/lib/helpers/Revocation.ts @@ -14,7 +14,8 @@ export const verifyRevocation = async ( throw new Error(`Revocation callback not provided`) } - const vcs = CredentialMapper.isWrappedSdJwtVerifiablePresentation(vpToken) ? [vpToken.vcs[0]] : vpToken.presentation.verifiableCredential + const vcs = (CredentialMapper.isWrappedSdJwtVerifiablePresentation(vpToken) || CredentialMapper.isWrappedMdocPresentation(vpToken)) + ? [vpToken.vcs[0]] : vpToken.presentation.verifiableCredential for (const vc of vcs) { if ( revocationVerification === RevocationVerification.ALWAYS || @@ -38,6 +39,7 @@ function originalTypeToVerifiableCredentialTypeFormat(original: WrappedVerifiabl jwt_vc: VerifiableCredentialTypeFormat.JWT_VC, ldp: VerifiableCredentialTypeFormat.LDP_VC, ldp_vc: VerifiableCredentialTypeFormat.LDP_VC, + mso_mdoc: VerifiableCredentialTypeFormat.MSO_MDOC } return mapping[original] diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts index abada593..2ba2e074 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts @@ -1938,6 +1938,9 @@ export const AuthorizationResponseOptsSchemaObj = { "jwt": { "type": "string" }, + "mso_mdoc": { + "type": "string" + }, "nonce": { "type": "string" }, @@ -1968,7 +1971,9 @@ export const AuthorizationResponseOptsSchemaObj = { "JcsEd25519Signature2020", "BbsBlsSignatureProof2020", "BbsBlsBoundSignatureProof2020", - "JwtProof2020" + "JwtProof2020", + "SdJwtProof2024", + "MsoMdocProof2024" ] }, "IProofPurpose": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0809a348..758cbf03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,7 +135,7 @@ importers: version: 0.23.0(expo@48.0.21(@babel/core@7.25.2)(encoding@0.1.13))(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) '@transmute/did-key.js': specifier: ^0.3.0-unstable.10 - version: 0.3.0-unstable.10(encoding@0.1.13) + version: 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@trust/keyto': specifier: ^2.0.0-alpha1 version: 2.0.0-alpha1 @@ -445,14 +445,14 @@ importers: specifier: workspace:* version: link:../common '@sphereon/pex': - specifier: 4.1.1-unstable.0 - version: 4.1.1-unstable.0 + specifier: 5.0.0-unstable.1 + version: 5.0.0-unstable.1(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) '@sphereon/pex-models': specifier: ^2.3.1 version: 2.3.1 '@sphereon/ssi-types': - specifier: 0.22.0 - version: 0.22.0 + specifier: 0.29.1-unstable.121 + version: 0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) '@sphereon/wellknown-dids-client': specifier: ^0.1.3 version: 0.1.3(encoding@0.1.13) @@ -2525,8 +2525,8 @@ packages: '@sphereon/pex-models@2.3.1': resolution: {integrity: sha512-SByU4cJ0XYA6VZQ/L6lsSiRcFtBPHbFioCeQ4GP7/W/jQ+PSBD7uK2oTnKQ9/0iEiMK/6JYqhKgLs4a9UX3UTQ==} - '@sphereon/pex@4.1.1-unstable.0': - resolution: {integrity: sha512-new7Eem41l7/HY3yvTOXrSxJLRer4hBRzIb6WCDNz7RfrzhuuQzW6UqudNABoEGpIW34E9UHkz0q8gclJrxprQ==} + '@sphereon/pex@5.0.0-unstable.1': + resolution: {integrity: sha512-pfcZnoNE9WDqFUuwXxMGXbNC0v/0MX0BhJm+LzeCdRqTVfujPT6QCuNkbsf0DJmacuvNnhwsuuDogqSSfbZZSA==} engines: {node: '>=18'} '@sphereon/ssi-express-support@0.29.1-unstable.208': @@ -2543,11 +2543,38 @@ packages: passport-http-bearer: optional: true + '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-nc0jFPOWg0H20S8m83aQUpNym0Wx0rJCGkgpH6GdK8gBtgza8Y9DvAap1AYZug18WbqPcF6rBjvtIJqAKsSvlQ==} + + '@sphereon/ssi-sdk-ext.identifier-resolution@0.24.1-unstable.112': + resolution: {integrity: sha512-VBkJjHokFNsQ0wsHUbyCysMuShTOEuK6yrvyW64uOFcB2hzq1J/wi9CewI+YRHv7mnejBlu46uYNycvOKKRcsQ==} + + '@sphereon/ssi-sdk-ext.jwt-service@0.24.1-unstable.112': + resolution: {integrity: sha512-OrBaSg5wLSehkJ4MyuyDWKD4CRIBERnJqRT0o/y5DbaCF3k02+/lN/rWP+4qwk2w192fIEAExG4L2GwZM/5PLQ==} + + '@sphereon/ssi-sdk-ext.key-manager@0.24.1-unstable.112': + resolution: {integrity: sha512-XdXV4qj+BYTZWyGHduWQxl0mxCYt5CF0Q93p4Thbm2/hjfaAC6aJi2WAXFGTIri95QVbKW1Uscob0CjNCVkWdg==} + '@sphereon/ssi-sdk-ext.key-utils@0.23.0': resolution: {integrity: sha512-BfULXvQmcUrBq2DqYxJHKnEoB2d5icu3TJ9GP2aP1WybSULTjL96Wv5r7QKgktcodKaL+F+oQ7r8sC9qBl1exw==} - '@sphereon/ssi-types@0.22.0': - resolution: {integrity: sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q==} + '@sphereon/ssi-sdk-ext.key-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-er6TwGUWxlao2lSP97r1DTFlUXcPSMsIToULOWQJp6wKbvCuvV6pN5luS0qKB/W0/TOUE5kXzFwNx086BPnPRA==} + + '@sphereon/ssi-sdk-ext.x509-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-bbx2jFoqWhW/xYABVwg3HiUo15yztPt3s+9bJtdB8n4PCjin4Nq3+vFvaHsmu70yAGkbWfsBcBVW6Y3oFtvpAA==} + + '@sphereon/ssi-sdk.agent-config@0.29.1-unstable.161': + resolution: {integrity: sha512-ZP/TjapF/Gv/AwnNr9e1U3rjyRwdLtAj4un9j1csnKcgYe9ff2fhYbe06y9mU4tfQilH69mAW4Tz1t6N5U7XbA==} + + '@sphereon/ssi-sdk.core@0.29.1-unstable.161': + resolution: {integrity: sha512-3E/KsjTywT9BzP5bMi41JVTu9nTiu2ekwNSPobF9tAJnHJv+LkjCJ59xA8jtbq/Xe4iq3xRMU17yBvpZXN2W4A==} + + '@sphereon/ssi-types@0.29.1-unstable.121': + resolution: {integrity: sha512-g5qt2cRa/9t3WrBWMneGOrwjyU2Mqdh4LlKeO6japli5bgl5D6lim91F++m1iIIhOYP4dqPpk5PLjy9XQlLyEw==} + + '@sphereon/ssi-types@0.29.1-unstable.161': + resolution: {integrity: sha512-ifMADjk6k0f97/isK/4Qw/PX6n4k+qS5k6mmmH47MTD3KMDddVghoXycsvNw7wObJdLUalHBX630ghr+u21oMg==} '@sphereon/ssi-types@0.29.1-unstable.208': resolution: {integrity: sha512-3YAFzy//BojsYN+RYoEjndWP3w5a8a3qRZi5dS0Gh6s4yMCiykqTJM1agJVeoaLce8JxFFaCWSpkzwbmJYGTaQ==} @@ -2558,6 +2585,9 @@ packages: '@sphereon/wellknown-dids-client@0.1.3': resolution: {integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA==} + '@sqltools/formatter@1.2.5': + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + '@stablelib/aead@1.0.1': resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} @@ -2742,6 +2772,9 @@ packages: resolution: {integrity: sha512-y4lPzk/SY/Cy1dUCa17ES3kqvShNQwevTO16dvbuevu6YcTYBAdSCYvW9JL+ppFqPYI5NSDPUwT6kkd4wNWmsA==} engines: {node: '>=16'} + '@trust/keyto@1.0.1': + resolution: {integrity: sha512-OXTmKkrnkwktCX86XA7eWs1TQ6u64enm0syzAfNhjigbuGLy5aLhbhRYWtjt4zzdG/irWudluheRZ9Ic9pCwsA==} + '@trust/keyto@2.0.0-alpha1': resolution: {integrity: sha512-VmlOa+nOaDzhEUfprnVp7RxFQyuEwA4fJ5+smnsud5WM01gU16yQnO/ejZnDVMGXuq/sUwTa5pCej4JhkKA5Sg==} @@ -2983,6 +3016,12 @@ packages: '@veramo/core@4.2.0': resolution: {integrity: sha512-HIqbXfCbwOAJelR5Ohsm22vr63cy6ND8Ua/+9wfMDAiymUUS7NryaJ/v6NRtnmIrNZqUMDdR9/TWdp4cCq5eBg==} + '@veramo/key-manager@4.2.0': + resolution: {integrity: sha512-v/swPrxxI155iFxWjcJDmeyfMLOnAu/VRxJJE+cv8Ld9mmPi5xljaoO9/ozt0j4Cz92n6lFKqfVOxs2ECV85UA==} + + '@veramo/utils@4.2.0': + resolution: {integrity: sha512-jHkli0Qz9rFsWzPAdfJP3P2MFxvVMZPDXZvtVBm8x1fjAGrw/Htz/c5drhDAeBXnqPd9011/7cyvp6AOvdbc8Q==} + '@xmldom/xmldom@0.7.13': resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} engines: {node: '>=10.0.0'} @@ -3129,6 +3168,10 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + appdirsjs@1.2.7: resolution: {integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==} @@ -3226,6 +3269,9 @@ packages: asmcrypto.js@2.3.2: resolution: {integrity: sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==} + asn1.js-rfc5280@3.0.0: + resolution: {integrity: sha512-Y2LZPOWeZ6qehv698ZgOGGCZXBQShObWnGthTrIFlIQjuV1gg2B8QOhWFRExq/MR1VnPpIIe7P9vX2vElxv+Pg==} + asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} @@ -3391,6 +3437,9 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} @@ -3494,6 +3543,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cacache@15.3.0: resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} engines: {node: '>= 10'} @@ -4141,6 +4194,9 @@ packages: deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -5155,6 +5211,11 @@ packages: engines: {node: '>=4.0'} hasBin: true + image-size@2.0.0-beta.2: + resolution: {integrity: sha512-1nDNnVxJixMWBynFgQ1q8+aVqK60TiNHpMyFAXt9xpzGZV+2lHI1IXjgdcAjBxPc4nx2ed1NdYs2I+Zfq+Zn7w==} + engines: {node: '>=18.18.0'} + hasBin: true + import-fresh@2.0.0: resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} engines: {node: '>=4'} @@ -5685,12 +5746,45 @@ packages: js-binary-schema-parser@2.0.3: resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} + js-crypto-aes@1.0.6: + resolution: {integrity: sha512-E2hu9z5+YtpDg9Un/bDfmH+I5dv/8aN+ozxv9L0ybZldcQ9T5iYDbBKdlKGBUKI3IvzoWSBSdnZnhwZaRIN46w==} + + js-crypto-ec@1.0.7: + resolution: {integrity: sha512-vou6cW3wGAQ75RzS++I/rthELPFp0nhHCmaAKQvdhwD480Q3FltLgyNkTMgcLTdN+Ghj8BRU1/+3oIEIOOK/MA==} + + js-crypto-env@1.0.5: + resolution: {integrity: sha512-8/UNN3sG8J+yMzqwSNVaobaWhIz4MqZFoOg5OB0DFXqS8eFjj2YvdmLJqIWXPl57Yw10SvYx0DQOtkfsWIV9Aw==} + + js-crypto-hash@1.0.7: + resolution: {integrity: sha512-GdbcVKjplbXJdR9oF2ks8+sBCLD7BUZ144Bc+Ie8OJuBHSIiHyMzdg2eD+ZYf87awTsKckNn1xIv+31+V2ewcA==} + + js-crypto-hmac@1.0.7: + resolution: {integrity: sha512-OVn2wjAuOV7ToQYvRKY2VoElCHoRW7BepycPPuH73xbLygDczkef41YsXMpKLnVAyS5kdwMJQy9qlMR9touHTg==} + + js-crypto-key-utils@1.0.7: + resolution: {integrity: sha512-8/y/hpKevnAgr5EXz2x4IXMfqjzYZAzzXXc9OnAyI5JNdUtAufJkGfwlmZ+o40lTHv3k1egCiP/6pG/dZiqiEA==} + + js-crypto-pbkdf@1.0.7: + resolution: {integrity: sha512-FGs1PZeqGWM8k8k5JlAhHbBhLYtls+iVmeJEC22DUJ98Q3qo9Ki4cu3i0oxhjA2VpZ8V4MmV1DJHDTFYY4iOwg==} + + js-crypto-random@1.0.5: + resolution: {integrity: sha512-WydEQ5rrWLzgSkX1QNsuGinkv7z57UkYnDGo5f5oGtBe9QeUWUehdmPNNG4a4Sf8xGkjZBOhKaZqT1ACnyYCBA==} + + js-crypto-rsa@1.0.7: + resolution: {integrity: sha512-HLBCWNGzuUZMNbZ3nndrVAqth1m1mvuCO4tW7PpBDn4nsdLSnPnPd+SA7NvjsufWry38DnZdpFrK2gqbsrksGw==} + + js-encoding-utils@0.7.3: + resolution: {integrity: sha512-cfjcyPOzkZ2esMAi6eAjuto7GiT6YpPan5xIeQyN/CFqFHTt1sdqP0PJPgzi3HqAqXKN9j9hduynkgwk+AAJOw==} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-x509-utils@1.0.7: + resolution: {integrity: sha512-IDB3CtWyvkNJVbDPZvzM9o3Y6CyzDiMls6R23ZPwfmHHil7nRrpLxtA098SENhqjv1t/6WTeeCKQ5dhIMOGiUw==} + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -5804,6 +5898,10 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + just-diff-apply@5.5.0: resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} @@ -6316,6 +6414,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} @@ -6353,6 +6456,9 @@ packages: resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} + multiformats@9.7.1: + resolution: {integrity: sha512-TaVmGEBt0fhxiNJMGphBfB+oGvUxFs8KgGvgl8d3C+GWtrFcvXdJ2196eg+dYhmSFClmgFfSfJEklo+SZzdNuw==} + multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -6932,6 +7038,10 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} + pkijs@3.2.4: + resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==} + engines: {node: '>=12.0.0'} + plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -7227,6 +7337,9 @@ packages: resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} engines: {node: '>=6'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} @@ -7494,6 +7607,9 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true + sha3@2.1.4: + resolution: {integrity: sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==} + shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -8146,6 +8262,64 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typeorm@0.3.20: + resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==} + engines: {node: '>=16.13.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.8.0 + mssql: ^9.1.1 || ^10.0.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -8586,6 +8760,11 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -11808,20 +11987,40 @@ snapshots: '@sphereon/pex-models@2.3.1': {} - '@sphereon/pex@4.1.1-unstable.0': + '@sphereon/pex@5.0.0-unstable.1(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': dependencies: '@astronautlabs/jsonpath': 1.1.2 '@sd-jwt/decode': 0.6.1 '@sd-jwt/present': 0.6.1 '@sd-jwt/types': 0.6.1 '@sphereon/pex-models': 2.3.1 - '@sphereon/ssi-types': 0.22.0 + '@sphereon/ssi-types': 0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) ajv: 8.17.1 ajv-formats: 2.1.1(ajv@8.17.1) jwt-decode: 3.1.2 nanoid: 3.3.7 string.prototype.matchall: 4.0.11 uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver '@sphereon/ssi-express-support@0.29.1-unstable.208': dependencies: @@ -11841,6 +12040,120 @@ snapshots: transitivePeerDependencies: - supports-color + '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': + dependencies: + '@ethersproject/networks': 5.7.1 + '@ethersproject/transactions': 5.7.0 + '@sphereon/did-uni-client': 0.6.3(encoding@0.1.13) + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-sdk.core': 0.29.1-unstable.161(encoding@0.1.13) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + did-jwt: 6.11.6 + did-resolver: 4.1.0 + elliptic: 6.5.7 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.identifier-resolution@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': + dependencies: + '@sphereon/ssi-sdk-ext.did-utils': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + debug: 4.3.6 + pkijs: 3.2.4 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.jwt-service@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': + dependencies: + '@sphereon/ssi-sdk-ext.did-utils': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.identifier-resolution': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.key-manager': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + debug: 4.3.6 + jwt-decode: 4.0.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.key-manager@0.24.1-unstable.112': + dependencies: + '@veramo/core': 4.2.0 + '@veramo/key-manager': 4.2.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + '@sphereon/ssi-sdk-ext.key-utils@0.23.0(expo@48.0.21(@babel/core@7.25.2)(encoding@0.1.13))(msrcrypto@1.5.8)(react-native-securerandom@1.0.1)': dependencies: '@ethersproject/random': 5.7.0 @@ -11866,10 +12179,115 @@ snapshots: - react-native-securerandom - supports-color - '@sphereon/ssi-types@0.22.0': + '@sphereon/ssi-sdk-ext.key-utils@0.24.1-unstable.112': + dependencies: + '@ethersproject/random': 5.7.0 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@stablelib/ed25519': 1.0.3 + '@stablelib/sha256': 1.0.1 + '@stablelib/sha512': 1.0.1 + '@trust/keyto': 1.0.1 + '@veramo/core': 4.2.0 + base64url: 3.0.1 + debug: 4.3.6 + did-resolver: 4.1.0 + elliptic: 6.5.7 + lodash.isplainobject: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + varint: 6.0.0 + web-encoding: 1.1.5 + transitivePeerDependencies: + - supports-color + + '@sphereon/ssi-sdk-ext.x509-utils@0.24.1-unstable.112': + dependencies: + '@trust/keyto': 1.0.1 + debug: 4.3.6 + js-x509-utils: 1.0.7 + pkijs: 3.2.4 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@sphereon/ssi-sdk.agent-config@0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': + dependencies: + '@veramo/core': 4.2.0 + debug: 4.3.6 + jsonpointer: 5.0.1 + typeorm: 0.3.20(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + url-parse: 1.5.10 + yaml: 2.5.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk.core@0.29.1-unstable.161(encoding@0.1.13)': + dependencies: + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + cross-fetch: 3.1.8(encoding@0.1.13) + debug: 4.3.6 + image-size: 2.0.0-beta.2 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@sphereon/ssi-types@0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5))': dependencies: '@sd-jwt/decode': 0.6.1 + '@sphereon/kmp-mdl-mdoc': 0.2.0-SNAPSHOT.22 + '@sphereon/ssi-sdk-ext.jwt-service': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)) + debug: 4.3.6 + events: 3.3.0 jwt-decode: 3.1.2 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-types@0.29.1-unstable.161': + dependencies: + '@sd-jwt/decode': 0.6.1 + debug: 4.3.6 + events: 3.3.0 + jwt-decode: 3.1.2 + transitivePeerDependencies: + - supports-color '@sphereon/ssi-types@0.29.1-unstable.208': dependencies: @@ -11893,6 +12311,8 @@ snapshots: transitivePeerDependencies: - encoding + '@sqltools/formatter@1.2.5': {} + '@stablelib/aead@1.0.1': {} '@stablelib/aes-kw@1.0.1': @@ -12012,7 +12432,7 @@ snapshots: '@transmute/did-context@0.6.1-unstable.37': {} - '@transmute/did-key-bls12381@0.3.0-unstable.10(encoding@0.1.13)': + '@transmute/did-key-bls12381@0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3)': dependencies: '@transmute/bls12381-key-pair': 0.7.0-unstable.81(encoding@0.1.13) '@transmute/did-key-common': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) @@ -12040,7 +12460,7 @@ snapshots: - encoding - web-streams-polyfill - '@transmute/did-key-secp256k1@0.3.0-unstable.10(encoding@0.1.13)': + '@transmute/did-key-secp256k1@0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3)': dependencies: '@transmute/did-key-common': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@transmute/secp256k1-key-pair': 0.7.0-unstable.82 @@ -12050,7 +12470,7 @@ snapshots: '@transmute/did-key-test-vectors@0.3.0-unstable.10': {} - '@transmute/did-key-web-crypto@0.3.0-unstable.10(encoding@0.1.13)': + '@transmute/did-key-web-crypto@0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3)': dependencies: '@transmute/did-key-common': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@transmute/web-crypto-key-pair': 0.7.0-unstable.82 @@ -12058,7 +12478,7 @@ snapshots: - encoding - web-streams-polyfill - '@transmute/did-key-x25519@0.3.0-unstable.10(encoding@0.1.13)': + '@transmute/did-key-x25519@0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3)': dependencies: '@transmute/did-key-common': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@transmute/x25519-key-pair': 0.7.0-unstable.82 @@ -12066,14 +12486,14 @@ snapshots: - encoding - web-streams-polyfill - '@transmute/did-key.js@0.3.0-unstable.10(encoding@0.1.13)': + '@transmute/did-key.js@0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3)': dependencies: - '@transmute/did-key-bls12381': 0.3.0-unstable.10(encoding@0.1.13) + '@transmute/did-key-bls12381': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@transmute/did-key-ed25519': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) - '@transmute/did-key-secp256k1': 0.3.0-unstable.10(encoding@0.1.13) + '@transmute/did-key-secp256k1': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) '@transmute/did-key-test-vectors': 0.3.0-unstable.10 - '@transmute/did-key-web-crypto': 0.3.0-unstable.10(encoding@0.1.13) - '@transmute/did-key-x25519': 0.3.0-unstable.10(encoding@0.1.13) + '@transmute/did-key-web-crypto': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) + '@transmute/did-key-x25519': 0.3.0-unstable.10(encoding@0.1.13)(web-streams-polyfill@3.3.3) base64url: 3.0.1 transitivePeerDependencies: - encoding @@ -12197,6 +12617,12 @@ snapshots: '@stablelib/x25519': 1.0.3 '@transmute/ld-key-pair': 0.7.0-unstable.82 + '@trust/keyto@1.0.1': + dependencies: + asn1.js: 5.4.1 + base64url: 3.0.1 + elliptic: 6.5.7 + '@trust/keyto@2.0.0-alpha1': dependencies: asn1.js: 5.4.1 @@ -12570,6 +12996,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@veramo/key-manager@4.2.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + did-jwt: 6.11.6 + uint8arrays: 3.1.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@veramo/utils@4.2.0(encoding@0.1.13)': + dependencies: + '@ethersproject/transactions': 5.7.0 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + blakejs: 1.2.1 + cross-fetch: 3.1.8(encoding@0.1.13) + debug: 4.3.6 + did-jwt: 6.11.6 + did-jwt-vc: 3.2.15 + did-resolver: 4.1.0 + elliptic: 6.5.7 + multiformats: 9.7.1 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + '@xmldom/xmldom@0.7.13': {} '@xmldom/xmldom@0.8.10': {} @@ -12702,6 +13159,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + app-root-path@3.1.0: {} + appdirsjs@1.2.7: {} application-config-path@0.1.1: {} @@ -12799,6 +13258,10 @@ snapshots: asmcrypto.js@2.3.2: {} + asn1.js-rfc5280@3.0.0: + dependencies: + asn1.js: 5.4.1 + asn1.js@5.4.1: dependencies: bn.js: 4.12.0 @@ -13049,6 +13512,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + blakejs@1.2.1: {} + blueimp-md5@2.19.0: {} bn.js@4.12.0: {} @@ -13165,6 +13630,8 @@ snapshots: bytes@3.1.2: {} + bytestreamjs@2.0.1: {} + cacache@15.3.0: dependencies: '@npmcli/fs': 1.1.1 @@ -13939,6 +14406,11 @@ snapshots: deprecation@2.3.1: {} + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + destroy@1.2.0: {} detect-indent@5.0.0: {} @@ -15322,6 +15794,8 @@ snapshots: image-size@0.6.3: {} + image-size@2.0.0-beta.2: {} + import-fresh@2.0.0: dependencies: caller-path: 2.0.0 @@ -16116,10 +16590,87 @@ snapshots: js-binary-schema-parser@2.0.3: {} + js-crypto-aes@1.0.6: + dependencies: + js-crypto-env: 1.0.5 + + js-crypto-ec@1.0.7: + dependencies: + asn1.js: 5.4.1 + buffer: 6.0.3 + elliptic: 6.5.7 + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + + js-crypto-env@1.0.5: {} + + js-crypto-hash@1.0.7: + dependencies: + buffer: 6.0.3 + hash.js: 1.1.7 + js-crypto-env: 1.0.5 + md5: 2.3.0 + sha3: 2.1.4 + + js-crypto-hmac@1.0.7: + dependencies: + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + + js-crypto-key-utils@1.0.7: + dependencies: + asn1.js: 5.4.1 + buffer: 6.0.3 + des.js: 1.1.0 + elliptic: 6.5.7 + js-crypto-aes: 1.0.6 + js-crypto-hash: 1.0.7 + js-crypto-pbkdf: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + lodash.clonedeep: 4.5.0 + + js-crypto-pbkdf@1.0.7: + dependencies: + js-crypto-hash: 1.0.7 + js-crypto-hmac: 1.0.7 + js-encoding-utils: 0.7.3 + + js-crypto-random@1.0.5: + dependencies: + js-crypto-env: 1.0.5 + + js-crypto-rsa@1.0.7: + dependencies: + bn.js: 5.2.1 + buffer: 6.0.3 + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + + js-encoding-utils@0.7.3: {} + js-sha3@0.8.0: {} js-tokens@4.0.0: {} + js-x509-utils@1.0.7: + dependencies: + asn1.js: 5.4.1 + asn1.js-rfc5280: 3.0.0 + bn.js: 5.2.1 + buffer: 6.0.3 + js-crypto-ec: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-crypto-rsa: 1.0.7 + js-encoding-utils: 0.7.3 + js-yaml@3.14.1: dependencies: argparse: 1.0.10 @@ -16259,6 +16810,8 @@ snapshots: jsonparse@1.3.1: {} + jsonpointer@5.0.1: {} + just-diff-apply@5.5.0: {} just-diff@6.0.2: {} @@ -17115,6 +17668,8 @@ snapshots: mkdirp@1.0.4: {} + mkdirp@2.1.6: {} + mkdirp@3.0.1: {} modify-values@1.0.1: {} @@ -17145,6 +17700,8 @@ snapshots: multiformats@12.1.3: {} + multiformats@9.7.1: {} + multiformats@9.9.0: {} multimatch@5.0.0: @@ -17821,6 +18378,15 @@ snapshots: dependencies: find-up: 3.0.0 + pkijs@3.2.4: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.5 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.5 + pvutils: 1.1.3 + tslib: 2.7.0 + plist@3.1.0: dependencies: '@xmldom/xmldom': 0.8.10 @@ -18165,6 +18731,8 @@ snapshots: reduce-flatten@2.0.0: optional: true + reflect-metadata@0.2.2: {} + regenerate-unicode-properties@10.1.1: dependencies: regenerate: 1.4.2 @@ -18432,6 +19000,10 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + sha3@2.1.4: + dependencies: + buffer: 6.0.3 + shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 @@ -19176,6 +19748,28 @@ snapshots: typedarray@0.0.6: {} + typeorm@0.3.20(ts-node@10.9.2(@types/node@18.19.47)(typescript@5.4.5)): + dependencies: + '@sqltools/formatter': 1.2.5 + app-root-path: 3.1.0 + buffer: 6.0.3 + chalk: 4.1.2 + cli-highlight: 2.1.11 + dayjs: 1.11.13 + debug: 4.3.6 + dotenv: 16.4.5 + glob: 10.4.5 + mkdirp: 2.1.6 + reflect-metadata: 0.2.2 + sha.js: 2.4.11 + tslib: 2.7.0 + uuid: 9.0.1 + yargs: 17.7.2 + optionalDependencies: + ts-node: 10.9.2(@types/node@18.19.47)(typescript@5.4.5) + transitivePeerDependencies: + - supports-color + typescript@5.3.3: {} typescript@5.4.5: {} @@ -19544,6 +20138,8 @@ snapshots: yallist@4.0.0: {} + yaml@2.5.1: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 From c72bed9d00babcbb667ed4f86b33d57e4670a13c Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 18 Sep 2024 17:50:19 +0200 Subject: [PATCH 6/6] chore: update Signed-off-by: Timo Glastra --- packages/siop-oid4vp/lib/__tests__/TestUtils.ts | 4 ++-- packages/siop-oid4vp/lib/op/OP.ts | 2 +- packages/siop-oid4vp/package.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts index 8e7c831a..b6afe293 100644 --- a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts +++ b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts @@ -3,7 +3,6 @@ import crypto, { createHash } from 'crypto' import { JwtPayload, parseJWT, SigningAlgo, uuidv4 } from '@sphereon/oid4vc-common' -import { SdJwtDecodedVerifiableCredentialWithKbJwtInput } from '@sphereon/pex' import { IProofType } from '@sphereon/ssi-types' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -40,6 +39,7 @@ import { VERIFIERZ_PURPOSE_TO_VERIFY, VERIFIERZ_PURPOSE_TO_VERIFY_NL, } from './data/mockedData' +import { PartialSdJwtDecodedVerifiableCredential } from '@sphereon/pex/dist/main/lib' export interface TESTKEY { key: JWK @@ -287,7 +287,7 @@ export const metadata: { export const pexHasher = (data: string) => createHash('sha256').update(data).digest() export const sdJwtVcPresentationSignCallback: PresentationSignCallback = async (_args) => { - const presentation = _args.presentation as SdJwtDecodedVerifiableCredentialWithKbJwtInput + const presentation = _args.presentation as PartialSdJwtDecodedVerifiableCredential // In real life scenario, the KB-JWT must be signed // As the KB-JWT is a normal JWT, the user does not need an sd-jwt implementation in the presentation sign callback diff --git a/packages/siop-oid4vp/lib/op/OP.ts b/packages/siop-oid4vp/lib/op/OP.ts index c55e59fb..ad751e61 100644 --- a/packages/siop-oid4vp/lib/op/OP.ts +++ b/packages/siop-oid4vp/lib/op/OP.ts @@ -178,7 +178,7 @@ export class OP { if (!responseUri) { throw Error('No response URI present') } - const authResponseAsURI = encodeJsonAsURI(payload, { arraysWithIndex: ['presentation_submission', 'vp_token'] }) + const authResponseAsURI = encodeJsonAsURI(payload, { arraysWithIndex: ['presentation_submission'] }) return post(responseUri, authResponseAsURI, { contentType: ContentType.FORM_URL_ENCODED, exceptionOnHttpErrorStatus: true }) .then((result: SIOPResonse) => { void this.emitEvent(AuthorizationEvents.ON_AUTH_RESPONSE_SENT_SUCCESS, { correlationId, subject: response }) diff --git a/packages/siop-oid4vp/package.json b/packages/siop-oid4vp/package.json index 2fbb8206..92272ce6 100644 --- a/packages/siop-oid4vp/package.json +++ b/packages/siop-oid4vp/package.json @@ -17,10 +17,10 @@ "@astronautlabs/jsonpath": "^1.1.2", "@sphereon/did-uni-client": "^0.6.2", "@sphereon/oid4vc-common": "workspace:*", - "@sphereon/pex": "4.1.1-unstable.0", + "@sphereon/pex": "5.0.0-unstable.1", "@sphereon/pex-models": "^2.3.1", "@sphereon/kmp-mdl-mdoc": "0.2.0-SNAPSHOT.22", - "@sphereon/ssi-types": "0.22.0", + "@sphereon/ssi-types": "0.29.1-unstable.121", "@sphereon/wellknown-dids-client": "^0.1.3", "cross-fetch": "^4.0.0", "debug": "^4.3.5",