@sd-jwt/core 0.19.1-next.4 → 0.19.1-next.6

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.
@@ -0,0 +1,305 @@
1
+ import { hasher as digest } from '@owf/crypto';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { decodeSdJwt, decodeSdJwtSync } from '../../decode';
4
+ import {
5
+ present,
6
+ presentableKeys,
7
+ presentableKeysSync,
8
+ presentSync,
9
+ type SerializedDisclosure,
10
+ selectDisclosures,
11
+ transformPresentationFrame,
12
+ } from '../../present';
13
+ import type { PresentationFrame } from '../../types';
14
+
15
+ describe('Present tests', () => {
16
+ test('presentableKeys', async () => {
17
+ const sdjwt =
18
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
19
+ const decodedSdJwt = await decodeSdJwt(sdjwt, digest);
20
+ const keys = await presentableKeys(
21
+ decodedSdJwt.jwt.payload,
22
+ decodedSdJwt.disclosures,
23
+ digest,
24
+ );
25
+ expect(keys).toStrictEqual(['arr', 'arr.0', 'arr.2.a', 'foo', 'test.zzz']);
26
+ });
27
+
28
+ test('presentableKeys sync', () => {
29
+ const sdjwt =
30
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
31
+ const decodedSdJwt = decodeSdJwtSync(sdjwt, digest);
32
+ const keys = presentableKeysSync(
33
+ decodedSdJwt.jwt.payload,
34
+ decodedSdJwt.disclosures,
35
+ digest,
36
+ );
37
+ expect(keys).toStrictEqual(['arr', 'arr.0', 'arr.2.a', 'foo', 'test.zzz']);
38
+ });
39
+
40
+ test('present', async () => {
41
+ const sdjwt =
42
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
43
+ const presentedSdJwt = await present(
44
+ sdjwt,
45
+ {
46
+ foo: true,
47
+ arr: {
48
+ 0: true,
49
+ },
50
+ test: {
51
+ zzz: true,
52
+ },
53
+ },
54
+ digest,
55
+ );
56
+ expect(presentedSdJwt).toStrictEqual(
57
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~',
58
+ );
59
+ });
60
+
61
+ test('present sync', () => {
62
+ const sdjwt =
63
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIwNGU0MjAzOWU4ZWFiOWRjIiwiYSIsIjEiXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~';
64
+ const presentedSdJwt = presentSync(
65
+ sdjwt,
66
+ {
67
+ foo: true,
68
+ arr: {
69
+ 0: true,
70
+ },
71
+ test: {
72
+ zzz: true,
73
+ },
74
+ },
75
+ digest,
76
+ );
77
+ expect(presentedSdJwt).toStrictEqual(
78
+ 'eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJ0ZXN0Ijp7Il9zZCI6WyJqVEszMHNleDZhYV9kUk1KSWZDR056Q0FwbVB5MzRRNjNBa3QzS3hhSktzIl19LCJfc2QiOlsiME9nMi1ReG95eW1UOGNnVzZZUjVSSFpQLUJuR2tHUi1NM2otLV92RWlzSSIsIkcwZ3lHNnExVFMyUlQxMkZ3X2RRRDVVcjlZc1AwZlVWOXVtQWdGMC1jQ1EiXSwiX3NkX2FsZyI6InNoYS0yNTYifQ.ggEyE4SeDO2Hu3tol3VLmi7NQj56yKzKQDaafocgkLrUBdivghohtzrfcbrMN7CRufJ_Cnh0EL54kymXLGTdDQ~WyIxYzdmOTE4ZTE0MjA2NzZiIiwiZm9vIiwiYmFyIl0~WyJmZjYxYzQ5ZGU2NjFiYzMxIiwiYXJyIixbeyIuLi4iOiJTSG96VW5KNUpkd0ZtTjVCbXB5dXZCWGZfZWRjckVvcExPYThTVlBFUmg0In0sIjIiLHsiX3NkIjpbIkpuODNhZkp0OGx4NG1FMzZpRkZyS2U2R2VnN0dlVUQ4Z3UwdVo3NnRZcW8iXX1dXQ~WyIwOGE1Yjc5MjMyYjAzYzBhIiwiMSJd~WyJiNWE2YjUzZGQwYTFmMGIwIiwienp6IiwieHh4Il0~',
79
+ );
80
+ });
81
+
82
+ test('transform an object for a presentation to a list', () => {
83
+ const claims = {
84
+ firstname: 'John',
85
+ lastname: 'Doe',
86
+ ssn: '123-45-6789',
87
+ id: '1234',
88
+ data: {
89
+ firstname: 'John',
90
+ lastname: 'Doe',
91
+ ssn: '123-45-6789',
92
+ list: [{ r: 'd' }, 'b', 'c'],
93
+ list2: ['1', '2', '3'],
94
+ list3: ['1', null, 2],
95
+ },
96
+ data2: {
97
+ hi: 'bye',
98
+ },
99
+ };
100
+
101
+ const presentFrame: PresentationFrame<typeof claims> = {
102
+ firstname: true,
103
+ data: {
104
+ firstname: true,
105
+ list: {
106
+ 1: true,
107
+ 0: {
108
+ r: true,
109
+ },
110
+ },
111
+ list2: {
112
+ 1: true,
113
+ },
114
+ list3: true,
115
+ },
116
+ data2: true,
117
+ };
118
+
119
+ const list = transformPresentationFrame(presentFrame);
120
+ expect(list).toStrictEqual([
121
+ 'firstname',
122
+ 'data',
123
+ 'data.firstname',
124
+ 'data.list',
125
+ 'data.list.0',
126
+ 'data.list.0.r',
127
+ 'data.list.1',
128
+ 'data.list2',
129
+ 'data.list2.1',
130
+ 'data.list3',
131
+ 'data2',
132
+ ]);
133
+ });
134
+
135
+ test('transform an object for a presentation to a list, but with faulty inputs', () => {
136
+ const claims = {
137
+ name: 'John',
138
+ address: {
139
+ city: 'New York',
140
+ street: '5th Avenue',
141
+ },
142
+ };
143
+
144
+ const obj: PresentationFrame<typeof claims> = {
145
+ name: false,
146
+ address: {
147
+ city: true,
148
+ street: true,
149
+ },
150
+ };
151
+ const list = transformPresentationFrame(obj);
152
+ expect(list).toStrictEqual(['address', 'address.city', 'address.street']);
153
+ });
154
+
155
+ test('select disclosures', () => {
156
+ const payload = {
157
+ lastname: 'Doe',
158
+ _sd: [
159
+ 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
160
+ 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
161
+ 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
162
+ ],
163
+ _sd_alg: 'sha-256',
164
+ };
165
+
166
+ const presentationFrame = {
167
+ firstname: true,
168
+ //ssn: true,
169
+ id: true,
170
+ };
171
+
172
+ const disclosures: SerializedDisclosure[] = [
173
+ {
174
+ digest: 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
175
+ encoded: 'WyJiMDQ3NjBiOTgxMDgyM2ZhIiwiZmlyc3RuYW1lIiwiSm9obiJd',
176
+ salt: 'b04760b9810823fa',
177
+ key: 'firstname',
178
+ value: 'John',
179
+ },
180
+ {
181
+ digest: 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
182
+ encoded: 'WyJjNTQwZWE4YjJhOTNmZDE1Iiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ',
183
+ salt: 'c540ea8b2a93fd15',
184
+ key: 'ssn',
185
+ value: '123-45-6789',
186
+ },
187
+ {
188
+ digest: 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
189
+ encoded: 'WyI5N2YwNTVkZTk0NGFmNzI5IiwiaWQiLCIxMjM0Il0',
190
+ salt: '97f055de944af729',
191
+ key: 'id',
192
+ value: '1234',
193
+ },
194
+ ];
195
+
196
+ const selected = selectDisclosures(payload, disclosures, presentationFrame);
197
+ expect(selected).toStrictEqual([
198
+ {
199
+ digest: 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
200
+ encoded: 'WyJiMDQ3NjBiOTgxMDgyM2ZhIiwiZmlyc3RuYW1lIiwiSm9obiJd',
201
+ salt: 'b04760b9810823fa',
202
+ key: 'firstname',
203
+ value: 'John',
204
+ },
205
+ {
206
+ digest: 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
207
+ encoded: 'WyI5N2YwNTVkZTk0NGFmNzI5IiwiaWQiLCIxMjM0Il0',
208
+ salt: '97f055de944af729',
209
+ key: 'id',
210
+ value: '1234',
211
+ },
212
+ ]);
213
+ });
214
+
215
+ test('select disclosures no input', () => {
216
+ const selected = selectDisclosures({}, [], {});
217
+ expect(selected).toStrictEqual([]);
218
+ });
219
+
220
+ test('select disclosures noting return', () => {
221
+ const payload = {
222
+ lastname: 'Doe',
223
+ _sd: [
224
+ 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
225
+ 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
226
+ 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
227
+ ],
228
+ _sd_alg: 'sha-256',
229
+ };
230
+
231
+ const presentationFrame = {};
232
+
233
+ const disclosures: SerializedDisclosure[] = [
234
+ {
235
+ digest: 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
236
+ encoded: 'WyJiMDQ3NjBiOTgxMDgyM2ZhIiwiZmlyc3RuYW1lIiwiSm9obiJd',
237
+ salt: 'b04760b9810823fa',
238
+ key: 'firstname',
239
+ value: 'John',
240
+ },
241
+ {
242
+ digest: 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
243
+ encoded: 'WyJjNTQwZWE4YjJhOTNmZDE1Iiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ',
244
+ salt: 'c540ea8b2a93fd15',
245
+ key: 'ssn',
246
+ value: '123-45-6789',
247
+ },
248
+ {
249
+ digest: 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
250
+ encoded: 'WyI5N2YwNTVkZTk0NGFmNzI5IiwiaWQiLCIxMjM0Il0',
251
+ salt: '97f055de944af729',
252
+ key: 'id',
253
+ value: '1234',
254
+ },
255
+ ];
256
+ const selected = selectDisclosures(payload, disclosures, presentationFrame);
257
+ expect(selected).toStrictEqual([]);
258
+ });
259
+
260
+ test('expect missing digest', () => {
261
+ const payload = {
262
+ lastname: 'Doe',
263
+ _sd: [
264
+ 'COnqXH7wGBFGR1ao12sDwTfu84Zs7cq92CZIg8ulIuU',
265
+ 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
266
+ 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
267
+ ],
268
+ _sd_alg: 'sha-256',
269
+ };
270
+
271
+ const presentationFrame = {
272
+ firstname: true,
273
+ //ssn: true,
274
+ id: true,
275
+ };
276
+
277
+ const disclosures: SerializedDisclosure[] = [
278
+ //@ts-expect-error
279
+ {
280
+ encoded: 'WyJiMDQ3NjBiOTgxMDgyM2ZhIiwiZmlyc3RuYW1lIiwiSm9obiJd',
281
+ salt: 'b04760b9810823fa',
282
+ key: 'firstname',
283
+ value: 'John',
284
+ },
285
+ {
286
+ digest: 'RrOc4ZfBVyD6iNlMbtmdokZOti322mOXfvIOBKvpuc4',
287
+ encoded: 'WyJjNTQwZWE4YjJhOTNmZDE1Iiwic3NuIiwiMTIzLTQ1LTY3ODkiXQ',
288
+ salt: 'c540ea8b2a93fd15',
289
+ key: 'ssn',
290
+ value: '123-45-6789',
291
+ },
292
+ {
293
+ digest: 'aXqInKwHoE1l8OM1VNUQDqTPeNUG1cMJVwVbxZJpP14',
294
+ encoded: 'WyI5N2YwNTVkZTk0NGFmNzI5IiwiaWQiLCIxMjM0Il0',
295
+ salt: '97f055de944af729',
296
+ key: 'id',
297
+ value: '1234',
298
+ },
299
+ ];
300
+
301
+ expect(() =>
302
+ selectDisclosures(payload, disclosures, presentationFrame),
303
+ ).toThrowError('Unreferenced disclosure(s) detected in SD-JWT');
304
+ });
305
+ });
@@ -1,11 +1,11 @@
1
1
  import Crypto from 'node:crypto';
