Skip to content

Commit

Permalink
Merge pull request #174 from Sphereon-Opensource/feature/SPHEREON-118…
Browse files Browse the repository at this point in the history
…3_sd-jwt-confomance

Feature/sphereon 1183 sd jwt confomance
  • Loading branch information
sanderPostma authored Nov 11, 2024
2 parents 9596f9d + 87da1d8 commit a5d5107
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 13 deletions.
33 changes: 31 additions & 2 deletions lib/evaluation/handlers/limitDisclosureEvaluationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
WrappedVerifiableCredential,
} from '@sphereon/ssi-types';

import { ClaimValue } from '../../../test/types';
import { Status } from '../../ConstraintUtils';
import { IInternalPresentationDefinition, InputDescriptorWithIndex, PathComponent } from '../../types';
import PexMessages from '../../types/Messages';
Expand Down Expand Up @@ -139,15 +140,43 @@ export class LimitDisclosureEvaluationHandler extends AbstractEvaluationHandler
// Can be nested array / object
const presentationFrame: SdJwtPresentationFrame = {};

const processNestedObject = (obj: ClaimValue, currentPath: PathComponent[], basePath: PathComponent[]) => {
if (obj === null || typeof obj !== 'object') {
// For literal values, set the path to true in the presentation frame
JsonPathUtils.setValue(presentationFrame, currentPath, true);
return;
}

// For arrays, process each element
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
processNestedObject(item, [...currentPath, index], basePath);
});
return;
}

// For objects, process each child property
Object.entries(obj).forEach(([key, value]) => {
processNestedObject(value, [...currentPath, key], basePath);
});
};

