@sd-jwt/core 0.3.0 → 2.0.2-next.26

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.
Files changed (107) hide show
  1. package/LICENSE +201 -0
  2. package/dist/index.d.mts +106 -0
  3. package/dist/index.d.ts +106 -0
  4. package/dist/index.js +606 -0
  5. package/dist/index.mjs +586 -0
  6. package/package.json +60 -48
  7. package/src/decoy.ts +15 -0
  8. package/src/index.ts +235 -0
  9. package/src/jwt.ts +107 -0
  10. package/src/kbjwt.ts +45 -0
  11. package/src/sdjwt.ts +318 -0
  12. package/src/test/decoy.spec.ts +30 -0
  13. package/src/test/index.spec.ts +379 -0
  14. package/src/test/jwt.spec.ts +141 -0
  15. package/src/test/kbjwt.spec.ts +275 -0
  16. package/src/test/pass.spec.ts +6 -0
  17. package/src/test/sdjwt.spec.ts +382 -0
  18. package/test/app-e2e.spec.ts +248 -0
  19. package/test/array_data_types.json +29 -0
  20. package/test/array_full_sd.json +21 -0
  21. package/test/array_in_sd.json +13 -0
  22. package/test/array_nested_in_plain.json +20 -0
  23. package/test/array_none_disclosed.json +17 -0
  24. package/test/array_of_nulls.json +15 -0
  25. package/test/array_of_objects.json +58 -0
  26. package/test/array_of_scalars.json +15 -0
  27. package/test/array_recursive_sd.json +35 -0
  28. package/test/array_recursive_sd_some_disclosed.json +55 -0
  29. package/test/complex.json +43 -0
  30. package/test/header_mod.json +44 -0
  31. package/test/json_serialization.json +44 -0
  32. package/test/key_binding.json +44 -0
  33. package/test/no_sd.json +36 -0
  34. package/test/object_data_types.json +60 -0
  35. package/test/recursions.json +98 -0
  36. package/tsconfig.json +7 -0
  37. package/vitest.config.mts +4 -0
  38. package/README.md +0 -97
  39. package/build/index.d.ts +0 -13
  40. package/build/index.js +0 -20
  41. package/build/index.js.map +0 -1
  42. package/build/jwt/error.d.ts +0 -2
  43. package/build/jwt/error.js +0 -7
  44. package/build/jwt/error.js.map +0 -1
  45. package/build/jwt/index.d.ts +0 -2
  46. package/build/jwt/index.js +0 -19
  47. package/build/jwt/index.js.map +0 -1
  48. package/build/jwt/jwt.d.ts +0 -208
  49. package/build/jwt/jwt.js +0 -325
  50. package/build/jwt/jwt.js.map +0 -1
  51. package/build/keyBinding/index.d.ts +0 -1
  52. package/build/keyBinding/index.js +0 -18
  53. package/build/keyBinding/index.js.map +0 -1
  54. package/build/keyBinding/keyBinding.d.ts +0 -64
  55. package/build/keyBinding/keyBinding.js +0 -119
  56. package/build/keyBinding/keyBinding.js.map +0 -1
  57. package/build/sdJwt/decoys.d.ts +0 -3
  58. package/build/sdJwt/decoys.js +0 -35
  59. package/build/sdJwt/decoys.js.map +0 -1
  60. package/build/sdJwt/disclosureFrame.d.ts +0 -8
  61. package/build/sdJwt/disclosureFrame.js +0 -87
  62. package/build/sdJwt/disclosureFrame.js.map +0 -1
  63. package/build/sdJwt/disclosures.d.ts +0 -33
  64. package/build/sdJwt/disclosures.js +0 -114
  65. package/build/sdJwt/disclosures.js.map +0 -1
  66. package/build/sdJwt/error.d.ts +0 -2
  67. package/build/sdJwt/error.js +0 -7
  68. package/build/sdJwt/error.js.map +0 -1
  69. package/build/sdJwt/index.d.ts +0 -6
  70. package/build/sdJwt/index.js +0 -23
  71. package/build/sdJwt/index.js.map +0 -1
  72. package/build/sdJwt/sdJwt.d.ts +0 -206
  73. package/build/sdJwt/sdJwt.js +0 -442
  74. package/build/sdJwt/sdJwt.js.map +0 -1
  75. package/build/sdJwt/types.d.ts +0 -5
  76. package/build/sdJwt/types.js +0 -3
  77. package/build/sdJwt/types.js.map +0 -1
  78. package/build/sdJwtVc/error.d.ts +0 -2
  79. package/build/sdJwtVc/error.js +0 -7
  80. package/build/sdJwtVc/error.js.map +0 -1
  81. package/build/sdJwtVc/index.d.ts +0 -2
  82. package/build/sdJwtVc/index.js +0 -19
  83. package/build/sdJwtVc/index.js.map +0 -1
  84. package/build/sdJwtVc/sdJwtVc.d.ts +0 -47
  85. package/build/sdJwtVc/sdJwtVc.js +0 -149
  86. package/build/sdJwtVc/sdJwtVc.js.map +0 -1
  87. package/build/signatureAndEncryptionAlgorithm.d.ts +0 -105
  88. package/build/signatureAndEncryptionAlgorithm.js +0 -110
  89. package/build/signatureAndEncryptionAlgorithm.js.map +0 -1
  90. package/build/types/disclosure.d.ts +0 -5
  91. package/build/types/disclosure.js +0 -3
  92. package/build/types/disclosure.js.map +0 -1
  93. package/build/types/index.d.ts +0 -5
  94. package/build/types/index.js +0 -22
  95. package/build/types/index.js.map +0 -1
  96. package/build/types/saltGenerator.d.ts +0 -17
  97. package/build/types/saltGenerator.js +0 -3
  98. package/build/types/saltGenerator.js.map +0 -1
  99. package/build/types/signer.d.ts +0 -2
  100. package/build/types/signer.js +0 -3
  101. package/build/types/signer.js.map +0 -1
  102. package/build/types/utils.d.ts +0 -2
  103. package/build/types/utils.js +0 -3
  104. package/build/types/utils.js.map +0 -1
  105. package/build/types/verifier.d.ts +0 -14
  106. package/build/types/verifier.js +0 -3
  107. package/build/types/verifier.js.map +0 -1
