Skip to content

Commit 4a4c1b0

Browse files
authored
fix: integrity check (#339)
Signed-off-by: Timo Glastra <timo@animo.id>
1 parent 9e8110d commit 4a4c1b0

File tree

4 files changed

+22
-6
lines changed

4 files changed

+22
-6
lines changed

packages/sd-jwt-vc/src/sd-jwt-vc-instance.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
128128

129129
/**
130130
* Gets VCT Metadata of the raw SD-JWT-VC. Returns the type metadata format. If the SD-JWT-VC is invalid or does not contain a vct claim, an error is thrown.
131+
*
132+
* It may return `undefined` if the fetcher returned an undefined value (instead of throwing an error).
133+
*
131134
* @param encodedSDJwt
132135
* @returns
133136
*/
@@ -222,6 +225,7 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
222225
// implement based on https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-08.html#name-extending-type-metadata
223226
//TODO: needs to be implemented. Unclear at this point which values will overwrite the values from the extended type metadata format
224227
}
228+
225229
return typeMetadataFormat;
226230
}
227231

@@ -244,7 +248,7 @@ export class SDJwtVcInstance extends SDJwtInstance<SdJwtVcPayload> {
244248
const fetcher: VcTFetcher =
245249
this.userConfig.vctFetcher ??
246250
((uri, integrity) => this.fetch(uri, integrity));
247-
return fetcher(result.payload.vct, result.payload['vct#Integrity']);
251+
return fetcher(result.payload.vct, result.payload['vct#integrity']);
248252
}
249253

250254
/**

packages/sd-jwt-vc/src/sd-jwt-vc-payload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface SdJwtVcPayload extends SdJwtPayload {
1313
// REQUIRED. The type of the Verifiable Credential, e.g., https://credentials.example.com/identity_credential, as defined in Section 3.2.2.1.1.
1414
vct: string;
1515
// OPTIONAL. If passed, the loaded type metadata format has to be validated according to https://www.w3.org/TR/SRI/
16-
'vct#Integrity'?: string;
16+
'vct#integrity'?: string;
1717
// OPTIONAL. The information on how to read the status of the Verifiable Credential. See [https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-02.html] for more information.
1818
status?: SDJWTVCStatusReference;
1919
// OPTIONAL. The identifier of the Subject of the Verifiable Credential. The Issuer MAY use it to provide the Subject identifier known by the Issuer. There is no requirement for a binding to exist between sub and cnf claims.

packages/sd-jwt-vc/src/sd-jwt-vc-type-metadata-format.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export type TypeMetadataFormat = {
136136
/** OPTIONAL. URI of another type that this one extends. */
137137
extends?: string;
138138
/** OPTIONAL. Integrity metadata for the 'extends' field. */
139-
'extends#Integrity'?: string;
139+
'extends#integrity'?: string;
140140
/** OPTIONAL. Array of localized display metadata for the type. */
141141
display?: Display[];
142142
/** OPTIONAL. Array of claim metadata. */

packages/sd-jwt-vc/src/test/vct.spec.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
44
import type { DisclosureFrame, Signer, Verifier } from '@sd-jwt/types';
55
import { HttpResponse, http } from 'msw';
66
import { setupServer } from 'msw/node';
7-
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
7+
import { afterAll, beforeAll, describe, expect, test, vitest } from 'vitest';
88
import { SDJwtVcInstance } from '..';
99
import type { SdJwtVcPayload } from '../sd-jwt-vc-payload';
1010
import type { TypeMetadataFormat } from '../sd-jwt-vc-type-metadata-format';
@@ -85,19 +85,31 @@ describe('App', () => {
8585
afterEach(() => server.resetHandlers());
8686

8787
test('VCT Validation', async () => {
88+
// The method is private, so TS complains, but you can use spies on private method just fine.
89+
// @ts-expect-error
90+
const validateIntegritySpy = vitest.spyOn(sdjwt, 'validateIntegrity');
91+
8892
const expectedPayload: SdJwtVcPayload = {
8993
iat,
9094
iss,
9195
vct,
92-
'vct#Integrity': vctIntegrity,
96+
'vct#integrity': vctIntegrity,
9397
...claims,
9498
};
99+
95100
const encodedSdjwt = await sdjwt.issue(
96101
expectedPayload,
97102
disclosureFrame as unknown as DisclosureFrame<SdJwtVcPayload>,
98103
);
99104

100105
await sdjwt.verify(encodedSdjwt);
106+
107+
// Ensure validateIntegrity method was called
108+
expect(validateIntegritySpy).toHaveBeenCalledWith(
109+
expect.any(Response),
110+
vct,
111+
vctIntegrity,
112+
);
101113
});
102114

103115
test('VCT from JWT header Validation', async () => {
@@ -133,7 +145,7 @@ describe('App', () => {
133145
disclosureFrame as unknown as DisclosureFrame<SdJwtVcPayload>,
134146
);
135147

136-
expect(sdjwt.verify(encodedSdjwt)).rejects.toThrowError(
148+
await expect(sdjwt.verify(encodedSdjwt)).rejects.toThrowError(
137149
`Request to ${vct} timed out`,
138150
);
139151
});

0 commit comments

Comments
 (0)