2
- import { generateSalt, digest as hasher } from '@sd-jwt/crypto-nodejs';
3
- import { createHashMapping, unpack } from '@sd-jwt/decode';
4
- import type { DisclosureFrame, Signer } from '@sd-jwt/types';
5
- import { Disclosure } from '@sd-jwt/utils';
2
+ import { generateSalt, hasher } from '@owf/crypto';
6
3
  import { describe, expect, test } from 'vitest';
4
+ import { createHashMapping, unpack } from '../decode';
7
5
  import { Jwt } from '../jwt';
8
6
  import { listKeys, pack, SDJwt } from '../sdjwt';
7
+ import type { DisclosureFrame, Signer } from '../types';
8
+ import { Disclosure } from '../utils';
9
9
 
10
10
  const hash = { alg: 'SHA256', hasher };
11
11
 
@@ -0,0 +1,88 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import {
3
+ type DisclosureFrame,
4
+ KB_JWT_TYP,
5
+ type PresentationFrame,
6
+ SD_DECOY,
7
+ SD_DIGEST,
8
+ SD_LIST_KEY,
9
+ SD_SEPARATOR,
10
+ } from '../../types';
11
+
12
+ const claims = {
13
+ firstname: 'John',
14
+ lastname: 'Doe',
15
+ ssn: '123-45-6789',
16
+ id: '1234',
17
+ data: {
18
+ firstname: 'John',
19
+ lastname: 'Doe',
20
+ ssn: '123-45-6789',
21
+ list: [{ r: 'd' }, 'b', 'c'],
22
+ list2: ['1', '2', '3'],
23
+ list3: ['1', null, 2],
24
+ },
25
+ data2: {
26
+ hi: 'bye',
27
+ },
28
+ };
29
+
30
+ describe('Variable tests', () => {
31
+ test('SD_SEPARATOR', () => {
32
+ expect(SD_SEPARATOR).toBe('~');
33
+ });
34
+
35
+ test('SD_LIST_KEY', () => {
36
+ expect(SD_LIST_KEY).toBe('...');
37
+ });
38
+
39
+ test('SD_DIGEST', () => {
40
+ expect(SD_DIGEST).toBe('_sd');
41
+ });
42
+
43
+ test('SD_DECOY', () => {
44
+ expect(SD_DECOY).toBe('_sd_decoy');
45
+ });
46
+
47
+ test('KB_JWT_TYP', () => {
48
+ expect(KB_JWT_TYP).toBe('kb+jwt');
49
+ });
50
+
51
+ test('DisclosureFrameType test', () => {
52
+ const disclosureFrame: DisclosureFrame<typeof claims> = {
53
+ _sd: ['data', 'firstname', 'data2'],
54
+ data: {
55
+ _sd: ['list', 'ssn'],
56
+ _sd_decoy: 2,
57
+ list: {
58
+ _sd: [0, 2],
59
+ 0: {
60
+ _sd: ['r'],
61
+ },
62
+ },
63
+ },
64
+ };
65
+ expect(disclosureFrame).toBeDefined();
66
+ });
67
+
68
+ test('PresentationFrameType test', () => {
69
+ const presentationFrame: PresentationFrame<typeof claims> = {
70
+ firstname: true,
71
+ data: {
72
+ firstname: true,
73
+ list: {
74
+ 1: true,
75
+ 0: {
76
+ r: true,
77
+ },
78
+ },
79
+ list2: {
80
+ 1: true,
81
+ },
82
+ list3: true,
83
+ },
84
+ data2: true,
85
+ };
86
+ expect(presentationFrame).toBeDefined();
87
+ });
88
+ });
@@ -0,0 +1,33 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import {
3
+ base64UrlToUint8Array,
4
+ base64urlDecode,
5
+ base64urlEncode,
6
+ uint8ArrayToBase64Url,
7
+ } from '../../utils/base64url';
8
+
9
+ describe('Base64url', () => {
10
+ const raw = 'abcdefghijklmnopqrstuvwxyz';
11
+ const encoded = 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo';
12
+ test('Encode', () => {
13
+ expect(base64urlEncode(raw)).toStrictEqual(encoded);
14
+ });
15
+ test('Decode', () => {
16
+ expect(base64urlDecode(encoded)).toStrictEqual(raw);
17
+ });
18
+ test('Encode and decode', () => {
19
+ const str = 'hello world';
20
+ expect(base64urlDecode(base64urlEncode(str))).toStrictEqual(str);
21
+ });
22
+ test('Uint8Array', () => {
23
+ const str = 'hello world';
24
+ const uint8 = new TextEncoder().encode(str);
25
+ expect(uint8ArrayToBase64Url(uint8)).toStrictEqual(base64urlEncode(str));
26
+ });
27
+
28
+ test('Base64Url to Uint8Array', () => {
29
+ const str = 'hello world';
30
+ const uint8 = new TextEncoder().encode(str);
31
+ expect(base64UrlToUint8Array(base64urlEncode(str))).toStrictEqual(uint8);
32
+ });
33
+ });
@@ -0,0 +1,170 @@
1
+ import { generateSalt, hasher } from '@owf/crypto';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { base64urlEncode, type SDJWTException } from '../../utils';
4
+ import { Disclosure } from '../../utils/disclosure';
5
+
6
+ const hash = { alg: 'SHA256', hasher };
7
+
8
+ /*
9
+ ref draft-ietf-oauth-selective-disclosure-jwt-07
10
+ Claim given_name:
11
+ SHA-256 Hash: jsu9yVulwQQlhFlM_3JlzMaSFzglhQG0DpfayQwLUK4
12
+ Disclosure: WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd
13
+ Contents: ["2GLC42sKQveCfGfryNRN9w", "given_name", "John"]
14
+ For example, the SHA-256 digest of the Disclosure
15
+ WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNw7ZiaXVzIl0 would be uutlBuYeMDyjLLTpf6Jxi7yNkEF35jdyWMn9U7b_RYY.
16
+ The SHA-256 digest of the Disclosure
17
+ WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIkZSIl0 would be w0I8EKcdCtUPkGCNUrfwVp2xEgNjtoIDlOxc9-PlOhs.
18
+ */
19
+ const TestDataDraft7 = {
20
+ claimTests: [
21
+ {
22
+ contents: '["2GLC42sKQveCfGfryNRN9w", "given_name", "John"]',
23
+ digest: 'jsu9yVulwQQlhFlM_3JlzMaSFzglhQG0DpfayQwLUK4',
24
+ disclosure:
25
+ 'WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd',
26
+ },
27
+ ],
28
+ sha256Tests: [
29
+ {
30
+ digest: 'uutlBuYeMDyjLLTpf6Jxi7yNkEF35jdyWMn9U7b_RYY',
31
+ disclosure: 'WyI2cU1RdlJMNWhhaiIsICJmYW1pbHlfbmFtZSIsICJNw7ZiaXVzIl0',
32
+ },
33
+ {
34
+ digest: 'w0I8EKcdCtUPkGCNUrfwVp2xEgNjtoIDlOxc9-PlOhs',
35
+ disclosure: 'WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIkZSIl0',
36
+ },
37
+ ],
38
+ };
39
+
40
+ describe('Disclosure', () => {
41
+ test('create object disclosure', async () => {
42
+ const salt = generateSalt(16);
43
+ const disclosure = new Disclosure([salt, 'name', 'James']);
44
+ expect(disclosure).toBeDefined();
45
+ expect(disclosure.key).toBe('name');
46
+ expect(disclosure.value).toBe('James');
47
+ expect(disclosure.salt).toBe(salt);
48
+ });
49
+
50
+ test('create array disclosure', async () => {
51
+ const salt = generateSalt(16);
52
+ const disclosure = new Disclosure([salt, 'US']);
53
+ expect(disclosure).toBeDefined();
54
+ expect(disclosure.key).toBe(undefined);
55
+ expect(disclosure.value).toBe('US');
56
+ expect(disclosure.salt).toBe(salt);
57
+ });
58
+
59
+ test('create disclosure error', async () => {
60
+ const salt = generateSalt(16);
61
+ const data: [string, string, string] = [salt, 'name', 'James'];
62
+ data.push('any');
63
+ expect(() => new Disclosure(data)).toThrow();
64
+ try {
65
+ new Disclosure(data);
66
+ } catch (e: unknown) {
67
+ const error = e as SDJWTException;
68
+ expect(typeof error.getFullMessage()).toBe('string');
69
+ }
70
+ });
71
+
72
+ test('encode disclosure', async () => {
73
+ const salt = generateSalt(16);
74
+ const disclosure = new Disclosure([salt, 'name', 'James']);
75
+ const encodedDisclosure = disclosure.encode();
76
+ expect(encodedDisclosure).toBeDefined();
77
+ expect(typeof encodedDisclosure).toBe('string');
78
+ });
79
+
80
+ test('decode disclosure', async () => {
81
+ const salt = generateSalt(16);
82
+ const disclosure = new Disclosure([salt, 'name', 'James']);
83
+ const encodedDisclosure = disclosure.encode();
84
+ const newDisclosure = await Disclosure.fromEncode(encodedDisclosure, {
85
+ alg: 'SHA256',
86
+ hasher,
87
+ });
88
+
89
+ expect(newDisclosure).toBeDefined();
90
+ expect(newDisclosure.key).toBe('name');
91
+ expect(newDisclosure.value).toBe('James');
92
+ expect(newDisclosure.salt).toBe(salt);
93
+ });
94
+
95
+ test('decode disclosure sync', () => {
96
+ const salt = generateSalt(16);
97
+ const disclosure = new Disclosure([salt, 'name', 'James']);
98
+ const encodedDisclosure = disclosure.encode();
99
+ const newDisclosure = Disclosure.fromEncodeSync(encodedDisclosure, {
100
+ alg: 'SHA256',
101
+ hasher,
102
+ });
103
+
104
+ expect(newDisclosure).toBeDefined();
105
+ expect(newDisclosure.key).toBe('name');
106
+ expect(newDisclosure.value).toBe('James');
107
+ expect(newDisclosure.salt).toBe(salt);
108
+ });
109
+
110
+ test('digest disclosure #1', async () => {
111
+ const salt = generateSalt(16);
112
+ const disclosure = new Disclosure([salt, 'name', 'James']);
113
+ const digest = await disclosure.digest({ alg: 'SHA256', hasher });
114
+ expect(digest).toBeDefined();
115
+ expect(typeof digest).toBe('string');
116
+ });
117
+
118
+ test('digest disclosure #2', async () => {
119
+ const disclosure = new Disclosure([
120
+ '2GLC42sKQveCfGfryNRN9w',
121
+ 'given_name',
122
+ 'John',
123
+ ]);
124
+ const encode = disclosure.encode();
125
+ expect(encode).toBe(
126
+ 'WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ',
127
+ );
128
+ const digest = await disclosure.digest(hash);
129
+ expect(digest).toBe('8VHiz7qTXavxvpiTYDCSr_shkUO6qRcVXjkhEnt1os4');
130
+ });
131
+
132
+ test('digest disclosure #2 sync', () => {
133
+ const disclosure = new Disclosure([
134
+ '2GLC42sKQveCfGfryNRN9w',
135
+ 'given_name',
136
+ 'John',
137
+ ]);
138
+ const encode = disclosure.encode();
139
+ expect(encode).toBe(
140
+ 'WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ',
141
+ );
142
+ const digest = disclosure.digestSync(hash);
143
+ expect(digest).toBe('8VHiz7qTXavxvpiTYDCSr_shkUO6qRcVXjkhEnt1os4');
144
+ });
145
+
146
+ test('digest disclosure #3', async () => {
147
+ const encoded = base64urlEncode(TestDataDraft7.claimTests[0].contents);
148
+ expect(encoded).toStrictEqual(TestDataDraft7.claimTests[0].disclosure);
149
+
150
+ const disclosure = await Disclosure.fromEncode(
151
+ TestDataDraft7.claimTests[0].disclosure,
152
+ hash,
153
+ );
154
+
155
+ const digest = await disclosure.digest(hash);
156
+ expect(digest).toBe(TestDataDraft7.claimTests[0].digest);
157
+ });
158
+
159
+ test('digest disclosure #4', async () => {
160
+ for (const sha256Test of TestDataDraft7.sha256Tests) {
161
+ const disclosure = await Disclosure.fromEncode(
162
+ sha256Test.disclosure,
163
+ hash,
164
+ );
165
+
166
+ const digest = await disclosure.digest(hash);
167
+ expect(digest).toBe(sha256Test.digest);
168
+ }
169
+ });
170
+ });
@@ -0,0 +1,15 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { SDJWTException } from '../../utils/error';
3
+
4
+ describe('Error tests', () => {
5
+ test('Detail', () => {
6
+ try {
7
+ throw new SDJWTException('msg', { info: 'details' });
8
+ } catch (e: unknown) {
9
+ const exception = e as SDJWTException;
10
+ expect(exception.getFullMessage()).toEqual(
11
+ 'SDJWTException: msg - {"info":"details"}',
12
+ );
13
+ }
14
+ });
15
+ });
@@ -0,0 +1,2 @@
1
+ export * from './type';
2
+ export * from './verification-error';