for (const { inputDescriptor, inputDescriptorIndex } of inputDescriptors) {
for (const field of inputDescriptor.constraints?.fields ?? []) {
if (field.path) {
const inputField = JsonPathUtils.extractInputField(vc.decodedPayload, field.path);

// We set the value to true at the path in the presentation frame,
if (inputField.length > 0) {
const selectedField = inputField[0];
JsonPathUtils.setValue(presentationFrame, selectedField.path, true);
const fieldValue = JsonPathUtils.getValue<ClaimValue>(vc.decodedPayload, selectedField.path);

if (fieldValue !== null && typeof fieldValue === 'object') {
// For objects, recursively process all nested fields
processNestedObject(fieldValue, selectedField.path, selectedField.path);
} else {
// For literal values, just set the path to true
JsonPathUtils.setValue(presentationFrame, selectedField.path, true);
}
} else {
this.createMandatoryFieldNotFoundResult(inputDescriptorIndex, vcIndex, field.path);
return undefined;
Expand Down
9 changes: 9 additions & 0 deletions lib/utils/jsonPathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ export class JsonPathUtils {
return obj;
}

public static getValue<T = unknown>(obj: InputFieldType, path: string | PathComponent[]): T | undefined {
try {
const stringPath = typeof path === 'string' ? path : jp.stringify(path);
return jp.value(obj, stringPath) as T;
} catch (error) {
return undefined;
}
}

private static copyResultPathToDestinationDefinition(pathDetails: (string | number)[], pd: IPresentationDefinition, newPropertyName: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let objectCursor: any = pd;
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/sdJwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function applySdJwtLimitDisclosure(
sdJwtDecodedVerifiableCredential: SdJwtDecodedVerifiableCredential,
presentationFrame: SdJwtPresentationFrame,
) {
const SerializedDisclosures = sdJwtDecodedVerifiableCredential.disclosures.map((d) => ({
const serializedDisclosures = sdJwtDecodedVerifiableCredential.disclosures.map((d) => ({
digest: d.digest,
encoded: d.encoded,
salt: d.decoded[0],
Expand All @@ -39,7 +39,7 @@ export function applySdJwtLimitDisclosure(
const requiredDisclosures = selectDisclosures(
ObjectUtils.cloneDeep(sdJwtDecodedVerifiableCredential.signedPayload),
// Map to sd-jwt disclosure format
SerializedDisclosures,
serializedDisclosures,
presentationFrame as PresentationFrame<Record<string, unknown>>,
);

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sphereon/pex",
"version": "5.0.0-unstable.18",
"version": "5.0.0-unstable.19",
"description": "A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification",
"main": "dist/main/index.js",
"module": "dist/module/index.js",
Expand Down
54 changes: 54 additions & 0 deletions test/dif_pe_examples/pdV2/pd-sd-jwt-vp-funke.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"presentation_definition": {
"id": "pid-sdjwt",
"name": "Identity verification SD-JWT",
"purpose": "We need to verify your identity information.",
"format": {
"vc+sd-jwt": {
"sd-jwt_alg_values": [
"ES256",
"ES384",
"ES512",
"EdDSA"
]
}
},
"input_descriptors": [
{
"id": "6aa0ac61-6535-46c2-924d-65fccfe65f4f",
"constraints": {
"limit_disclosure": "required",
"fields": [
{
"path": [
"$.place_of_birth"
]
},
{
"path": [
"$.given_name"
]
},
{
"path": [
"$.family_name"
]
},
{
"path": [
"$.address"
]
},
{
"path": [
"$.age_equal_or_over.18"
]
}
]
},
"name": "Identity",
"purpose": "We need your identity information."
}
]
}
}
1 change: 1 addition & 0 deletions test/dif_pe_examples/vc/vc-funke-pid-sd.jwt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJ4NWMiOlsiTUlJQ2REQ0NBaHVnQXdJQkFnSUJBakFLQmdncWhrak9QUVFEQWpDQmlERUxNQWtHQTFVRUJoTUNSRVV4RHpBTkJnTlZCQWNNQmtKbGNteHBiakVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneEVUQVBCZ05WQkFzTUNGUWdRMU1nU1VSRk1UWXdOQVlEVlFRRERDMVRVRkpKVGtRZ1JuVnVhMlVnUlZWRVNTQlhZV3hzWlhRZ1VISnZkRzkwZVhCbElFbHpjM1ZwYm1jZ1EwRXdIaGNOTWpRd05UTXhNRGd4TXpFM1doY05NalV3TnpBMU1EZ3hNekUzV2pCc01Rc3dDUVlEVlFRR0V3SkVSVEVkTUJzR0ExVUVDZ3dVUW5WdVpHVnpaSEoxWTJ0bGNtVnBJRWR0WWtneENqQUlCZ05WQkFzTUFVa3hNakF3QmdOVkJBTU1LVk5RVWtsT1JDQkdkVzVyWlNCRlZVUkpJRmRoYkd4bGRDQlFjbTkwYjNSNWNHVWdTWE56ZFdWeU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRU9GQnE0WU1LZzR3NWZUaWZzeXR3QnVKZi83RTdWaFJQWGlObTUyUzNxMUVUSWdCZFh5REsza1Z4R3hnZUhQaXZMUDN1dU12UzZpREVjN3FNeG12ZHVLT0JrRENCalRBZEJnTlZIUTRFRmdRVWlQaENrTEVyRFhQTFcyL0owV1ZlZ2h5dyttSXdEQVlEVlIwVEFRSC9CQUl3QURBT0JnTlZIUThCQWY4RUJBTUNCNEF3TFFZRFZSMFJCQ1l3SklJaVpHVnRieTV3YVdRdGFYTnpkV1Z5TG1KMWJtUmxjMlJ5ZFdOclpYSmxhUzVrWlRBZkJnTlZIU01FR0RBV2dCVFVWaGpBaVRqb0RsaUVHTWwyWXIrcnU4V1F2akFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUFiZjVUemtjUXpoZldvSW95aTFWTjdkOEk5QnNGS20xTVdsdVJwaDJieUdRSWdLWWtkck5mMnhYUGpWU2JqVy9VLzVTNXZBRUM1WHhjT2FudXNPQnJvQmJVPSIsIk1JSUNlVENDQWlDZ0F3SUJBZ0lVQjVFOVFWWnRtVVljRHRDaktCL0gzVlF2NzJnd0NnWUlLb1pJemowRUF3SXdnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUI0WERUSTBNRFV6TVRBMk5EZ3dPVm9YRFRNME1EVXlPVEEyTkRnd09Wb3dnWWd4Q3pBSkJnTlZCQVlUQWtSRk1ROHdEUVlEVlFRSERBWkNaWEpzYVc0eEhUQWJCZ05WQkFvTUZFSjFibVJsYzJSeWRXTnJaWEpsYVNCSGJXSklNUkV3RHdZRFZRUUxEQWhVSUVOVElFbEVSVEUyTURRR0ExVUVBd3d0VTFCU1NVNUVJRVoxYm10bElFVlZSRWtnVjJGc2JHVjBJRkJ5YjNSdmRIbHdaU0JKYzNOMWFXNW5JRU5CTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWUd6ZHdGRG5jNytLbjVpYkF2Q09NOGtlNzdWUXhxZk1jd1pMOElhSUErV0NST2NDZm1ZL2dpSDkycU1ydTVwL2t5T2l2RTBSQy9JYmRNT052RG9VeWFObU1HUXdIUVlEVlIwT0JCWUVGTlJXR01DSk9PZ09XSVFZeVhaaXY2dTd4WkMrTUI4R0ExVWRJd1FZTUJhQUZOUldHTUNKT09nT1dJUVl5WFppdjZ1N3haQytNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3RGdZRFZSMFBBUUgvQkFRREFnR0dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR0VtN3drWktIdC9hdGI0TWRGblhXNnlybndNVVQydTEzNmdkdGwxMFk2aEFpQnVURnF2Vll0aDFyYnh6Q1AweFdaSG1RSzlrVnl4bjhHUGZYMjdFSXp6c3c9PSJdLCJraWQiOiJNSUdVTUlHT3BJR0xNSUdJTVFzd0NRWURWUVFHRXdKRVJURVBNQTBHQTFVRUJ3d0dRbVZ5YkdsdU1SMHdHd1lEVlFRS0RCUkNkVzVrWlhOa2NuVmphMlZ5WldrZ1IyMWlTREVSTUE4R0ExVUVDd3dJVkNCRFV5QkpSRVV4TmpBMEJnTlZCQU1NTFZOUVVrbE9SQ0JHZFc1clpTQkZWVVJKSUZkaGJHeGxkQ0JRY205MGIzUjVjR1VnU1hOemRXbHVaeUJEUVFJQkFnPT0iLCJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFUzI1NiJ9.eyJwbGFjZV9vZl9iaXJ0aCI6eyJfc2QiOlsiNkJQYWczZ0pwZ1JLLU1DdXlLSXZ5Q3B2RHdrdFZoZG1SMm9GWnF0UDJQVSJdfSwiX3NkIjpbIi1tdXhPS01BUHREUWl0WW1YcWJxZVhSTmc4ckJBbHNmZUNZYlhVRWY5WTQiLCJIRHdYOFVWREF0RVdvRUlIbER1eVFMQURoWXRlUHVPTFJGM2VnaHhLcmJRIiwiZENnbVpkTWNMalJtM0d2Rm0wb211NjcyMVlYbktiZ05lLXhDN25heHoybyIsImc1akowdktRRFJqaTAxNm5vcVc3Z293cTlXVUxpMU9JaWlLUVJpZXlZbjQiLCJzY1l6aVFTU2JJeUxpM0lUazd1Q0dFc3Jua3BsODZpZGFhRUFZN3IycUNnIiwieHJmTmRLcXF2TDU5SjZiQ1BicjctRXZuS0JZVm9Hc3FkVk5MZlhhMDMzNCIsInlJbWtEYm9SUE5sUHBBWDJFTEM1SURJNlpNbDdOVXloblRQXzJTaUItR00iXSwiYWRkcmVzcyI6eyJfc2QiOlsiTlc3S3lQY2hFVzBfV1hwWi1yNHlia0dRNjBOLVlYdktuYUFmZks0VTFHTSIsIllsT0dvT1NielJhVmhvQUFlTjRTTzBSZ0p1THNPcHdPSG5jLVBsOEtyWlEiLCJhOFA4SHk0QjRPV19TYm1JUlFhMzhfOUh5bXU5ZFN5TWZZekt4S1d1SGJVIiwiY19hMXhBLUVwS3M5X0ZaMF9uUXJJa3czR2FwWlpBNjVQOTJFUHFtUkJxSSJdfSwiaXNzdWluZ19jb3VudHJ5IjoiREUiLCJ2Y3QiOiJodHRwczovL2V4YW1wbGUuYm1pLmJ1bmQuZGUvY3JlZGVudGlhbC9waWQvMS4wIiwiaXNzdWluZ19hdXRob3JpdHkiOiJERSIsIl9zZF9hbGciOiJzaGEtMjU2IiwiaXNzIjoiaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL2MiLCJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJraWQiOiJ0OG9KZUQzRmY2VWV2bThVZDJUTG80RGtfOHRtUTJOQkdrQ3Z3eENDOHJBIiwieCI6InFCdWVJWDFrcnVlTURMWmtRd0xEcElTWTBkWURkNEIzVm12Z0NxcXVlQzQiLCJ5IjoiWGYwQzl4a1NJalFpUmU1VV9JVnFWb3RQU3cydG4xLU16VnhQNnZWeVBsNCIsImFsZyI6IkVTMjU2In19LCJleHAiOjE3MzA5ODY2MzksImlhdCI6MTcyOTc3NzAzOSwiYWdlX2VxdWFsX29yX292ZXIiOnsiX3NkIjpbIi1HWThEMzJGb0xCeWhlZzFWcC1WMEg2Y2UtLU15dEZKWmk5cEVzdXd1bmciLCIzVEprYTg0Z1NKZEoxYTJmTkZNcEJHbldHcUFRYW94VF8xVm5heXptUzdnIiwiRlROcFl5T0hUcGl5M1pNSk9TelNmZFNQSktyN0FfZFd0MVpZVEttTGxqSSIsIkk0b1lCRXpQMHM5bVJlcXpnTU5yWGJnemxMc3RBTTJvUmV6SThTbUdRNkkiLCJoN2YwSlB5Ui1JM09mSURJZWJPTmpNamdYdW5QQ05EeEVkSHFkc0F3WTJRIiwicmZYb1ZWYnY0ZWNkbmZBcXlUUlB6LVQ3NEhoMThqRHlOQlo4TWNIX1NBUSJdfX0.B-EwOkVtbddnmlG-nKwPHUZ5VbgsZ-iUzb8lWb6tdLr1-9CZUo1iK3poxVcHl8u3GGN3MEzju67BImFmrHdR6g~WyJHcE9kMGVRcXl4WDJvOG1OMnNwRDZnIiwiZmFtaWx5X25hbWUiLCJNVVNURVJNQU5OIl0~WyJXMVB4QmVyUWxLUkM5c1RvM0VUWmVRIiwiZ2l2ZW5fbmFtZSIsIkVSSUtBIl0~WyIzR0k1OVUxcUM4bzBmU0w1dHY3US13IiwiYmlydGhkYXRlIiwiMTk4NC0wMS0yNiJd~WyJhN1pmV1pRdFlZZU5TNmEwVGNwQ1JRIiwiYWdlX2JpcnRoX3llYXIiLDE5ODRd~WyJFbEtKc2hWMlZycnZ3Zmgtd0RwandnIiwiYWdlX2luX3llYXJzIiw0MF0~WyJsTV9ULWxqQTRSV0NkUHozVTFyMEhBIiwiYmlydGhfZmFtaWx5X25hbWUiLCJHQUJMRVIiXQ~WyJFck93ZUlzVV9FU3NMREdxZmlYOElnIiwibmF0aW9uYWxpdGllcyIsWyJERSJdXQ~WyJXLS04bjRTT094dWVEVU9GalUyMVJ3IiwiMTIiLHRydWVd~WyJVaXdLS1MxSlNGdGl6NEF3QTBqenhnIiwiMTQiLHRydWVd~WyJha0x3aGd1S2lSaG5nQVdmZ01XOEhRIiwiMTYiLHRydWVd~WyJHYXVBaUc2ZW1CT0pJX1B5WE1YdDVBIiwiMTgiLHRydWVd~WyJiMGM5NHphbGMzZ0FwQkFzUllGX0ZnIiwiMjEiLHRydWVd~WyJwd25JYTdnb2xacjA5c3I5OGpteUdnIiwiNjUiLGZhbHNlXQ~WyJvdHpyeU1yYXc2NUY0SjkyVU1zWXF3IiwibG9jYWxpdHkiLCJCRVJMSU4iXQ~WyJ2WWt2RzBqVEpzb2QwVGJieHQyT2hRIiwibG9jYWxpdHkiLCJLw5ZMTiJd~WyJ2ZWJsRU1URmJVTUx3V0FqUzYza1pnIiwiY291bnRyeSIsIkRFIl0~WyJqZTJHMnU4RFlyN1UxSEh3YWFqa3ZRIiwicG9zdGFsX2NvZGUiLCI1MTE0NyJd~WyJVM0hKdEZHNm02NFBqYmg1ZTgxaVFRIiwic3RyZWV0X2FkZHJlc3MiLCJIRUlERVNUUkFTU0UgMTciXQ~
102 changes: 94 additions & 8 deletions test/evaluation/selectFrom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EvaluationClientWrapper } from '../../lib/evaluation';
import { SubmissionRequirementMatchType } from '../../lib/evaluation/core';
import { InternalPresentationDefinitionV1, InternalPresentationDefinitionV2, SSITypesBuilder } from '../../lib/types';
import PexMessages from '../../lib/types/Messages';
import { ClaimValue } from '../types';

export const hasher = (data: string) => createHash('sha256').update(data).digest();

Expand Down Expand Up @@ -42,7 +43,10 @@ describe('selectFrom tests', () => {
const pd = SSITypesBuilder.modelEntityToInternalPresentationDefinitionV1(pdSchema);
const evaluationClientWrapper: EvaluationClientWrapper = new EvaluationClientWrapper();
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -164,7 +168,10 @@ describe('selectFrom tests', () => {
vpSimple.verifiableCredential[2],
]);
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -256,7 +263,10 @@ describe('selectFrom tests', () => {
vpSimple.verifiableCredential[2],
]);
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -329,7 +339,10 @@ describe('selectFrom tests', () => {
vpSimple.verifiableCredential[2],
]);
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -452,7 +465,10 @@ describe('selectFrom tests', () => {
vpSimple.verifiableCredential[2],
]);
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -576,7 +592,10 @@ describe('selectFrom tests', () => {
vpSimple.verifiableCredential[2],
]);
expect(
evaluationClientWrapper.selectFrom(pd, wvcs, { holderDIDs: dids, limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES }),
evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: dids,
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
}),
).toEqual({
areRequiredCredentialsPresent: Status.INFO,
errors: [],
Expand Down Expand Up @@ -1028,9 +1047,76 @@ describe('selectFrom tests', () => {

pex.evaluateCredentials(pd, result.verifiableCredential!);
const presentationResult = pex.presentationFrom(pd, result.verifiableCredential!);
expect(presentationResult).toBeDefined();
const cred = await SDJwt.fromEncode(presentationResult.presentations[1].compactSdJwtVc, hasher);
await cred.getClaims(hasher);
const claims = await cred.getClaims<Record<string, ClaimValue>>(hasher);

console.log(claims);

// Check data group 1
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.birthdate).toBe('2024-10-09');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.issuerCode).toBe('d');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.sexCode).toBe('dd');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.expiryDate).toBe('2024-10-10');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.holdersName).toBe('dd');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.natlCode).toBe('d');
expect((claims.electronicPassport as { dataGroup1: Record<string, string> }).dataGroup1.passportNumberIdentifier).toBe('d');

// Check empty objects
expect((claims.electronicPassport as Record<string, Record<string, unknown>>).dataGroup2EncodedFaceBiometrics).toEqual({});
expect((claims.electronicPassport as Record<string, Record<string, unknown>>).docSecurityObject).toEqual({});
expect((claims.electronicPassport as Record<string, Record<string, unknown>>).dataGroup15.activeAuthentication).toEqual({});
expect((claims.electronicPassport as Record<string, Record<string, unknown>>).digitalTravelCredential).toEqual({});

// Top level claims
expect(claims.vct).toBe('epassport_copy_vc');
expect(claims.type).toBe('epassport_copy_vc');
expect(claims.iss).toBe('did:web:agent.nb.dev.sphereon.com');
});

it('Funke test', async function () {
const pdSchema: InternalPresentationDefinitionV2 = getFileAsJson('./test/dif_pe_examples/pdV2/pd-sd-jwt-vp-funke.json').presentation_definition;
const vcs: string[] = [];
vcs.push(getFile('test/dif_pe_examples/vc/vc-funke-pid-sd.jwt').replace(/(\r\n|\n|\r)/gm, ''));
const pd = SSITypesBuilder.modelEntityInternalPresentationDefinitionV2(pdSchema);
const evaluationClientWrapper: EvaluationClientWrapper = new EvaluationClientWrapper();
const wvcs: WrappedVerifiableCredential[] = SSITypesBuilder.mapExternalVerifiableCredentialsToWrappedVcs(vcs, hasher);
const result = evaluationClientWrapper.selectFrom(pd, wvcs, {
holderDIDs: ['FAsYneKJhWBP2n5E21ZzdY'],
limitDisclosureSignatureSuites: LIMIT_DISCLOSURE_SIGNATURE_SUITES,
});
expect(result.areRequiredCredentialsPresent).toBe(Status.INFO);

pex.evaluateCredentials(pd, result.verifiableCredential!);
const presentationResult = pex.presentationFrom(pd, result.verifiableCredential!);
expect(presentationResult).toBeDefined();
// TODO finish test
const cred = await SDJwt.fromEncode(presentationResult.presentations[0].compactSdJwtVc, hasher);
const claims = await cred.getClaims<Record<string, ClaimValue>>(hasher);
console.log(claims);

// Check personal information
expect(claims.family_name).toBe('MUSTERMANN');
expect(claims.given_name).toBe('ERIKA');

// Check place of birth
expect((claims.place_of_birth as { locality: string }).locality).toBe('BERLIN');

// Check address details
expect(
(
claims.address as {
locality: string;
postal_code: string;
country: string;
street_address: string;
}
).locality,
).toBe('KÖLN');

// Check issuing country
expect(claims.issuing_country).toBe('DE');

// Check age verification
expect((claims.age_equal_or_over as { '18': boolean })['18']).toBe(true);
});
});
1 change: 1 addition & 0 deletions test/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type ClaimValue = string | number | boolean | { [key: string]: ClaimValue } | Array<ClaimValue>;

0 comments on commit a5d5107

Please sign in to comment.