@sd-jwt/core 0.3.2-next.98 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/index.d.mts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +49 -37
- package/dist/index.mjs +39 -25
- package/package.json +7 -6
- package/src/index.ts +13 -6
- package/src/jwt.ts +1 -1
- package/src/kbjwt.ts +34 -9
- package/src/sdjwt.ts +12 -13
- package/src/test/index.spec.ts +154 -57
- package/src/test/kbjwt.spec.ts +111 -45
- package/test/app-e2e.spec.ts +12 -4
- package/test/array_data_types.json +10 -8
- package/test/array_full_sd.json +1 -1
- package/test/array_in_sd.json +1 -1
- package/test/array_nested_in_plain.json +10 -1
- package/test/array_none_disclosed.json +1 -1
- package/test/array_of_nulls.json +1 -1
- package/test/array_of_objects.json +8 -1
- package/test/array_of_scalars.json +5 -1
- package/test/array_recursive_sd.json +1 -1
- package/test/array_recursive_sd_some_disclosed.json +14 -6
- package/test/complex.json +1 -1
- package/test/header_mod.json +5 -1
- package/test/json_serialization.json +5 -1
- package/test/key_binding.json +5 -1
- package/test/no_sd.json +1 -1
- package/test/object_data_types.json +11 -9
- package/test/recursions.json +36 -17
package/src/sdjwt.ts
CHANGED
|
@@ -6,19 +6,18 @@ import {
|
|
|
6
6
|
DisclosureFrame,
|
|
7
7
|
Hasher,
|
|
8
8
|
HasherAndAlg,
|
|
9
|
-
|
|
10
|
-
KB_JWT_TYP,
|
|
9
|
+
PresentationFrame,
|
|
11
10
|
SDJWTCompact,
|
|
12
11
|
SD_DECOY,
|
|
13
12
|
SD_DIGEST,
|
|
14
13
|
SD_LIST_KEY,
|
|
15
14
|
SD_SEPARATOR,
|
|
16
15
|
SaltGenerator,
|
|
17
|
-
Signer,
|
|
18
16
|
kbHeader,
|
|
19
17
|
kbPayload,
|
|
20
18
|
} from '@sd-jwt/types';
|
|
21
19
|
import { createHashMapping, getSDAlgAndPayload, unpack } from '@sd-jwt/decode';
|
|
20
|
+
import { transformPresentationFrame } from '@sd-jwt/present';
|
|
22
21
|
|
|
23
22
|
export type SDJwtData<
|
|
24
23
|
Header extends Record<string, unknown>,
|
|
@@ -117,7 +116,10 @@ export class SDJwt<
|
|
|
117
116
|
});
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
public async present
|
|
119
|
+
public async present<T extends Record<string, unknown>>(
|
|
120
|
+
presentFrame: PresentationFrame<T> | undefined,
|
|
121
|
+
hasher: Hasher,
|
|
122
|
+
): Promise<SDJWTCompact> {
|
|
121
123
|
if (!this.jwt?.payload || !this.disclosures) {
|
|
122
124
|
throw new SDJWTException('Invalid sd-jwt: jwt or disclosures is missing');
|
|
123
125
|
}
|
|
@@ -130,15 +132,12 @@ export class SDJwt<
|
|
|
130
132
|
hasher,
|
|
131
133
|
);
|
|
132
134
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const disclosures = keys.map((k) => hashmap[disclosureKeymap[k]]);
|
|
135
|
+
const keys = presentFrame
|
|
136
|
+
? transformPresentationFrame(presentFrame)
|
|
137
|
+
: await this.presentableKeys(hasher);
|
|
138
|
+
const disclosures = keys
|
|
139
|
+
.map((k) => hashmap[disclosureKeymap[k]])
|
|
140
|
+
.filter((d) => d !== undefined);
|
|
142
141
|
const presentSDJwt = new SDJwt({
|
|
143
142
|
jwt: this.jwt,
|
|
144
143
|
disclosures,
|
package/src/test/index.spec.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { SDJwtInstance, SdJwtPayload } from '../index';
|
|
2
|
-
import { Signer, Verifier } from '@sd-jwt/types';
|
|
3
|
-
import Crypto from 'node:crypto';
|
|
2
|
+
import { Signer, Verifier, KbVerifier, JwtPayload } from '@sd-jwt/types';
|
|
3
|
+
import Crypto, { KeyLike } from 'node:crypto';
|
|
4
4
|
import { describe, expect, test } from 'vitest';
|
|
5
5
|
import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
|
|
6
|
+
import { importJWK, exportJWK, JWK } from 'jose';
|
|
6
7
|
|
|
7
8
|
export const createSignerVerifier = () => {
|
|
8
9
|
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
@@ -52,15 +53,19 @@ describe('index', () => {
|
|
|
52
53
|
|
|
53
54
|
expect(credential).toBeDefined();
|
|
54
55
|
|
|
55
|
-
const presentation = await sdjwt.present(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const presentation = await sdjwt.present(
|
|
57
|
+
credential,
|
|
58
|
+
{ foo: true },
|
|
59
|
+
{
|
|
60
|
+
kb: {
|
|
61
|
+
payload: {
|
|
62
|
+
aud: '1',
|
|
63
|
+
iat: 1,
|
|
64
|
+
nonce: '342',
|
|
65
|
+
},
|
|
61
66
|
},
|
|
62
67
|
},
|
|
63
|
-
|
|
68
|
+
);
|
|
64
69
|
|
|
65
70
|
expect(presentation).toBeDefined();
|
|
66
71
|
});
|
|
@@ -162,15 +167,19 @@ describe('index', () => {
|
|
|
162
167
|
},
|
|
163
168
|
);
|
|
164
169
|
|
|
165
|
-
const presentation = await sdjwt.present(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
const presentation = await sdjwt.present(
|
|
171
|
+
credential,
|
|
172
|
+
{ foo: true },
|
|
173
|
+
{
|
|
174
|
+
kb: {
|
|
175
|
+
payload: {
|
|
176
|
+
aud: '',
|
|
177
|
+
iat: 1,
|
|
178
|
+
nonce: '342',
|
|
179
|
+
},
|
|
171
180
|
},
|
|
172
181
|
},
|
|
173
|
-
|
|
182
|
+
);
|
|
174
183
|
|
|
175
184
|
try {
|
|
176
185
|
await sdjwt.verify(presentation);
|
|
@@ -181,38 +190,72 @@ describe('index', () => {
|
|
|
181
190
|
|
|
182
191
|
test('verify with kbJwt', async () => {
|
|
183
192
|
const { signer, verifier } = createSignerVerifier();
|
|
193
|
+
|
|
194
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
195
|
+
|
|
196
|
+
//TODO: maybe we can pass a minial class of the jwt to pass the token
|
|
197
|
+
const kbVerifier: KbVerifier = async (
|
|
198
|
+
data: string,
|
|
199
|
+
sig: string,
|
|
200
|
+
payload: JwtPayload,
|
|
201
|
+
) => {
|
|
202
|
+
let publicKey: JsonWebKey;
|
|
203
|
+
if (payload.cnf) {
|
|
204
|
+
// use the key from the cnf
|
|
205
|
+
publicKey = payload.cnf.jwk;
|
|
206
|
+
} else {
|
|
207
|
+
throw Error('key binding not supported');
|
|
208
|
+
}
|
|
209
|
+
// get the key of the holder to verify the signature
|
|
210
|
+
return Crypto.verify(
|
|
211
|
+
null,
|
|
212
|
+
Buffer.from(data),
|
|
213
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
214
|
+
Buffer.from(sig, 'base64url'),
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const kbSigner = (data: string) => {
|
|
219
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
220
|
+
return Buffer.from(sig).toString('base64url');
|
|
221
|
+
};
|
|
222
|
+
|
|
184
223
|
const sdjwt = new SDJwtInstance<SdJwtPayload>({
|
|
185
224
|
signer,
|
|
186
225
|
signAlg: 'EdDSA',
|
|
187
226
|
verifier,
|
|
188
227
|
hasher: digest,
|
|
189
228
|
saltGenerator: generateSalt,
|
|
190
|
-
kbSigner:
|
|
191
|
-
kbVerifier:
|
|
229
|
+
kbSigner: kbSigner,
|
|
230
|
+
kbVerifier: kbVerifier,
|
|
192
231
|
kbSignAlg: 'EdDSA',
|
|
193
232
|
});
|
|
194
|
-
|
|
195
233
|
const credential = await sdjwt.issue(
|
|
196
234
|
{
|
|
197
235
|
foo: 'bar',
|
|
198
|
-
iss: 'Issuer',
|
|
199
236
|
iat: new Date().getTime(),
|
|
200
|
-
|
|
237
|
+
cnf: {
|
|
238
|
+
jwk: await exportJWK(publicKey),
|
|
239
|
+
},
|
|
201
240
|
},
|
|
202
241
|
{
|
|
203
242
|
_sd: ['foo'],
|
|
204
243
|
},
|
|
205
244
|
);
|
|
206
245
|
|
|
207
|
-
const presentation = await sdjwt.present(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
246
|
+
const presentation = await sdjwt.present(
|
|
247
|
+
credential,
|
|
248
|
+
{ foo: true },
|
|
249
|
+
{
|
|
250
|
+
kb: {
|
|
251
|
+
payload: {
|
|
252
|
+
aud: '1',
|
|
253
|
+
iat: 1,
|
|
254
|
+
nonce: '342',
|
|
255
|
+
},
|
|
213
256
|
},
|
|
214
257
|
},
|
|
215
|
-
|
|
258
|
+
);
|
|
216
259
|
|
|
217
260
|
const results = await sdjwt.verify(presentation, ['foo'], true);
|
|
218
261
|
expect(results).toBeDefined();
|
|
@@ -310,15 +353,19 @@ describe('index', () => {
|
|
|
310
353
|
},
|
|
311
354
|
);
|
|
312
355
|
|
|
313
|
-
const presentation = await sdjwt.present(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
356
|
+
const presentation = await sdjwt.present(
|
|
357
|
+
credential,
|
|
358
|
+
{ foo: true },
|
|
359
|
+
{
|
|
360
|
+
kb: {
|
|
361
|
+
payload: {
|
|
362
|
+
aud: '1',
|
|
363
|
+
iat: 1,
|
|
364
|
+
nonce: '342',
|
|
365
|
+
},
|
|
319
366
|
},
|
|
320
367
|
},
|
|
321
|
-
|
|
368
|
+
);
|
|
322
369
|
try {
|
|
323
370
|
const results = await sdjwt.verify(presentation, ['foo'], true);
|
|
324
371
|
} catch (e) {
|
|
@@ -350,15 +397,19 @@ describe('index', () => {
|
|
|
350
397
|
},
|
|
351
398
|
);
|
|
352
399
|
try {
|
|
353
|
-
const presentation = await sdjwt.present(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
400
|
+
const presentation = await sdjwt.present(
|
|
401
|
+
credential,
|
|
402
|
+
{ foo: true },
|
|
403
|
+
{
|
|
404
|
+
kb: {
|
|
405
|
+
payload: {
|
|
406
|
+
aud: '1',
|
|
407
|
+
iat: 1,
|
|
408
|
+
nonce: '342',
|
|
409
|
+
},
|
|
359
410
|
},
|
|
360
411
|
},
|
|
361
|
-
|
|
412
|
+
);
|
|
362
413
|
} catch (e) {
|
|
363
414
|
expect(e).toBeDefined();
|
|
364
415
|
}
|
|
@@ -388,15 +439,19 @@ describe('index', () => {
|
|
|
388
439
|
},
|
|
389
440
|
);
|
|
390
441
|
|
|
391
|
-
const presentation = await sdjwt.present(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
442
|
+
const presentation = await sdjwt.present(
|
|
443
|
+
credential,
|
|
444
|
+
{ foo: true },
|
|
445
|
+
{
|
|
446
|
+
kb: {
|
|
447
|
+
payload: {
|
|
448
|
+
aud: '1',
|
|
449
|
+
iat: 1,
|
|
450
|
+
nonce: '342',
|
|
451
|
+
},
|
|
397
452
|
},
|
|
398
453
|
},
|
|
399
|
-
|
|
454
|
+
);
|
|
400
455
|
try {
|
|
401
456
|
const results = await sdjwt.verify(presentation, ['foo'], true);
|
|
402
457
|
} catch (e) {
|
|
@@ -427,15 +482,19 @@ describe('index', () => {
|
|
|
427
482
|
},
|
|
428
483
|
);
|
|
429
484
|
|
|
430
|
-
const presentation = sdjwt.present(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
485
|
+
const presentation = sdjwt.present(
|
|
486
|
+
credential,
|
|
487
|
+
{ foo: true },
|
|
488
|
+
{
|
|
489
|
+
kb: {
|
|
490
|
+
payload: {
|
|
491
|
+
aud: '1',
|
|
492
|
+
iat: 1,
|
|
493
|
+
nonce: '342',
|
|
494
|
+
},
|
|
436
495
|
},
|
|
437
496
|
},
|
|
438
|
-
|
|
497
|
+
);
|
|
439
498
|
expect(presentation).rejects.toThrow(
|
|
440
499
|
'Key Binding sign algorithm not specified',
|
|
441
500
|
);
|
|
@@ -465,7 +524,7 @@ describe('index', () => {
|
|
|
465
524
|
expect(sdjwt.presentableKeys('')).rejects.toThrow('Hasher not found');
|
|
466
525
|
expect(sdjwt.getClaims('')).rejects.toThrow('Hasher not found');
|
|
467
526
|
expect(() => sdjwt.decode('')).toThrowError('Hasher not found');
|
|
468
|
-
expect(sdjwt.present(credential,
|
|
527
|
+
expect(sdjwt.present(credential, { foo: true })).rejects.toThrow(
|
|
469
528
|
'Hasher not found',
|
|
470
529
|
);
|
|
471
530
|
});
|
|
@@ -493,4 +552,42 @@ describe('index', () => {
|
|
|
493
552
|
expect(keys).toBeDefined();
|
|
494
553
|
expect(keys).toEqual(['foo']);
|
|
495
554
|
});
|
|
555
|
+
|
|
556
|
+
test('present all disclosures with kb jwt', async () => {
|
|
557
|
+
const { signer } = createSignerVerifier();
|
|
558
|
+
const sdjwt = new SDJwtInstance<SdJwtPayload>({
|
|
559
|
+
signer,
|
|
560
|
+
kbSigner: signer,
|
|
561
|
+
hasher: digest,
|
|
562
|
+
saltGenerator: generateSalt,
|
|
563
|
+
signAlg: 'EdDSA',
|
|
564
|
+
kbSignAlg: 'EdDSA',
|
|
565
|
+
});
|
|
566
|
+
const credential = await sdjwt.issue(
|
|
567
|
+
{
|
|
568
|
+
foo: 'bar',
|
|
569
|
+
iss: 'Issuer',
|
|
570
|
+
iat: new Date().getTime(),
|
|
571
|
+
vct: '',
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
_sd: ['foo'],
|
|
575
|
+
},
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
const presentation = await sdjwt.present(credential, undefined, {
|
|
579
|
+
kb: {
|
|
580
|
+
payload: {
|
|
581
|
+
aud: '1',
|
|
582
|
+
iat: 1,
|
|
583
|
+
nonce: '342',
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
const decoded = await sdjwt.decode(presentation);
|
|
589
|
+
expect(decoded.jwt).toBeDefined();
|
|
590
|
+
expect(decoded.disclosures).toBeDefined();
|
|
591
|
+
expect(decoded.kbJwt).toBeDefined();
|
|
592
|
+
});
|
|
496
593
|
});
|
package/src/test/kbjwt.spec.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { SDJWTException } from '@sd-jwt/utils';
|
|
2
2
|
import { KBJwt } from '../kbjwt';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
JwtPayload,
|
|
5
|
+
KB_JWT_TYP,
|
|
6
|
+
KbVerifier,
|
|
7
|
+
Signer,
|
|
8
|
+
Verifier,
|
|
9
|
+
} from '@sd-jwt/types';
|
|
10
|
+
import Crypto, { KeyLike } from 'node:crypto';
|
|
5
11
|
import { describe, expect, test } from 'vitest';
|
|
12
|
+
import { JWK, exportJWK, importJWK } from 'jose';
|
|
6
13
|
|
|
7
14
|
describe('KB JWT', () => {
|
|
8
15
|
test('create', async () => {
|
|
@@ -69,11 +76,27 @@ describe('KB JWT', () => {
|
|
|
69
76
|
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
70
77
|
return Buffer.from(sig).toString('base64url');
|
|
71
78
|
};
|
|
72
|
-
|
|
79
|
+
|
|
80
|
+
const payload = {
|
|
81
|
+
cnf: {
|
|
82
|
+
jwk: await exportJWK(publicKey),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const testVerifier: KbVerifier = async (
|
|
87
|
+
data: string,
|
|
88
|
+
sig: string,
|
|
89
|
+
payload: JwtPayload,
|
|
90
|
+
) => {
|
|
91
|
+
expect(payload).toStrictEqual(payload);
|
|
92
|
+
expect(payload.cnf?.jwk).toBeDefined();
|
|
93
|
+
|
|
94
|
+
const publicKey = payload.cnf?.jwk;
|
|
95
|
+
|
|
73
96
|
return Crypto.verify(
|
|
74
97
|
null,
|
|
75
98
|
Buffer.from(data),
|
|
76
|
-
publicKey,
|
|
99
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
77
100
|
Buffer.from(sig, 'base64url'),
|
|
78
101
|
);
|
|
79
102
|
};
|
|
@@ -91,7 +114,10 @@ describe('KB JWT', () => {
|
|
|
91
114
|
});
|
|
92
115
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
93
116
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
94
|
-
const verified = await decoded.
|
|
117
|
+
const verified = await decoded.verifyKB({
|
|
118
|
+
verifier: testVerifier,
|
|
119
|
+
payload,
|
|
120
|
+
});
|
|
95
121
|
expect(verified).toStrictEqual({
|
|
96
122
|
header: {
|
|
97
123
|
typ: KB_JWT_TYP,
|
|
@@ -112,11 +138,26 @@ describe('KB JWT', () => {
|
|
|
112
138
|
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
113
139
|
return Buffer.from(sig).toString('base64url');
|
|
114
140
|
};
|
|
115
|
-
|
|
141
|
+
|
|
142
|
+
const payload = {
|
|
143
|
+
cnf: {
|
|
144
|
+
jwk: await exportJWK(publicKey),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const testVerifier: KbVerifier = async (
|
|
148
|
+
data: string,
|
|
149
|
+
sig: string,
|
|
150
|
+
payload: JwtPayload,
|
|
151
|
+
) => {
|
|
152
|
+
expect(payload).toStrictEqual(payload);
|
|
153
|
+
expect(payload.cnf?.jwk).toBeDefined();
|
|
154
|
+
|
|
155
|
+
const publicKey = payload.cnf?.jwk;
|
|
156
|
+
|
|
116
157
|
return Crypto.verify(
|
|
117
158
|
null,
|
|
118
159
|
Buffer.from(data),
|
|
119
|
-
publicKey,
|
|
160
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
120
161
|
Buffer.from(sig, 'base64url'),
|
|
121
162
|
);
|
|
122
163
|
};
|
|
@@ -136,26 +177,34 @@ describe('KB JWT', () => {
|
|
|
136
177
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
137
178
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
138
179
|
try {
|
|
139
|
-
await decoded.
|
|
180
|
+
await decoded.verifyKB({ verifier: testVerifier, payload });
|
|
140
181
|
} catch (e: unknown) {
|
|
141
182
|
const error = e as SDJWTException;
|
|
142
183
|
expect(error.message).toBe('Invalid Key Binding Jwt');
|
|
143
184
|
}
|
|
144
185
|
});
|
|
145
186
|
|
|
146
|
-
test('verify with
|
|
187
|
+
test('verify failed with verifier return false', async () => {
|
|
147
188
|
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
148
189
|
const testSigner: Signer = async (data: string) => {
|
|
149
190
|
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
150
191
|
return Buffer.from(sig).toString('base64url');
|
|
151
192
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
193
|
+
|
|
194
|
+
const payload = {
|
|
195
|
+
cnf: {
|
|
196
|
+
jwk: await exportJWK(publicKey),
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
const testVerifier: KbVerifier = async (
|
|
200
|
+
data: string,
|
|
201
|
+
sig: string,
|
|
202
|
+
payload: JwtPayload,
|
|
203
|
+
) => {
|
|
204
|
+
expect(payload).toStrictEqual(payload);
|
|
205
|
+
expect(payload.cnf?.jwk).toBeDefined();
|
|
206
|
+
|
|
207
|
+
return false;
|
|
159
208
|
};
|
|
160
209
|
|
|
161
210
|
const kbJwt = new KBJwt({
|
|
@@ -170,37 +219,37 @@ describe('KB JWT', () => {
|
|
|
170
219
|
sd_hash: 'hash',
|
|
171
220
|
},
|
|
172
221
|
});
|
|
173
|
-
|
|
174
222
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
175
223
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
payload: {
|
|
183
|
-
iat: 1,
|
|
184
|
-
aud: 'aud',
|
|
185
|
-
nonce: 'nonce',
|
|
186
|
-
sd_hash: 'hash',
|
|
187
|
-
},
|
|
188
|
-
});
|
|
224
|
+
try {
|
|
225
|
+
await decoded.verifyKB({ verifier: testVerifier, payload });
|
|
226
|
+
} catch (e: unknown) {
|
|
227
|
+
const error = e as SDJWTException;
|
|
228
|
+
expect(error.message).toBe('Verify Error: Invalid JWT Signature');
|
|
229
|
+
}
|
|
189
230
|
});
|
|
190
231
|
|
|
191
|
-
test('verify failed with
|
|
232
|
+
test('verify failed with invalid jwt', async () => {
|
|
192
233
|
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
193
234
|
const testSigner: Signer = async (data: string) => {
|
|
194
235
|
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
195
236
|
return Buffer.from(sig).toString('base64url');
|
|
196
237
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
238
|
+
|
|
239
|
+
const payload = {
|
|
240
|
+
cnf: {
|
|
241
|
+
jwk: await exportJWK(publicKey),
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
const testVerifier: KbVerifier = async (
|
|
245
|
+
data: string,
|
|
246
|
+
sig: string,
|
|
247
|
+
payload: JwtPayload,
|
|
248
|
+
) => {
|
|
249
|
+
expect(payload).toStrictEqual(payload);
|
|
250
|
+
expect(payload.cnf?.jwk).toBeDefined();
|
|
251
|
+
|
|
252
|
+
return false;
|
|
204
253
|
};
|
|
205
254
|
|
|
206
255
|
const kbJwt = new KBJwt({
|
|
@@ -212,17 +261,17 @@ describe('KB JWT', () => {
|
|
|
212
261
|
iat: 1,
|
|
213
262
|
aud: 'aud',
|
|
214
263
|
nonce: 'nonce',
|
|
215
|
-
sd_hash: '',
|
|
264
|
+
sd_hash: 'hash',
|
|
216
265
|
},
|
|
217
266
|
});
|
|
218
|
-
|
|
219
267
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
220
268
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
269
|
+
decoded.signature = undefined;
|
|
221
270
|
try {
|
|
222
|
-
await decoded.
|
|
271
|
+
await decoded.verifyKB({ verifier: testVerifier, payload });
|
|
223
272
|
} catch (e: unknown) {
|
|
224
273
|
const error = e as SDJWTException;
|
|
225
|
-
expect(error.message).toBe('
|
|
274
|
+
expect(error.message).toBe('Verify Error: Invalid JWT');
|
|
226
275
|
}
|
|
227
276
|
});
|
|
228
277
|
|
|
@@ -232,11 +281,25 @@ describe('KB JWT', () => {
|
|
|
232
281
|
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
233
282
|
return Buffer.from(sig).toString('base64url');
|
|
234
283
|
};
|
|
235
|
-
const
|
|
284
|
+
const payload = {
|
|
285
|
+
cnf: {
|
|
286
|
+
jwk: await exportJWK(publicKey),
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
const testVerifier: KbVerifier = async (
|
|
290
|
+
data: string,
|
|
291
|
+
sig: string,
|
|
292
|
+
payload: JwtPayload,
|
|
293
|
+
) => {
|
|
294
|
+
expect(payload).toStrictEqual(payload);
|
|
295
|
+
expect(payload.cnf?.jwk).toBeDefined();
|
|
296
|
+
|
|
297
|
+
const publicKey = payload.cnf?.jwk;
|
|
298
|
+
|
|
236
299
|
return Crypto.verify(
|
|
237
300
|
null,
|
|
238
301
|
Buffer.from(data),
|
|
239
|
-
publicKey,
|
|
302
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
240
303
|
Buffer.from(sig, 'base64url'),
|
|
241
304
|
);
|
|
242
305
|
};
|
|
@@ -258,7 +321,10 @@ describe('KB JWT', () => {
|
|
|
258
321
|
|
|
259
322
|
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
260
323
|
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
261
|
-
const verified = await decoded.
|
|
324
|
+
const verified = await decoded.verifyKB({
|
|
325
|
+
verifier: testVerifier,
|
|
326
|
+
payload,
|
|
327
|
+
});
|
|
262
328
|
expect(verified).toStrictEqual({
|
|
263
329
|
header: {
|
|
264
330
|
typ: KB_JWT_TYP,
|
package/test/app-e2e.spec.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import Crypto from 'node:crypto';
|
|
2
2
|
import { SDJwtInstance, SdJwtPayload } from '../src';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
DisclosureFrame,
|
|
5
|
+
PresentationFrame,
|
|
6
|
+
Signer,
|
|
7
|
+
Verifier,
|
|
8
|
+
} from '@sd-jwt/types';
|
|
4
9
|
import fs from 'fs';
|
|
5
10
|
import path from 'path';
|
|
6
11
|
import { describe, expect, test } from 'vitest';
|
|
@@ -105,7 +110,10 @@ describe('App', () => {
|
|
|
105
110
|
'id',
|
|
106
111
|
]);
|
|
107
112
|
|
|
108
|
-
const presentationFrame =
|
|
113
|
+
const presentationFrame = {
|
|
114
|
+
firstname: true,
|
|
115
|
+
id: true,
|
|
116
|
+
};
|
|
109
117
|
const presentedSDJwt = await sdjwt.present(encodedSdjwt, presentationFrame);
|
|
110
118
|
expect(presentedSDJwt).toBeDefined();
|
|
111
119
|
|
|
@@ -215,7 +223,7 @@ async function JSONtest(filename: string) {
|
|
|
215
223
|
|
|
216
224
|
const presentedSDJwt = await sdjwt.present(
|
|
217
225
|
encodedSdjwt,
|
|
218
|
-
test.
|
|
226
|
+
test.presentationFrames,
|
|
219
227
|
);
|
|
220
228
|
|
|
221
229
|
expect(presentedSDJwt).toBeDefined();
|
|
@@ -236,7 +244,7 @@ async function JSONtest(filename: string) {
|
|
|
236
244
|
type TestJson = {
|
|
237
245
|
claims: SdJwtPayload;
|
|
238
246
|
disclosureFrame: DisclosureFrame<SdJwtPayload>;
|
|
239
|
-
|
|
247
|
+
presentationFrames: PresentationFrame<SdJwtPayload>;
|
|
240
248
|
presenatedClaims: object;
|
|
241
249
|
requiredClaimKeys: string[];
|
|
242
250
|
};
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
"_sd": [0, 1, 2, 3, 4, 5]
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
|
-
"
|
|
11
|
-
"data_types
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
"presentationFrames": {
|
|
11
|
+
"data_types": {
|
|
12
|
+
"0": true,
|
|
13
|
+
"1": true,
|
|
14
|
+
"2": true,
|
|
15
|
+
"3": true,
|
|
16
|
+
"4": true,
|
|
17
|
+
"5": true
|
|
18
|
+
}
|
|
19
|
+
},
|
|
18
20
|
"presenatedClaims": {
|
|
19
21
|
"data_types": [null, 42, 3.14, "foo", ["Test"], { "foo": "bar" }]
|
|
20
22
|
},
|