@sd-jwt/core 0.19.1-next.10 → 0.19.1-next.12
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/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -12
- package/dist/index.mjs +10 -12
- package/package.json +2 -2
- package/src/index.ts +3 -0
- package/src/jwt.ts +7 -0
- package/src/kbjwt.ts +15 -10
- package/src/test/index.spec.ts +134 -0
- package/src/test/kbjwt.spec.ts +211 -0
package/dist/index.d.mts
CHANGED
|
@@ -336,6 +336,12 @@ type VerifierOptions = {
|
|
|
336
336
|
* nonce used to verify the key binding jwt to prevent replay attacks.
|
|
337
337
|
*/
|
|
338
338
|
keyBindingNonce?: string;
|
|
339
|
+
/**
|
|
340
|
+
* disable the verification of the status claim in the payload.
|
|
341
|
+
*
|
|
342
|
+
* @default false
|
|
343
|
+
*/
|
|
344
|
+
disableStatusVerification?: boolean;
|
|
339
345
|
/**
|
|
340
346
|
* any other custom options
|
|
341
347
|
*/
|
|
@@ -376,6 +382,11 @@ declare class KBJwt<Header extends kbHeader = kbHeader, Payload extends kbPayloa
|
|
|
376
382
|
verifier: KbVerifier;
|
|
377
383
|
payload: Record<string, unknown>;
|
|
378
384
|
nonce: string;
|
|
385
|
+
/**
|
|
386
|
+
* Options forwarded to the common JWT verification, e.g. currentDate and
|
|
387
|
+
* skewSeconds used to validate the iat, nbf and exp claims.
|
|
388
|
+
*/
|
|
389
|
+
options?: VerifierOptions;
|
|
379
390
|
}): Promise<{
|
|
380
391
|
payload: Payload;
|
|
381
392
|
header: Header;
|
package/dist/index.d.ts
CHANGED
|
@@ -336,6 +336,12 @@ type VerifierOptions = {
|
|
|
336
336
|
* nonce used to verify the key binding jwt to prevent replay attacks.
|
|
337
337
|
*/
|
|
338
338
|
keyBindingNonce?: string;
|
|
339
|
+
/**
|
|
340
|
+
* disable the verification of the status claim in the payload.
|
|
341
|
+
*
|
|
342
|
+
* @default false
|
|
343
|
+
*/
|
|
344
|
+
disableStatusVerification?: boolean;
|
|
339
345
|
/**
|
|
340
346
|
* any other custom options
|
|
341
347
|
*/
|
|
@@ -376,6 +382,11 @@ declare class KBJwt<Header extends kbHeader = kbHeader, Payload extends kbPayloa
|
|
|
376
382
|
verifier: KbVerifier;
|
|
377
383
|
payload: Record<string, unknown>;
|
|
378
384
|
nonce: string;
|
|
385
|
+
/**
|
|
386
|
+
* Options forwarded to the common JWT verification, e.g. currentDate and
|
|
387
|
+
* skewSeconds used to validate the iat, nbf and exp claims.
|
|
388
|
+
*/
|
|
389
|
+
options?: VerifierOptions;
|
|
379
390
|
}): Promise<{
|
|
380
391
|
payload: Payload;
|
|
381
392
|
header: Header;
|
package/dist/index.js
CHANGED
|
@@ -730,18 +730,13 @@ var KBJwt = class _KBJwt extends Jwt {
|
|
|
730
730
|
!(this.payload.sd_hash || "_sd_hash" in this.payload && this.payload._sd_hash)) {
|
|
731
731
|
throw new SDJWTException("Invalid Key Binding Jwt");
|
|
732
732
|
}
|
|
733
|
-
const data = this.getUnsignedToken();
|
|
734
|
-
const verified = yield values.verifier(
|
|
735
|
-
data,
|
|
736
|
-
this.signature,
|
|
737
|
-
values.payload
|
|
738
|
-
);
|
|
739
|
-
if (!verified) {
|
|
740
|
-
throw new SDJWTException("Verify Error: Invalid JWT Signature");
|
|
741
|
-
}
|
|
742
733
|
if (this.payload.nonce !== values.nonce) {
|
|
743
734
|
throw new SDJWTException("Verify Error: Invalid Nonce");
|
|
744
735
|
}
|
|
736
|
+
yield this.verify(
|
|
737
|
+
(data, sig) => values.verifier(data, sig, values.payload),
|
|
738
|
+
values.options
|
|
739
|
+
);
|
|
745
740
|
return { payload: this.payload, header: this.header };
|
|
746
741
|
});
|
|
747
742
|
}
|
|
@@ -1259,7 +1254,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
1259
1254
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1260
1255
|
verifier: this.userConfig.kbVerifier,
|
|
1261
1256
|
payload,
|
|
1262
|
-
nonce: options.keyBindingNonce
|
|
1257
|
+
nonce: options.keyBindingNonce,
|
|
1258
|
+
options
|
|
1263
1259
|
});
|
|
1264
1260
|
if (!kb) {
|
|
1265
1261
|
throw new Error("signature is not valid");
|
|
@@ -1390,7 +1386,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
1390
1386
|
const kbResult = yield sdjwt.kbJwt.verifyKB({
|
|
1391
1387
|
verifier: this.userConfig.kbVerifier,
|
|
1392
1388
|
payload,
|
|
1393
|
-
nonce: options.keyBindingNonce
|
|
1389
|
+
nonce: options.keyBindingNonce,
|
|
1390
|
+
options
|
|
1394
1391
|
});
|
|
1395
1392
|
if (!kbResult) {
|
|
1396
1393
|
addError(
|
|
@@ -1690,7 +1687,8 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1690
1687
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1691
1688
|
verifier: this.userConfig.kbVerifier,
|
|
1692
1689
|
payload,
|
|
1693
|
-
nonce: options.keyBindingNonce
|
|
1690
|
+
nonce: options.keyBindingNonce,
|
|
1691
|
+
options
|
|
1694
1692
|
});
|
|
1695
1693
|
if (!kb) {
|
|
1696
1694
|
throw new Error("signature is not valid");
|
package/dist/index.mjs
CHANGED
|
@@ -671,18 +671,13 @@ var KBJwt = class _KBJwt extends Jwt {
|
|
|
671
671
|
!(this.payload.sd_hash || "_sd_hash" in this.payload && this.payload._sd_hash)) {
|
|
672
672
|
throw new SDJWTException("Invalid Key Binding Jwt");
|
|
673
673
|
}
|
|
674
|
-
const data = this.getUnsignedToken();
|
|
675
|
-
const verified = yield values.verifier(
|
|
676
|
-
data,
|
|
677
|
-
this.signature,
|
|
678
|
-
values.payload
|
|
679
|
-
);
|
|
680
|
-
if (!verified) {
|
|
681
|
-
throw new SDJWTException("Verify Error: Invalid JWT Signature");
|
|
682
|
-
}
|
|
683
674
|
if (this.payload.nonce !== values.nonce) {
|
|
684
675
|
throw new SDJWTException("Verify Error: Invalid Nonce");
|
|
685
676
|
}
|
|
677
|
+
yield this.verify(
|
|
678
|
+
(data, sig) => values.verifier(data, sig, values.payload),
|
|
679
|
+
values.options
|
|
680
|
+
);
|
|
686
681
|
return { payload: this.payload, header: this.header };
|
|
687
682
|
});
|
|
688
683
|
}
|
|
@@ -1200,7 +1195,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
1200
1195
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1201
1196
|
verifier: this.userConfig.kbVerifier,
|
|
1202
1197
|
payload,
|
|
1203
|
-
nonce: options.keyBindingNonce
|
|
1198
|
+
nonce: options.keyBindingNonce,
|
|
1199
|
+
options
|
|
1204
1200
|
});
|
|
1205
1201
|
if (!kb) {
|
|
1206
1202
|
throw new Error("signature is not valid");
|
|
@@ -1331,7 +1327,8 @@ var _SDJwtInstance = class _SDJwtInstance {
|
|
|
1331
1327
|
const kbResult = yield sdjwt.kbJwt.verifyKB({
|
|
1332
1328
|
verifier: this.userConfig.kbVerifier,
|
|
1333
1329
|
payload,
|
|
1334
|
-
nonce: options.keyBindingNonce
|
|
1330
|
+
nonce: options.keyBindingNonce,
|
|
1331
|
+
options
|
|
1335
1332
|
});
|
|
1336
1333
|
if (!kbResult) {
|
|
1337
1334
|
addError(
|
|
@@ -1631,7 +1628,8 @@ var SDJwtGeneralJSONInstance = class {
|
|
|
1631
1628
|
const kb = yield sdjwt.kbJwt.verifyKB({
|
|
1632
1629
|
verifier: this.userConfig.kbVerifier,
|
|
1633
1630
|
payload,
|
|
1634
|
-
nonce: options.keyBindingNonce
|
|
1631
|
+
nonce: options.keyBindingNonce,
|
|
1632
|
+
options
|
|
1635
1633
|
});
|
|
1636
1634
|
if (!kb) {
|
|
1637
1635
|
throw new Error("signature is not valid");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sd-jwt/core",
|
|
3
|
-
"version": "0.19.1-next.
|
|
3
|
+
"version": "0.19.1-next.12+c9d73e4",
|
|
4
4
|
"description": "sd-jwt draft 7 implementation in typescript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"esm"
|
|
59
59
|
]
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "c9d73e4db2a925e5096988fffe7eded62db1b6a7"
|
|
62
62
|
}
|
package/src/index.ts
CHANGED
|
@@ -242,6 +242,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload, T = unknown> {
|
|
|
242
242
|
verifier: this.userConfig.kbVerifier,
|
|
243
243
|
payload,
|
|
244
244
|
nonce: options.keyBindingNonce,
|
|
245
|
+
options,
|
|
245
246
|
});
|
|
246
247
|
if (!kb) {
|
|
247
248
|
throw new Error('signature is not valid');
|
|
@@ -414,6 +415,7 @@ export class SDJwtInstance<ExtendedPayload extends SdJwtPayload, T = unknown> {
|
|
|
414
415
|
verifier: this.userConfig.kbVerifier,
|
|
415
416
|
payload,
|
|
416
417
|
nonce: options.keyBindingNonce,
|
|
418
|
+
options,
|
|
417
419
|
});
|
|
418
420
|
if (!kbResult) {
|
|
419
421
|
addError(
|
|
@@ -774,6 +776,7 @@ export class SDJwtGeneralJSONInstance<ExtendedPayload extends SdJwtPayload> {
|
|
|
774
776
|
verifier: this.userConfig.kbVerifier,
|
|
775
777
|
payload,
|
|
776
778
|
nonce: options.keyBindingNonce,
|
|
779
|
+
options,
|
|
777
780
|
});
|
|
778
781
|
if (!kb) {
|
|
779
782
|
throw new Error('signature is not valid');
|
package/src/jwt.ts
CHANGED
|
@@ -37,6 +37,13 @@ export type VerifierOptions = {
|
|
|
37
37
|
*/
|
|
38
38
|
keyBindingNonce?: string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* disable the verification of the status claim in the payload.
|
|
42
|
+
*
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
disableStatusVerification?: boolean;
|
|
46
|
+
|
|
40
47
|
/**
|
|
41
48
|
* any other custom options
|
|
42
49
|
*/
|
package/src/kbjwt.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Jwt } from './jwt';
|
|
1
|
+
import { Jwt, type VerifierOptions } from './jwt';
|
|
2
2
|
import {
|
|
3
3
|
KB_JWT_TYP,
|
|
4
4
|
type KbVerifier,
|
|
@@ -17,6 +17,11 @@ export class KBJwt<
|
|
|
17
17
|
verifier: KbVerifier;
|
|
18
18
|
payload: Record<string, unknown>;
|
|
19
19
|
nonce: string;
|
|
20
|
+
/**
|
|
21
|
+
* Options forwarded to the common JWT verification, e.g. currentDate and
|
|
22
|
+
* skewSeconds used to validate the iat, nbf and exp claims.
|
|
23
|
+
*/
|
|
24
|
+
options?: VerifierOptions;
|
|
20
25
|
}) {
|
|
21
26
|
if (!this.header || !this.payload || !this.signature) {
|
|
22
27
|
throw new SDJWTException('Verify Error: Invalid JWT');
|
|
@@ -39,19 +44,19 @@ export class KBJwt<
|
|
|
39
44
|
throw new SDJWTException('Invalid Key Binding Jwt');
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
const data = this.getUnsignedToken();
|
|
43
|
-
const verified = await values.verifier(
|
|
44
|
-
data,
|
|
45
|
-
this.signature,
|
|
46
|
-
values.payload,
|
|
47
|
-
);
|
|
48
|
-
if (!verified) {
|
|
49
|
-
throw new SDJWTException('Verify Error: Invalid JWT Signature');
|
|
50
|
-
}
|
|
51
47
|
if (this.payload.nonce !== values.nonce) {
|
|
52
48
|
throw new SDJWTException('Verify Error: Invalid Nonce');
|
|
53
49
|
}
|
|
54
50
|
|
|
51
|
+
// Delegate signature verification and common JWT claim validation
|
|
52
|
+
// (iat, nbf, exp) to the shared Jwt.verify implementation. The kbVerifier
|
|
53
|
+
// needs the kb+jwt payload (e.g. the holder's cnf key), so we wrap it to
|
|
54
|
+
// forward values.payload instead of the base verifier's options argument.
|
|
55
|
+
await this.verify(
|
|
56
|
+
(data, sig) => values.verifier(data, sig, values.payload),
|
|
57
|
+
values.options,
|
|
58
|
+
);
|
|
59
|
+
|
|
55
60
|
return { payload: this.payload, header: this.header };
|
|
56
61
|
}
|
|
57
62
|
|
package/src/test/index.spec.ts
CHANGED
|
@@ -269,6 +269,140 @@ describe('index', () => {
|
|
|
269
269
|
expect(results).toBeDefined();
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
+
test('verify rejects an expired kbJwt during full verification', async () => {
|
|
273
|
+
const { signer, verifier } = createSignerVerifier();
|
|
274
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
275
|
+
|
|
276
|
+
const kbVerifier: KbVerifier = async (
|
|
277
|
+
data: string,
|
|
278
|
+
sig: string,
|
|
279
|
+
payload: JwtPayload,
|
|
280
|
+
) => {
|
|
281
|
+
if (!payload.cnf) throw Error('key binding not supported');
|
|
282
|
+
return Crypto.verify(
|
|
283
|
+
null,
|
|
284
|
+
Buffer.from(data),
|
|
285
|
+
(await importJWK(payload.cnf.jwk as JWK, 'EdDSA')) as KeyLike,
|
|
286
|
+
Buffer.from(sig, 'base64url'),
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const kbSigner = (data: string) => {
|
|
291
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
292
|
+
return Buffer.from(sig).toString('base64url');
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const sdjwt = new SDJwtInstance<SdJwtPayload>({
|
|
296
|
+
signer,
|
|
297
|
+
signAlg: 'EdDSA',
|
|
298
|
+
verifier,
|
|
299
|
+
hasher: digest,
|
|
300
|
+
saltGenerator: generateSalt,
|
|
301
|
+
kbSigner,
|
|
302
|
+
kbVerifier,
|
|
303
|
+
kbSignAlg: 'EdDSA',
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const now = Math.floor(Date.now() / 1000);
|
|
307
|
+
const credential = await sdjwt.issue(
|
|
308
|
+
{
|
|
309
|
+
foo: 'bar',
|
|
310
|
+
iat: now,
|
|
311
|
+
cnf: { jwk: await exportJWK(publicKey) },
|
|
312
|
+
},
|
|
313
|
+
{ _sd: ['foo'] },
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const presentation = await sdjwt.present(
|
|
317
|
+
credential,
|
|
318
|
+
{ foo: true },
|
|
319
|
+
{
|
|
320
|
+
kb: {
|
|
321
|
+
payload: {
|
|
322
|
+
aud: '1',
|
|
323
|
+
iat: now - 7200,
|
|
324
|
+
nonce: '342',
|
|
325
|
+
// kb+jwt expired one hour ago
|
|
326
|
+
exp: now - 3600,
|
|
327
|
+
} as never,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
await expect(
|
|
333
|
+
sdjwt.verify(presentation, {
|
|
334
|
+
requiredClaimKeys: ['foo'],
|
|
335
|
+
keyBindingNonce: '342',
|
|
336
|
+
}),
|
|
337
|
+
).rejects.toThrow('Verify Error: JWT is expired');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('verify accepts a kbJwt with a future exp during full verification', async () => {
|
|
341
|
+
const { signer, verifier } = createSignerVerifier();
|
|
342
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
343
|
+
|
|
344
|
+
const kbVerifier: KbVerifier = async (
|
|
345
|
+
data: string,
|
|
346
|
+
sig: string,
|
|
347
|
+
payload: JwtPayload,
|
|
348
|
+
) => {
|
|
349
|
+
if (!payload.cnf) throw Error('key binding not supported');
|
|
350
|
+
return Crypto.verify(
|
|
351
|
+
null,
|
|
352
|
+
Buffer.from(data),
|
|
353
|
+
(await importJWK(payload.cnf.jwk as JWK, 'EdDSA')) as KeyLike,
|
|
354
|
+
Buffer.from(sig, 'base64url'),
|
|
355
|
+
);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const kbSigner = (data: string) => {
|
|
359
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
360
|
+
return Buffer.from(sig).toString('base64url');
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const sdjwt = new SDJwtInstance<SdJwtPayload>({
|
|
364
|
+
signer,
|
|
365
|
+
signAlg: 'EdDSA',
|
|
366
|
+
verifier,
|
|
367
|
+
hasher: digest,
|
|
368
|
+
saltGenerator: generateSalt,
|
|
369
|
+
kbSigner,
|
|
370
|
+
kbVerifier,
|
|
371
|
+
kbSignAlg: 'EdDSA',
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const now = Math.floor(Date.now() / 1000);
|
|
375
|
+
const credential = await sdjwt.issue(
|
|
376
|
+
{
|
|
377
|
+
foo: 'bar',
|
|
378
|
+
iat: now,
|
|
379
|
+
cnf: { jwk: await exportJWK(publicKey) },
|
|
380
|
+
},
|
|
381
|
+
{ _sd: ['foo'] },
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
const presentation = await sdjwt.present(
|
|
385
|
+
credential,
|
|
386
|
+
{ foo: true },
|
|
387
|
+
{
|
|
388
|
+
kb: {
|
|
389
|
+
payload: {
|
|
390
|
+
aud: '1',
|
|
391
|
+
iat: now,
|
|
392
|
+
nonce: '342',
|
|
393
|
+
exp: now + 3600,
|
|
394
|
+
} as never,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
const results = await sdjwt.verify(presentation, {
|
|
400
|
+
requiredClaimKeys: ['foo'],
|
|
401
|
+
keyBindingNonce: '342',
|
|
402
|
+
});
|
|
403
|
+
expect(results.kb).toBeDefined();
|
|
404
|
+
});
|
|
405
|
+
|
|
272
406
|
test('Hasher not found', async () => {
|
|
273
407
|
const sdjwt = new SDJwtInstance<SdJwtPayload>({});
|
|
274
408
|
try {
|
package/src/test/kbjwt.spec.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type JwtPayload,
|
|
7
7
|
KB_JWT_TYP,
|
|
8
8
|
type KbVerifier,
|
|
9
|
+
type kbPayload,
|
|
9
10
|
type Signer,
|
|
10
11
|
} from '../types';
|
|
11
12
|
import type { SDJWTException } from '../utils';
|
|
@@ -287,6 +288,216 @@ describe('KB JWT', () => {
|
|
|
287
288
|
}
|
|
288
289
|
});
|
|
289
290
|
|
|
291
|
+
test('verify failed with expired exp claim', async () => {
|
|
292
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
293
|
+
const testSigner: Signer = async (data: string) => {
|
|
294
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
295
|
+
return Buffer.from(sig).toString('base64url');
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const payload = {
|
|
299
|
+
cnf: {
|
|
300
|
+
jwk: await exportJWK(publicKey),
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
const testVerifier: KbVerifier = async (
|
|
304
|
+
data: string,
|
|
305
|
+
sig: string,
|
|
306
|
+
payload: JwtPayload,
|
|
307
|
+
) => {
|
|
308
|
+
const publicKey = payload.cnf?.jwk;
|
|
309
|
+
return Crypto.verify(
|
|
310
|
+
null,
|
|
311
|
+
Buffer.from(data),
|
|
312
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
313
|
+
Buffer.from(sig, 'base64url'),
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const kbJwt = new KBJwt({
|
|
318
|
+
header: {
|
|
319
|
+
typ: KB_JWT_TYP,
|
|
320
|
+
alg: 'EdDSA',
|
|
321
|
+
},
|
|
322
|
+
payload: {
|
|
323
|
+
iat: 1,
|
|
324
|
+
aud: 'aud',
|
|
325
|
+
nonce: 'nonce',
|
|
326
|
+
sd_hash: 'hash',
|
|
327
|
+
// expired one hour before the current date used below
|
|
328
|
+
exp: 1000,
|
|
329
|
+
} as kbPayload,
|
|
330
|
+
});
|
|
331
|
+
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
332
|
+
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
333
|
+
|
|
334
|
+
await expect(
|
|
335
|
+
decoded.verifyKB({
|
|
336
|
+
verifier: testVerifier,
|
|
337
|
+
payload,
|
|
338
|
+
nonce: 'nonce',
|
|
339
|
+
options: { currentDate: 5000 },
|
|
340
|
+
}),
|
|
341
|
+
).rejects.toThrow('Verify Error: JWT is expired');
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('verify failed with iat in the future', async () => {
|
|
345
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
346
|
+
const testSigner: Signer = async (data: string) => {
|
|
347
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
348
|
+
return Buffer.from(sig).toString('base64url');
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const payload = {
|
|
352
|
+
cnf: {
|
|
353
|
+
jwk: await exportJWK(publicKey),
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
const testVerifier: KbVerifier = async (
|
|
357
|
+
data: string,
|
|
358
|
+
sig: string,
|
|
359
|
+
payload: JwtPayload,
|
|
360
|
+
) => {
|
|
361
|
+
const publicKey = payload.cnf?.jwk;
|
|
362
|
+
return Crypto.verify(
|
|
363
|
+
null,
|
|
364
|
+
Buffer.from(data),
|
|
365
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
366
|
+
Buffer.from(sig, 'base64url'),
|
|
367
|
+
);
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const kbJwt = new KBJwt({
|
|
371
|
+
header: {
|
|
372
|
+
typ: KB_JWT_TYP,
|
|
373
|
+
alg: 'EdDSA',
|
|
374
|
+
},
|
|
375
|
+
payload: {
|
|
376
|
+
iat: 5000,
|
|
377
|
+
aud: 'aud',
|
|
378
|
+
nonce: 'nonce',
|
|
379
|
+
sd_hash: 'hash',
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
383
|
+
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
384
|
+
|
|
385
|
+
await expect(
|
|
386
|
+
decoded.verifyKB({
|
|
387
|
+
verifier: testVerifier,
|
|
388
|
+
payload,
|
|
389
|
+
nonce: 'nonce',
|
|
390
|
+
// iat (5000) is after the current date (1000)
|
|
391
|
+
options: { currentDate: 1000 },
|
|
392
|
+
}),
|
|
393
|
+
).rejects.toThrow('Verify Error: JWT is not yet valid');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test('verify failed with nbf in the future', async () => {
|
|
397
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
398
|
+
const testSigner: Signer = async (data: string) => {
|
|
399
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
400
|
+
return Buffer.from(sig).toString('base64url');
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const payload = {
|
|
404
|
+
cnf: {
|
|
405
|
+
jwk: await exportJWK(publicKey),
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
const testVerifier: KbVerifier = async (
|
|
409
|
+
data: string,
|
|
410
|
+
sig: string,
|
|
411
|
+
payload: JwtPayload,
|
|
412
|
+
) => {
|
|
413
|
+
const publicKey = payload.cnf?.jwk;
|
|
414
|
+
return Crypto.verify(
|
|
415
|
+
null,
|
|
416
|
+
Buffer.from(data),
|
|
417
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
418
|
+
Buffer.from(sig, 'base64url'),
|
|
419
|
+
);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const kbJwt = new KBJwt({
|
|
423
|
+
header: {
|
|
424
|
+
typ: KB_JWT_TYP,
|
|
425
|
+
alg: 'EdDSA',
|
|
426
|
+
},
|
|
427
|
+
payload: {
|
|
428
|
+
iat: 1,
|
|
429
|
+
aud: 'aud',
|
|
430
|
+
nonce: 'nonce',
|
|
431
|
+
sd_hash: 'hash',
|
|
432
|
+
nbf: 5000,
|
|
433
|
+
} as kbPayload,
|
|
434
|
+
});
|
|
435
|
+
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
436
|
+
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
437
|
+
|
|
438
|
+
await expect(
|
|
439
|
+
decoded.verifyKB({
|
|
440
|
+
verifier: testVerifier,
|
|
441
|
+
payload,
|
|
442
|
+
nonce: 'nonce',
|
|
443
|
+
options: { currentDate: 1000 },
|
|
444
|
+
}),
|
|
445
|
+
).rejects.toThrow('Verify Error: JWT is not yet valid');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('verify succeeds for expired exp within the allowed skew', async () => {
|
|
449
|
+
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
450
|
+
const testSigner: Signer = async (data: string) => {
|
|
451
|
+
const sig = Crypto.sign(null, Buffer.from(data), privateKey);
|
|
452
|
+
return Buffer.from(sig).toString('base64url');
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const payload = {
|
|
456
|
+
cnf: {
|
|
457
|
+
jwk: await exportJWK(publicKey),
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
const testVerifier: KbVerifier = async (
|
|
461
|
+
data: string,
|
|
462
|
+
sig: string,
|
|
463
|
+
payload: JwtPayload,
|
|
464
|
+
) => {
|
|
465
|
+
const publicKey = payload.cnf?.jwk;
|
|
466
|
+
return Crypto.verify(
|
|
467
|
+
null,
|
|
468
|
+
Buffer.from(data),
|
|
469
|
+
(await importJWK(publicKey as JWK, 'EdDSA')) as KeyLike,
|
|
470
|
+
Buffer.from(sig, 'base64url'),
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const kbJwt = new KBJwt({
|
|
475
|
+
header: {
|
|
476
|
+
typ: KB_JWT_TYP,
|
|
477
|
+
alg: 'EdDSA',
|
|
478
|
+
},
|
|
479
|
+
payload: {
|
|
480
|
+
iat: 1,
|
|
481
|
+
aud: 'aud',
|
|
482
|
+
nonce: 'nonce',
|
|
483
|
+
sd_hash: 'hash',
|
|
484
|
+
exp: 1000,
|
|
485
|
+
} as kbPayload,
|
|
486
|
+
});
|
|
487
|
+
const encodedKbJwt = await kbJwt.sign(testSigner);
|
|
488
|
+
const decoded = KBJwt.fromKBEncode(encodedKbJwt);
|
|
489
|
+
|
|
490
|
+
// exp (1000) is before the current date (1100) but within the 200s skew
|
|
491
|
+
const verified = await decoded.verifyKB({
|
|
492
|
+
verifier: testVerifier,
|
|
493
|
+
payload,
|
|
494
|
+
nonce: 'nonce',
|
|
495
|
+
options: { currentDate: 1100, skewSeconds: 200 },
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
expect(verified.payload.exp).toBe(1000);
|
|
499
|
+
});
|
|
500
|
+
|
|
290
501
|
test('compatibility test for version 06', async () => {
|
|
291
502
|
const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
|
|
292
503
|
const testSigner: Signer = async (data: string) => {
|