package/src/sdjwt.ts ADDED
@@ -0,0 +1,318 @@
1
+ import { createDecoy } from './decoy';
2
+ import { SDJWTException, Disclosure } from '@sd-jwt/utils';
3
+ import { Jwt } from './jwt';
4
+ import { KBJwt } from './kbjwt';
5
+ import {
6
+ DisclosureFrame,
7
+ Hasher,
8
+ HasherAndAlg,
9
+ SDJWTCompact,
10
+ SD_DECOY,
11
+ SD_DIGEST,
12
+ SD_LIST_KEY,
13
+ SD_SEPARATOR,
14
+ SaltGenerator,
15
+ kbHeader,
16
+ kbPayload,
17
+ } from '@sd-jwt/types';
18
+ import { createHashMapping, getSDAlgAndPayload, unpack } from '@sd-jwt/decode';
19
+
20
+ export type SDJwtData<
21
+ Header extends Record<string, unknown>,
22
+ Payload extends Record<string, unknown>,
23
+ KBHeader extends kbHeader = kbHeader,
24
+ KBPayload extends kbPayload = kbPayload,
25
+ > = {
26
+ jwt?: Jwt<Header, Payload>;
27
+ disclosures?: Array<Disclosure>;
28
+ kbJwt?: KBJwt<KBHeader, KBPayload>;
29
+ };
30
+
31
+ export class SDJwt<
32
+ Header extends Record<string, unknown> = Record<string, unknown>,
33
+ Payload extends Record<string, unknown> = Record<string, unknown>,
34
+ KBHeader extends kbHeader = kbHeader,
35
+ KBPayload extends kbPayload = kbPayload,
36
+ > {
37
+ public jwt?: Jwt<Header, Payload>;
38
+ public disclosures?: Array<Disclosure>;
39
+ public kbJwt?: KBJwt<KBHeader, KBPayload>;
40
+
41
+ constructor(data?: SDJwtData<Header, Payload, KBHeader, KBPayload>) {
42
+ this.jwt = data?.jwt;
43
+ this.disclosures = data?.disclosures;
44
+ this.kbJwt = data?.kbJwt;
45
+ }
46
+
47
+ public static async decodeSDJwt<
48
+ Header extends Record<string, unknown> = Record<string, unknown>,
49
+ Payload extends Record<string, unknown> = Record<string, unknown>,
50
+ KBHeader extends kbHeader = kbHeader,
51
+ KBPayload extends kbPayload = kbPayload,
52
+ >(
53
+ sdjwt: SDJWTCompact,
54
+ hasher: Hasher,
55
+ ): Promise<{
56
+ jwt: Jwt<Header, Payload>;
57
+ disclosures: Array<Disclosure>;
58
+ kbJwt?: KBJwt<KBHeader, KBPayload>;
59
+ }> {
60
+ const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
61
+ const jwt = Jwt.fromEncode<Header, Payload>(encodedJwt);
62
+
63
+ if (!jwt.payload) {
64
+ throw new Error('Payload is undefined on the JWT. Invalid state reached');
65
+ }
66
+
67
+ if (encodedDisclosures.length === 0) {
68
+ return {
69
+ jwt,
70
+ disclosures: [],
71
+ };
72
+ }
73
+
74
+ const encodedKeyBindingJwt = encodedDisclosures.pop();
75
+ const kbJwt = encodedKeyBindingJwt
76
+ ? KBJwt.fromKBEncode<KBHeader, KBPayload>(encodedKeyBindingJwt)
77
+ : undefined;
78
+
79
+ const { _sd_alg } = getSDAlgAndPayload(jwt.payload);
80
+
81
+ const disclosures = await Promise.all(
82
+ (encodedDisclosures as Array<string>).map((ed) =>
83
+ Disclosure.fromEncode(ed, { alg: _sd_alg, hasher }),
84
+ ),
85
+ );
86
+
87
+ return {
88
+ jwt,
89
+ disclosures,
90
+ kbJwt,
91
+ };
92
+ }
93
+
94
+ public static async fromEncode<
95
+ Header extends Record<string, unknown> = Record<string, unknown>,
96
+ Payload extends Record<string, unknown> = Record<string, unknown>,
97
+ KBHeader extends kbHeader = kbHeader,
98
+ KBPayload extends kbPayload = kbPayload,
99
+ >(
100
+ encodedSdJwt: SDJWTCompact,
101
+ hasher: Hasher,
102
+ ): Promise<SDJwt<Header, Payload>> {
103
+ const { jwt, disclosures, kbJwt } = await SDJwt.decodeSDJwt<
104
+ Header,
105
+ Payload,
106
+ KBHeader,
107
+ KBPayload
108
+ >(encodedSdJwt, hasher);
109
+
110
+ return new SDJwt<Header, Payload, KBHeader, KBPayload>({
111
+ jwt,
112
+ disclosures,
113
+ kbJwt,
114
+ });
115
+ }
116
+
117
+ public async present(keys: string[], hasher: Hasher): Promise<SDJWTCompact> {
118
+ if (!this.jwt?.payload || !this.disclosures) {
119
+ throw new SDJWTException('Invalid sd-jwt: jwt or disclosures is missing');
120
+ }
121
+ const { _sd_alg: alg } = getSDAlgAndPayload(this.jwt.payload);
122
+ const hash = { alg, hasher };
123
+ const hashmap = await createHashMapping(this.disclosures, hash);
124
+ const { disclosureKeymap } = await unpack(
125
+ this.jwt.payload,
126
+ this.disclosures,
127
+ hasher,
128
+ );
129
+
130
+ const presentableKeys = Object.keys(disclosureKeymap);
131
+ const missingKeys = keys.filter((k) => !presentableKeys.includes(k));
132
+ if (missingKeys.length > 0) {
133
+ throw new SDJWTException(
134
+ `Invalid sd-jwt: invalid present keys: ${missingKeys.join(', ')}`,
135
+ );
136
+ }
137
+
138
+ const disclosures = keys.map((k) => hashmap[disclosureKeymap[k]]);
139
+ const presentSDJwt = new SDJwt({
140
+ jwt: this.jwt,
141
+ disclosures,
142
+ kbJwt: this.kbJwt,
143
+ });
144
+ return presentSDJwt.encodeSDJwt();
145
+ }
146
+
147
+ public encodeSDJwt(): SDJWTCompact {
148
+ const data: string[] = [];
149
+
150
+ if (!this.jwt) {
151
+ throw new SDJWTException('Invalid sd-jwt: jwt is missing');
152
+ }
153
+
154
+ const encodedJwt = this.jwt.encodeJwt();
155
+ data.push(encodedJwt);
156
+
157
+ if (this.disclosures && this.disclosures.length > 0) {
158
+ const encodeddisclosures = this.disclosures
159
+ .map((dc) => dc.encode())
160
+ .join(SD_SEPARATOR);
161
+ data.push(encodeddisclosures);
162
+ }
163
+
164
+ data.push(this.kbJwt ? this.kbJwt.encodeJwt() : '');
165
+ return data.join(SD_SEPARATOR);
166
+ }
167
+
168
+ public async keys(hasher: Hasher): Promise<string[]> {
169
+ return listKeys(await this.getClaims(hasher)).sort();
170
+ }
171
+
172
+ public async presentableKeys(hasher: Hasher): Promise<string[]> {
173
+ if (!this.jwt?.payload || !this.disclosures) {
174
+ throw new SDJWTException('Invalid sd-jwt: jwt or disclosures is missing');
175
+ }
176
+ const { disclosureKeymap } = await unpack(
177
+ this.jwt?.payload,
178
+ this.disclosures,
179
+ hasher,
180
+ );
181
+ return Object.keys(disclosureKeymap).sort();
182
+ }
183
+
184
+ public async getClaims<T>(hasher: Hasher): Promise<T> {
185
+ if (!this.jwt?.payload || !this.disclosures) {
186
+ throw new SDJWTException('Invalid sd-jwt: jwt or disclosures is missing');
187
+ }
188
+ const { unpackedObj } = await unpack(
189
+ this.jwt.payload,
190
+ this.disclosures,
191
+ hasher,
192
+ );
193
+ return unpackedObj as T;
194
+ }
195
+ }
196
+
197
+ export const listKeys = (obj: Record<string, unknown>, prefix = '') => {
198
+ const keys: string[] = [];
199
+ for (const key in obj) {
200
+ if (obj[key] === undefined) continue;
201
+ const newKey = prefix ? `${prefix}.${key}` : key;
202
+ keys.push(newKey);
203
+
204
+ if (obj[key] && typeof obj[key] === 'object' && obj[key] !== null) {
205
+ keys.push(...listKeys(obj[key] as Record<string, unknown>, newKey));
206
+ }
207
+ }
208
+ return keys;
209
+ };
210
+
211
+ export const pack = async <T extends Record<string, unknown>>(
212
+ claims: T,
213
+ disclosureFrame: DisclosureFrame<T> | undefined,
214
+ hash: HasherAndAlg,
215
+ saltGenerator: SaltGenerator,
216
+ ): Promise<{
217
+ packedClaims: Record<string, unknown> | Array<Record<string, unknown>>;
218
+ disclosures: Array<Disclosure>;
219
+ }> => {
220
+ if (!disclosureFrame) {
221
+ return {
222
+ packedClaims: claims,
223
+ disclosures: [],
224
+ };
225
+ }
226
+
227
+ const sd = disclosureFrame[SD_DIGEST] ?? [];
228
+ const decoyCount = disclosureFrame[SD_DECOY] ?? 0;
229
+
230
+ if (Array.isArray(claims)) {
231
+ const packedClaims: Array<Record<typeof SD_LIST_KEY, string>> = [];
232
+ const disclosures: Array<Disclosure> = [];
233
+ const recursivePackedClaims: Record<number, unknown> = {};
234
+
235
+ for (const key in disclosureFrame) {
236
+ if (key !== SD_DIGEST) {
237
+ const idx = parseInt(key);
238
+ const packed = await pack(
239
+ claims[idx],
240
+ disclosureFrame[idx],
241
+ hash,
242
+ saltGenerator,
243
+ );
244
+ recursivePackedClaims[idx] = packed.packedClaims;
245
+ disclosures.push(...packed.disclosures);
246
+ }
247
+ }
248
+
249
+ for (let i = 0; i < claims.length; i++) {
250
+ const claim = recursivePackedClaims[i]
251
+ ? recursivePackedClaims[i]
252
+ : claims[i];
253
+ // TODO: should this actually be the `i` or `claim`?
254
+ // @ts-ignore
255
+ if (sd.includes(i)) {
256
+ const salt = await saltGenerator(16);
257
+ const disclosure = new Disclosure([salt, claim]);
258
+ const digest = await disclosure.digest(hash);
259
+ packedClaims.push({ [SD_LIST_KEY]: digest });
260
+ disclosures.push(disclosure);
261
+ } else {
262
+ packedClaims.push(claim);
263
+ }
264
+ }
265
+ for (let j = 0; j < decoyCount; j++) {
266
+ const decoyDigest = await createDecoy(hash, saltGenerator);
267
+ packedClaims.push({ [SD_LIST_KEY]: decoyDigest });
268
+ }
269
+ return { packedClaims, disclosures };
270
+ }
271
+
272
+ const packedClaims: Record<string, unknown> = {};
273
+ const disclosures: Array<Disclosure> = [];
274
+ const recursivePackedClaims: Record<string, unknown> = {};
275
+
276
+ for (const key in disclosureFrame) {
277
+ if (key !== SD_DIGEST) {
278
+ const packed = await pack(
279
+ // @ts-ignore
280
+ claims[key],
281
+ disclosureFrame[key],
282
+ hash,
283
+ saltGenerator,
284
+ );
285
+ recursivePackedClaims[key] = packed.packedClaims;
286
+ disclosures.push(...packed.disclosures);
287
+ }
288
+ }
289
+
290
+ const _sd: string[] = [];
291
+
292
+ for (const key in claims) {
293
+ const claim = recursivePackedClaims[key]
294
+ ? recursivePackedClaims[key]
295
+ : claims[key];
296
+ // @ts-ignore
297
+ if (sd.includes(key)) {
298
+ const salt = await saltGenerator(16);
299
+ const disclosure = new Disclosure([salt, key, claim]);
300
+ const digest = await disclosure.digest(hash);
301
+
302
+ _sd.push(digest);
303
+ disclosures.push(disclosure);
304
+ } else {
305
+ packedClaims[key] = claim;
306
+ }
307
+ }
308
+
309
+ for (let j = 0; j < decoyCount; j++) {
310
+ const decoyDigest = await createDecoy(hash, saltGenerator);
311
+ _sd.push(decoyDigest);
312
+ }
313
+
314
+ if (_sd.length > 0) {
315
+ packedClaims[SD_DIGEST] = _sd.sort();
316
+ }
317
+ return { packedClaims, disclosures };
318
+ };
@@ -0,0 +1,30 @@
1
+ import { createDecoy } from '../decoy';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { Base64urlEncode } from '@sd-jwt/utils';
4
+ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
5
+
6
+ const hash = {
7
+ hasher: digest,
8
+ alg: 'SHA256',
9
+ };
10
+
11
+ describe('Decoy', () => {
12
+ test('decoy', async () => {
13
+ const decoyValue = await createDecoy(hash, generateSalt);
14
+ expect(decoyValue.length).toBe(43);
15
+ });
16
+
17
+ // ref https://datatracker.ietf.org/doc/draft-ietf-oauth-selective-disclosure-jwt/07/
18
+ // *Claim email*:
19
+ // * SHA-256 Hash: JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE
20
+ // * Disclosure: WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ
21
+ // * Contents: ["6Ij7tM-a5iVPGboS5tmvVA", "email", "johndoe@example.com"]
22
+ test('apply hasher and saltGenerator', async () => {
23
+ const decoyValue = await createDecoy(hash, () =>
24
+ Base64urlEncode(
25
+ '["6Ij7tM-a5iVPGboS5tmvVA", "email", "johndoe@example.com"]',
26
+ ),
27
+ );
28
+ expect(decoyValue).toBe('JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE');
29
+ });
30
+ });
@@ -0,0 +1,379 @@
1
+ import { SDJwtInstance } from '../index';
2
+ import { Signer, Verifier } from '@sd-jwt/types';
3
+ import Crypto from 'node:crypto';
4
+ import { describe, expect, test } from 'vitest';
5
+ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
6
+
7
+ export const createSignerVerifier = () => {
8
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
9
+ const signer: Signer = async (data: string) => {
10
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
11
+ return Buffer.from(sig).toString('base64url');
12
+ };
13
+ const verifier: Verifier = async (data: string, sig: string) => {
14
+ return Crypto.verify(
15
+ null,
16
+ Buffer.from(data),
17
+ publicKey,
18
+ Buffer.from(sig, 'base64url'),
19
+ );
20
+ };
21
+ return { signer, verifier };
22
+ };
23
+
24
+ describe('index', () => {
25
+ test('create', async () => {
26
+ const sdjwt = new SDJwtInstance();
27
+ expect(sdjwt).toBeDefined();
28
+ });
29
+
30
+ test('kbJwt', async () => {
31
+ const { signer, verifier } = createSignerVerifier();
32
+ const sdjwt = new SDJwtInstance({
33
+ signer,
34
+ signAlg: 'EdDSA',
35
+ verifier,
36
+ hasher: digest,
37
+ saltGenerator: generateSalt,
38
+ kbSigner: signer,
39
+ kbSignAlg: 'EdDSA',
40
+ });
41
+ const credential = await sdjwt.issue(
42
+ {
43
+ foo: 'bar',
44
+ },
45
+ {
46
+ _sd: ['foo'],
47
+ },
48
+ );
49
+
50
+ expect(credential).toBeDefined();
51
+
52
+ const presentation = await sdjwt.present(credential, ['foo'], {
53
+ kb: {
54
+ payload: {
55
+ sd_hash: 'sha-256',
56
+ aud: '1',
57
+ iat: 1,
58
+ nonce: '342',
59
+ },
60
+ },
61
+ });
62
+
63
+ expect(presentation).toBeDefined();
64
+ });
65
+
66
+ test('issue', async () => {
67
+ const { signer, verifier } = createSignerVerifier();
68
+ const sdjwt = new SDJwtInstance({
69
+ signer,
70
+ signAlg: 'EdDSA',
71
+ verifier,
72
+ hasher: digest,
73
+ saltGenerator: generateSalt,
74
+ });
75
+ const credential = await sdjwt.issue(
76
+ {
77
+ foo: 'bar',
78
+ },
79
+ {
80
+ _sd: ['foo'],
81
+ },
82
+ );
83
+
84
+ expect(credential).toBeDefined();
85
+ });
86
+
87
+ test('verify failed', async () => {
88
+ const { signer } = createSignerVerifier();
89
+ const { publicKey } = Crypto.generateKeyPairSync('ed25519');
90
+ const failedverifier: Verifier = async (data: string, sig: string) => {
91
+ return Crypto.verify(
92
+ null,
93
+ Buffer.from(data),
94
+ publicKey,
95
+ Buffer.from(sig, 'base64url'),
96
+ );
97
+ };
98
+
99
+ const sdjwt = new SDJwtInstance({
100
+ signer,
101
+ signAlg: 'EdDSA',
102
+ verifier: failedverifier,
103
+ hasher: digest,
104
+ saltGenerator: generateSalt,
105
+ });
106
+
107
+ const credential = await sdjwt.issue(
108
+ {
109
+ foo: 'bar',
110
+ },
111
+ {
112
+ _sd: ['foo'],
113
+ },
114
+ );
115
+
116
+ try {
117
+ await sdjwt.verify(credential);
118
+ } catch (e) {
119
+ expect(e).toBeDefined();
120
+ }
121
+ });
122
+
123
+ test('verify failed with kbJwt', async () => {
124
+ const { signer, verifier } = createSignerVerifier();
125
+ const { publicKey } = Crypto.generateKeyPairSync('ed25519');
126
+ const failedverifier: Verifier = async (data: string, sig: string) => {
127
+ return Crypto.verify(
128
+ null,
129
+ Buffer.from(data),
130
+ publicKey,
131
+ Buffer.from(sig, 'base64url'),
132
+ );
133
+ };
134
+ const sdjwt = new SDJwtInstance({
135
+ signer,
136
+ signAlg: 'EdDSA',
137
+ verifier,
138
+ hasher: digest,
139
+ saltGenerator: generateSalt,
140
+ kbSigner: signer,
141
+ kbVerifier: failedverifier,
142
+ kbSignAlg: 'EdDSA',
143
+ });
144
+
145
+ const credential = await sdjwt.issue(
146
+ {
147
+ foo: 'bar',
148
+ },
149
+ {
150
+ _sd: ['foo'],
151
+ },
152
+ );
153
+
154
+ const presentation = await sdjwt.present(credential, ['foo'], {
155
+ kb: {
156
+ payload: {
157
+ sd_hash: '',
158
+ aud: '1',
159
+ iat: 1,
160
+ nonce: '342',
161
+ },
162
+ },
163
+ });
164
+
165
+ try {
166
+ await sdjwt.verify(presentation);
167
+ } catch (e) {
168
+ expect(e).toBeDefined();
169
+ }
170
+ });
171
+
172
+ test('verify with kbJwt', async () => {
173
+ const { signer, verifier } = createSignerVerifier();
174
+ const sdjwt = new SDJwtInstance({
175
+ signer,
176
+ signAlg: 'EdDSA',
177
+ verifier,
178
+ hasher: digest,
179
+ saltGenerator: generateSalt,
180
+ kbSigner: signer,
181
+ kbVerifier: verifier,
182
+ kbSignAlg: 'EdDSA',
183
+ });
184
+
185
+ const credential = await sdjwt.issue(
186
+ {
187
+ foo: 'bar',
188
+ },
189
+ {
190
+ _sd: ['foo'],
191
+ },
192
+ );
193
+
194
+ const presentation = await sdjwt.present(credential, ['foo'], {
195
+ kb: {
196
+ payload: {
197
+ sd_hash: 'sha-256',
198
+ aud: '1',
199
+ iat: 1,
200
+ nonce: '342',
201
+ },
202
+ },
203
+ });
204
+
205
+ const results = await sdjwt.verify(presentation, ['foo'], true);
206
+ expect(results).toBeDefined();
207
+ });
208
+
209
+ test('Hasher not found', async () => {
210
+ const sdjwt = new SDJwtInstance({});
211
+ try {
212
+ const credential = await sdjwt.issue(
213
+ {
214
+ foo: 'bar',
215
+ },
216
+ {
217
+ _sd: ['foo'],
218
+ },
219
+ );
220
+
221
+ expect(credential).toBeDefined();
222
+ } catch (e) {
223
+ expect(e).toBeDefined();
224
+ }
225
+ });
226
+
227
+ test('SaltGenerator not found', async () => {
228
+ const sdjwt = new SDJwtInstance({
229
+ hasher: digest,
230
+ });
231
+ try {
232
+ const credential = await sdjwt.issue(
233
+ {
234
+ foo: 'bar',
235
+ },
236
+ {
237
+ _sd: ['foo'],
238
+ },
239
+ );
240
+
241
+ expect(credential).toBeDefined();
242
+ } catch (e) {
243
+ expect(e).toBeDefined();
244
+ }
245
+ });
246
+
247
+ test('Signer not found', async () => {
248
+ const sdjwt = new SDJwtInstance({
249
+ hasher: digest,
250
+ saltGenerator: generateSalt,
251
+ });
252
+ try {
253
+ const credential = await sdjwt.issue(
254
+ {
255
+ foo: 'bar',
256
+ },
257
+ {
258
+ _sd: ['foo'],
259
+ },
260
+ );
261
+
262
+ expect(credential).toBeDefined();
263
+ } catch (e) {
264
+ expect(e).toBeDefined();
265
+ }
266
+ });
267
+
268
+ test('Verifier not found', async () => {
269
+ const { signer, verifier } = createSignerVerifier();
270
+ const sdjwt = new SDJwtInstance({
271
+ signer,
272
+ hasher: digest,
273
+ saltGenerator: generateSalt,
274
+ kbSigner: signer,
275
+ kbVerifier: verifier,
276
+ signAlg: 'EdDSA',
277
+ kbSignAlg: 'EdDSA',
278
+ });
279
+
280
+ const credential = await sdjwt.issue(
281
+ {
282
+ foo: 'bar',
283
+ },
284
+ {
285
+ _sd: ['foo'],
286
+ },
287
+ );
288
+
289
+ const presentation = await sdjwt.present(credential, ['foo'], {
290
+ kb: {
291
+ payload: {
292
+ sd_hash: 'sha-256',
293
+ aud: '1',
294
+ iat: 1,
295
+ nonce: '342',
296
+ },
297
+ },
298
+ });
299
+ try {
300
+ const results = await sdjwt.verify(presentation, ['foo'], true);
301
+ } catch (e) {
302
+ expect(e).toBeDefined();
303
+ }
304
+ });
305
+
306
+ test('kbSigner not found', async () => {
307
+ const { signer, verifier } = createSignerVerifier();
308
+ const sdjwt = new SDJwtInstance({
309
+ signer,
310
+ verifier,
311
+ hasher: digest,
312
+ saltGenerator: generateSalt,
313
+ kbVerifier: verifier,
314
+ signAlg: 'EdDSA',
315
+ kbSignAlg: 'EdDSA',
316
+ });
317
+
318
+ const credential = await sdjwt.issue(
319
+ {
320
+ foo: 'bar',
321
+ },
322
+ {
323
+ _sd: ['foo'],
324
+ },
325
+ );
326
+ try {
327
+ const presentation = await sdjwt.present(credential, ['foo'], {
328
+ kb: {
329
+ payload: {
330
+ sd_hash: 'sha-256',
331
+ aud: '1',
332
+ iat: 1,
333
+ nonce: '342',
334
+ },
335
+ },
336
+ });
337
+ } catch (e) {
338
+ expect(e).toBeDefined();
339
+ }
340
+ });
341
+
342
+ test('kbVerifier not found', async () => {
343
+ const { signer, verifier } = createSignerVerifier();
344
+ const sdjwt = new SDJwtInstance({
345
+ signer,
346
+ verifier,
347
+ hasher: digest,
348
+ saltGenerator: generateSalt,
349
+ kbSigner: signer,
350
+ signAlg: 'EdDSA',
351
+ kbSignAlg: 'EdDSA',
352
+ });
353
+
354
+ const credential = await sdjwt.issue(
355
+ {
356
+ foo: 'bar',
357
+ },
358
+ {
359
+ _sd: ['foo'],
360
+ },
361
+ );
362
+
363
+ const presentation = await sdjwt.present(credential, ['foo'], {
364
+ kb: {
365
+ payload: {
366
+ sd_hash: 'sha-256',
367
+ aud: '1',
368
+ iat: 1,
369
+ nonce: '342',
370
+ },
371
+ },
372
+ });
373
+ try {
374
+ const results = await sdjwt.verify(presentation, ['foo'], true);
375
+ } catch (e) {
376
+ expect(e).toBeDefined();
377
+ }
378
+ });
379
+ });