Skip to content

Commit

Permalink
Merge pull request #166 from Sphereon-Opensource/feature/funke-mdoc
Browse files Browse the repository at this point in the history
feature/funke-mdoc
  • Loading branch information
sanderPostma authored Sep 18, 2024
2 parents 6023f0e + 66db088 commit ce95ed3
Show file tree
Hide file tree
Showing 8 changed files with 1,407 additions and 120 deletions.
2 changes: 0 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
PresentationSignCallBackParams,
PresentationSubmissionLocation,
ProofOptions,
SdJwtDecodedVerifiableCredentialWithKbJwtInput,
SelectResults,
SignatureOptions,
Status,
Expand Down Expand Up @@ -57,5 +56,4 @@ export {
PresentationResult,
PresentationFromOpts,
PresentationSubmissionLocation,
SdJwtDecodedVerifiableCredentialWithKbJwtInput,
};
41 changes: 25 additions & 16 deletions lib/PEX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import {

import { Status } from './ConstraintUtils';
import { EvaluationClientWrapper, EvaluationResults, SelectResults } from './evaluation';
import { PresentationEvaluationResults } from './evaluation/core';
import { PresentationEvaluationResults } from './evaluation';
import {
PartialSdJwtDecodedVerifiableCredential,
PartialSdJwtKbJwt,
PresentationFromOpts,
PresentationResult,
PresentationSignCallBackParams,
PresentationSubmissionLocation,
SdJwtDecodedVerifiableCredentialWithKbJwtInput,
SdJwtKbJwtInput,
VerifiablePresentationFromOpts,
VerifiablePresentationResult,
} from './signing';
Expand Down Expand Up @@ -72,16 +72,16 @@ export class PEX {
*/
public evaluatePresentation(
presentationDefinition: IPresentationDefinition,
presentations: OrArray<OriginalVerifiablePresentation | IPresentation>,
presentations: OrArray<OriginalVerifiablePresentation | IPresentation | PartialSdJwtDecodedVerifiableCredential>,
opts?: {
limitDisclosureSignatureSuites?: string[];
restrictToFormats?: Format;
restrictToDIDMethods?: string[];
presentationSubmission?: PresentationSubmission;
/**
* The location of the presentation submission. By default {@link PresentationSubmissionLocation.PRESENTATION}
* is used when one W3C presentation is passed (not as array) , while {@link PresentationSubmissionLocation.EXTERNAL} is
* used when an array is passed or the presentation is not a W3C presentation
* is used when one presentation is passed (not as array), while {@link PresentationSubmissionLocation.EXTERNAL} is
* used when an array is passed
*/
presentationSubmissionLocation?: PresentationSubmissionLocation;
generatePresentationSubmission?: boolean;
Expand Down Expand Up @@ -117,7 +117,10 @@ export class PEX {
CredentialMapper.isW3cPresentation(wrappedPresentations[0].presentation) &&
!generatePresentationSubmission
) {
presentationSubmission = wrappedPresentations[0].decoded.presentation_submission;
const decoded = wrappedPresentations[0].decoded;
if ('presentation_submission' in decoded) {
presentationSubmission = decoded.presentation_submission;
}
if (!presentationSubmission) {
throw Error(`Either a presentation submission as part of the VP or provided in options was expected`);
}
Expand All @@ -134,7 +137,9 @@ export class PEX {
// TODO: we should probably add support for holder dids in the kb-jwt of an SD-JWT. We can extract this from the
// `wrappedPresentation.original.compactKbJwt`, but as HAIP doesn't use dids, we'll leave it for now.
const holderDIDs = wrappedPresentations
.map((p) => (CredentialMapper.isW3cPresentation(p.presentation) && p.presentation.holder ? p.presentation.holder : undefined))
.map((p) => {
return CredentialMapper.isW3cPresentation(p.presentation) && p.presentation.holder ? p.presentation.holder : undefined;
})
.filter((d): d is string => d !== undefined);

const updatedOpts = {
Expand Down Expand Up @@ -310,7 +315,7 @@ export class PEX {
*/
hasher?: Hasher;
},
): IPresentation | SdJwtDecodedVerifiableCredentialWithKbJwtInput {
): IPresentation | PartialSdJwtDecodedVerifiableCredential {
const credentials = Array.isArray(selectedCredentials) ? selectedCredentials : [selectedCredentials];

// for SD-JWT we want to return the SD-JWT with only the needed disclosures (so filter disclosures array, and update the compactSdJwt)
Expand Down Expand Up @@ -350,18 +355,19 @@ export class PEX {
// alg MUST be set by the signer
header: {
typ: 'kb+jwt',
// aud MUST be set by the signer or provided by e.g. SIOP/OpenID4VP lib
},
// aud MUST be set by the signer or provided by e.g. SIOP/OpenID4VP lib
payload: {
iat: new Date().getTime(),
sd_hash: sdHash,
},
} satisfies SdJwtKbJwtInput;
} satisfies PartialSdJwtKbJwt;

return {
const sdJwtDecodedPresentation: PartialSdJwtDecodedVerifiableCredential = {
...decoded,
kbJwt,
};
return sdJwtDecodedPresentation;
} else {
if (!selectedCredentials) {
throw Error(`At least a verifiable credential needs to be passed in to create a presentation`);
Expand Down Expand Up @@ -525,7 +531,9 @@ export class PEX {

let presentation = presentationResult.presentation;

if (CredentialMapper.isSdJwtDecodedCredential(presentationResult.presentation)) {
// Select type without kbJwt as isSdJwtDecodedCredential and won't accept the partial sdvc type
if (CredentialMapper.isSdJwtDecodedCredential(presentationResult.presentation as SdJwtDecodedVerifiableCredential)) {
const sdJwtPresentation = presentation as SdJwtDecodedVerifiableCredential;
if (!this.options?.hasher) {
throw new Error('Hasher must be provided when creating a presentation with an SD-JWT VC');
}
Expand All @@ -538,19 +546,20 @@ export class PEX {
// alg MUST be set by the signer
header: {
typ: 'kb+jwt',
alg: hashAlg,
},
// aud MUST be set by the signer or provided by e.g. SIOP/OpenID4VP lib
payload: {
iat: new Date().getTime(),
nonce: proofOptions?.nonce,
sd_hash: sdHash,
},
} satisfies SdJwtKbJwtInput;
} satisfies PartialSdJwtKbJwt;

presentation = {
...presentation,
...sdJwtPresentation,
kbJwt,
};
} satisfies PartialSdJwtDecodedVerifiableCredential;
}

const callBackParams: PresentationSignCallBackParams = {
Expand Down
6 changes: 5 additions & 1 deletion lib/evaluation/handlers/didRestrictionEvaluationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ export class DIDRestrictionEvaluationHandler extends AbstractEvaluationHandler {
return typeof wrappedVc.credential.issuer === 'object' ? wrappedVc.credential.issuer.id : wrappedVc.credential.issuer;
} else if (CredentialMapper.isSdJwtDecodedCredential(wrappedVc.credential)) {
return wrappedVc.credential.decodedPayload.iss;
} else if (CredentialMapper.isWrappedMdocCredential(wrappedVc)) {
if (typeof wrappedVc.decoded === 'object' && wrappedVc.decoded.iss !== undefined) {
return wrappedVc.decoded.iss;
}
throw new Error('cannot get issuer from the supplied mdoc credential');
}

throw new Error('Unsupported credential type');
}

Expand Down
22 changes: 9 additions & 13 deletions lib/signing/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
IProofType,
OriginalVerifiableCredential,
SdJwtDecodedVerifiableCredential,
SdJwtVcKbJwtPayload,
W3CVerifiablePresentation,
} from '@sphereon/ssi-types';
import { SdJwtVcKbJwtHeader } from '@sphereon/ssi-types/src/types/sd-jwt-vc';

import { PresentationEvaluationResults } from '../evaluation';

Expand Down Expand Up @@ -84,18 +86,12 @@ export enum PresentationSubmissionLocation {
PRESENTATION, // Part of the VP itself
}

export interface SdJwtKbJwtInput {
header: {
typ: 'kb+jwt';
};
payload: {
iat: number;
sd_hash: string;
nonce?: string;
};
}
export type PartialSdJwtKbJwt = {
header: Partial<SdJwtVcKbJwtHeader>;
payload: Partial<SdJwtVcKbJwtPayload>;
};

export type SdJwtDecodedVerifiableCredentialWithKbJwtInput = SdJwtDecodedVerifiableCredential & { kbJwt: SdJwtKbJwtInput };
export type PartialSdJwtDecodedVerifiableCredential = Omit<SdJwtDecodedVerifiableCredential, 'kbJwt'> & { kbJwt: PartialSdJwtKbJwt };

/**
* The result object containing the presentation and presentation submission
Expand All @@ -104,7 +100,7 @@ export interface PresentationResult {
/**
* The resulting presentation, can have an embedded submission data depending on the location parameter
*/
presentation: IPresentation | SdJwtDecodedVerifiableCredentialWithKbJwtInput;
presentation: IPresentation | SdJwtDecodedVerifiableCredential | PartialSdJwtDecodedVerifiableCredential;

/**
* The resulting location of the presentation submission.
Expand Down Expand Up @@ -193,7 +189,7 @@ export interface PresentationSignCallBackParams {
* and only the optional KB-JWT should be appended to the `compactSdJwt` property. If no KB-JWT is needed on the presentation, the `compactSdJwt` property
* from the decoded SD-JWT can be returned as-is.
*/
presentation: IPresentation | SdJwtDecodedVerifiableCredentialWithKbJwtInput;
presentation: IPresentation | SdJwtDecodedVerifiableCredential | PartialSdJwtDecodedVerifiableCredential;

/**
* A partial proof value the callback can use to complete. If proofValue or JWS was supplied the proof could be complete already
Expand Down
1 change: 1 addition & 0 deletions lib/validation/bundlers/presentationSubmissionVB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export class PresentationSubmissionVB extends ValidationBundler<PresentationSubm
'di_vc',
'di_vp',
'vc+sd-jwt',
'mso_mdoc',
];

for (let i = 0; i < descriptor_map.length; i++) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sphereon/pex",
"version": "4.1.0",
"version": "5.0.0-unstable.0",
"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 Expand Up @@ -47,7 +47,7 @@
"@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",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"jwt-decode": "^3.1.2",
Expand Down
Loading

0 comments on commit ce95ed3

Please sign in to comment.