@sd-jwt/sd-jwt-vc 0.3.2-next.94

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,266 @@
1
+ import Crypto from 'node:crypto';
2
+ import { SDJwtVcInstance } from '../src';
3
+ import { DisclosureFrame, Signer, Verifier } from '@sd-jwt/types';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ import { describe, expect, test } from 'vitest';
7
+ import { digest, generateSalt } from '@sd-jwt/crypto-nodejs';
8
+
9
+ export const createSignerVerifier = () => {
10
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
11
+ const signer: Signer = async (data: string) => {
12
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
13
+ return Buffer.from(sig).toString('base64url');
14
+ };
15
+ const verifier: Verifier = async (data: string, sig: string) => {
16
+ return Crypto.verify(
17
+ null,
18
+ Buffer.from(data),
19
+ publicKey,
20
+ Buffer.from(sig, 'base64url'),
21
+ );
22
+ };
23
+ return { signer, verifier };
24
+ };
25
+
26
+ const iss = 'ExampleIssuer';
27
+ const vct = 'https://example.com/schema/1';
28
+ const iat = new Date().getTime() / 1000;
29
+
30
+ describe('App', () => {
31
+ test('Example', async () => {
32
+ const { signer, verifier } = createSignerVerifier();
33
+ const sdjwt = new SDJwtVcInstance({
34
+ signer,
35
+ signAlg: 'EdDSA',
36
+ verifier,
37
+ hasher: digest,
38
+ hashAlg: 'SHA-256',
39
+ saltGenerator: generateSalt,
40
+ });
41
+
42
+ const claims = {
43
+ firstname: 'John',
44
+ lastname: 'Doe',
45
+ ssn: '123-45-6789',
46
+ id: '1234',
47
+ data: {
48
+ firstname: 'John',
49
+ lastname: 'Doe',
50
+ ssn: '123-45-6789',
51
+ list: [{ r: '1' }, 'b', 'c'],
52
+ },
53
+ data2: {
54
+ hi: 'bye',
55
+ },
56
+ };
57
+ const disclosureFrame: DisclosureFrame<typeof claims> = {
58
+ _sd: ['firstname', 'id', 'data2'],
59
+ data: {
60
+ _sd: ['list'],
61
+ _sd_decoy: 2,
62
+ list: {
63
+ _sd: [0, 2],
64
+ _sd_decoy: 1,
65
+ 0: {
66
+ _sd: ['r'],
67
+ },
68
+ },
69
+ },
70
+ data2: {
71
+ _sd: ['hi'],
72
+ },
73
+ };
74
+
75
+ const expectedPayload = { iat, iss, vct, ...claims };
76
+ const encodedSdjwt = await sdjwt.issue(expectedPayload, disclosureFrame);
77
+ expect(encodedSdjwt).toBeDefined();
78
+ const validated = await sdjwt.validate(encodedSdjwt);
79
+ expect(validated).toBeDefined();
80
+
81
+ const decoded = await sdjwt.decode(encodedSdjwt);
82
+ const keys = await decoded.keys(digest);
83
+ expect(keys).toEqual([
84
+ 'data',
85
+ 'data.firstname',
86
+ 'data.lastname',
87
+ 'data.list',
88
+ 'data.list.0',
89
+ 'data.list.0.r',
90
+ 'data.list.1',
91
+ 'data.list.2',
92
+ 'data.ssn',
93
+ 'data2',
94
+ 'data2.hi',
95
+ 'firstname',
96
+ 'iat',
97
+ 'id',
98
+ 'iss',
99
+ 'lastname',
100
+ 'ssn',
101
+ 'vct',
102
+ ]);
103
+ const payloads = await decoded.getClaims(digest);
104
+ expect(payloads).toEqual(expectedPayload);
105
+ const presentableKeys = await decoded.presentableKeys(digest);
106
+ expect(presentableKeys).toEqual([
107
+ 'data.list',
108
+ 'data.list.0',
109
+ 'data.list.0.r',
110
+ 'data.list.2',
111
+ 'data2',
112
+ 'data2.hi',
113
+ 'firstname',
114
+ 'id',
115
+ ]);
116
+
117
+ const presentationFrame = ['firstname', 'id'];
118
+ const presentedSDJwt = await sdjwt.present(encodedSdjwt, presentationFrame);
119
+ expect(presentedSDJwt).toBeDefined();
120
+
121
+ const presentationClaims = await sdjwt.getClaims(presentedSDJwt);
122
+ expect(presentationClaims).toBeDefined();
123
+ expect(presentationClaims).toEqual({
124
+ lastname: 'Doe',
125
+ ssn: '123-45-6789',
126
+ data: { firstname: 'John', lastname: 'Doe', ssn: '123-45-6789' },
127
+ id: '1234',
128
+ firstname: 'John',
129
+ iat,
130
+ iss,
131
+ vct,
132
+ });
133
+
134
+ const requiredClaimKeys = ['firstname', 'id', 'data.ssn'];
135
+ const verified = await sdjwt.verify(encodedSdjwt, requiredClaimKeys);
136
+ expect(verified).toBeDefined();
137
+ });
138
+
139
+ test('From JSON (complex)', async () => {
140
+ await JSONtest('./complex.json');
141
+ });
142
+
143
+ test('From JSON (array_data_types)', async () => {
144
+ await JSONtest('./array_data_types.json');
145
+ });
146
+
147
+ test('From JSON (array_full_sd)', async () => {
148
+ await JSONtest('./array_full_sd.json');
149
+ });
150
+
151
+ test('From JSON (array_in_sd)', async () => {
152
+ await JSONtest('./array_in_sd.json');
153
+ });
154
+
155
+ test('From JSON (array_recursive_sd_some_disclosed)', async () => {
156
+ await JSONtest('./array_recursive_sd_some_disclosed.json');
157
+ });
158
+
159
+ test('From JSON (header_mod)', async () => {
160
+ await JSONtest('./header_mod.json');
161
+ });
162
+
163
+ test('From JSON (json_serialization)', async () => {
164
+ await JSONtest('./json_serialization.json');
165
+ });
166
+
167
+ test('From JSON (key_binding)', async () => {
168
+ await JSONtest('./key_binding.json');
169
+ });
170
+
171
+ test('From JSON (no_sd)', async () => {
172
+ await JSONtest('./no_sd.json');
173
+ });
174
+
175
+ test('From JSON (object_data_types)', async () => {
176
+ await JSONtest('./object_data_types.json');
177
+ });
178
+
179
+ test('From JSON (recursions)', async () => {
180
+ await JSONtest('./recursions.json');
181
+ });
182
+
183
+ test('From JSON (array_recursive_sd)', async () => {
184
+ await JSONtest('./array_recursive_sd.json');
185
+ });
186
+
187
+ test('From JSON (array_of_scalars)', async () => {
188
+ await JSONtest('./array_of_scalars.json');
189
+ });
190
+
191
+ test('From JSON (array_of_objects)', async () => {
192
+ await JSONtest('./array_of_objects.json');
193
+ });
194
+
195
+ test('From JSON (array_of_nulls)', async () => {
196
+ await JSONtest('./array_of_nulls.json');
197
+ });
198
+
199
+ test('From JSON (array_nested_in_plain)', async () => {
200
+ await JSONtest('./array_nested_in_plain.json');
201
+ });
202
+ });
203
+
204
+ async function JSONtest(filename: string) {
205
+ const test = loadTestJsonFile(filename);
206
+ const { signer, verifier } = createSignerVerifier();
207
+ const sdjwt = new SDJwtVcInstance({
208
+ signer,
209
+ signAlg: 'EdDSA',
210
+ verifier,
211
+ hasher: digest,
212
+ hashAlg: 'SHA-256',
213
+ saltGenerator: generateSalt,
214
+ });
215
+
216
+ const payload = { iss, vct, iat, ...test.claims };
217
+ const encodedSdjwt = await sdjwt.issue(payload, test.disclosureFrame);
218
+
219
+ expect(encodedSdjwt).toBeDefined();
220
+
221
+ const validated = await sdjwt.validate(encodedSdjwt);
222
+
223
+ expect(validated).toBeDefined();
224
+ expect(validated).toStrictEqual({
225
+ header: { alg: 'EdDSA', typ: 'sd-jwt-vc' },
226
+ payload,
227
+ });
228
+
229
+ const presentedSDJwt = await sdjwt.present(
230
+ encodedSdjwt,
231
+ test.presentationKeys,
232
+ );
233
+
234
+ expect(presentedSDJwt).toBeDefined();
235
+
236
+ const presentationClaims = await sdjwt.getClaims(presentedSDJwt);
237
+
238
+ expect(presentationClaims).toEqual({
239
+ ...test.presenatedClaims,
240
+ iat,
241
+ iss,
242
+ vct,
243
+ });
244
+
245
+ const verified = await sdjwt.verify(encodedSdjwt, test.requiredClaimKeys);
246
+
247
+ expect(verified).toBeDefined();
248
+ expect(verified).toStrictEqual({
249
+ header: { alg: 'EdDSA', typ: 'sd-jwt-vc' },
250
+ payload,
251
+ });
252
+ }
253
+
254
+ type TestJson = {
255
+ claims: object;
256
+ disclosureFrame: DisclosureFrame<object>;
257
+ presentationKeys: string[];
258
+ presenatedClaims: object;
259
+ requiredClaimKeys: string[];
260
+ };
261
+
262
+ function loadTestJsonFile(filename: string) {
263
+ const filepath = path.join(__dirname, filename);
264
+ const fileContents = fs.readFileSync(filepath, 'utf8');
265
+ return JSON.parse(fileContents) as TestJson;
266
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "claims": {
3
+ "data_types": [null, 42, 3.14, "foo", ["Test"], { "foo": "bar" }]
4
+ },
5
+ "disclosureFrame": {
6
+ "data_types": {
7
+ "_sd": [0, 1, 2, 3, 4, 5]
8
+ }
9
+ },
10
+ "presentationKeys": [
11
+ "data_types.0",
12
+ "data_types.1",
13
+ "data_types.2",
14
+ "data_types.3",
15
+ "data_types.4",
16
+ "data_types.5"
17
+ ],
18
+ "presenatedClaims": {
19
+ "data_types": [null, 42, 3.14, "foo", ["Test"], { "foo": "bar" }]
20
+ },
21
+ "requiredClaimKeys": [
22
+ "data_types.0",
23
+ "data_types.1",
24
+ "data_types.2",
25
+ "data_types.3",
26
+ "data_types.4",
27
+ "data_types.5"
28
+ ]
29
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "claims": {
3
+ "is_over": {
4
+ "13": true,
5
+ "18": false,
6
+ "21": false
7
+ }
8
+ },
9
+ "disclosureFrame": {
10
+ "is_over": {
11
+ "_sd": ["13", "18", "21"]
12
+ }
13
+ },
14
+ "presentationKeys": ["is_over.18"],
15
+ "presenatedClaims": {
16
+ "is_over": {
17
+ "18": false
18
+ }
19
+ },
20
+ "requiredClaimKeys": ["is_over.18"]
21
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "claims": {
3
+ "sd_array": ["32", "23"]
4
+ },
5
+ "disclosureFrame": {
6
+ "_sd": ["sd_array"]
7
+ },
8
+ "presentationKeys": ["sd_array"],
9
+ "presenatedClaims": {
10
+ "sd_array": ["32", "23"]
11
+ },
12
+ "requiredClaimKeys": ["sd_array"]
13
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "claims": {
3
+ "nested_array": [["foo", "bar"], ["baz", "qux"]]
4
+ },
5
+ "disclosureFrame": {
6
+ "nested_array": {
7
+ "0": {
8
+ "_sd": [0, 1]
9
+ },
10
+ "1": {
11
+ "_sd": [0, 1]
12
+ }
13
+ }
14
+ },
15
+ "presentationKeys": ["nested_array.0.0", "nested_array.1.1"],
16
+ "presenatedClaims": {
17
+ "nested_array": [["foo"], ["qux"]]
18
+ },
19
+ "requiredClaimKeys": ["nested_array.0.0", "nested_array.1.0"]
20
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "claims": {
3
+ "is_over": {
4
+ "13": false,
5
+ "18": true,
6
+ "21": false
7
+ }
8
+ },
9
+ "disclosureFrame": {
10
+ "is_over": {
11
+ "_sd": ["13", "18", "21"]
12
+ }
13
+ },
14
+ "presentationKeys": [],
15
+ "presenatedClaims": {},
16
+ "requiredClaimKeys": []
17
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "claims": {
3
+ "null_values": [null, null, null, null]
4
+ },
5
+ "disclosureFrame": {
6
+ "null_values": {
7
+ "_sd": [1, 2]
8
+ }
9
+ },
10
+ "presentationKeys": [],
11
+ "presenatedClaims": {
12
+ "null_values": [null, null]
13
+ },
14
+ "requiredClaimKeys": ["null_values.0", "null_values.1"]
15
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "claims": {
3
+ "addresses": [
4
+ {
5
+ "street": "123 Main St",
6
+ "city": "Anytown",
7
+ "state": "NY",
8
+ "zip": "12345",
9
+ "type": "main_address"
10
+ },
11
+ {
12
+ "street": "456 Main St",
13
+ "city": "Anytown",
14
+ "state": "NY",
15
+ "zip": "12345",
16
+ "type": "secondary_address"
17
+ }
18
+ ],
19
+ "array_with_one_sd_object": {
20
+ "foo": "bar"
21
+ }
22
+ },
23
+ "disclosureFrame": {
24
+ "addresses": {
25
+ "_sd": [1]
26
+ },
27
+ "array_with_one_sd_object": {
28
+ "_sd": ["foo"]
29
+ }
30
+ },
31
+ "presentationKeys": ["addresses.1", "array_with_one_sd_object.foo"],
32
+ "presenatedClaims": {
33
+ "addresses": [
34
+ {
35
+ "street": "123 Main St",
36
+ "city": "Anytown",
37
+ "state": "NY",
38
+ "zip": "12345",
39
+ "type": "main_address"
40
+ },
41
+ {
42
+ "street": "456 Main St",
43
+ "city": "Anytown",
44
+ "state": "NY",
45
+ "zip": "12345",
46
+ "type": "secondary_address"
47
+ }
48
+ ],
49
+ "array_with_one_sd_object": {
50
+ "foo": "bar"
51
+ }
52
+ },
53
+ "requiredClaimKeys": [
54
+ "addresses.0.type",
55
+ "addresses.1.city",
56
+ "array_with_one_sd_object.foo"
57
+ ]
58
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "claims": {
3
+ "nationalities": ["US", "CA", "DE"]
4
+ },
5
+ "disclosureFrame": {
6
+ "nationalities": {
7
+ "_sd": [0, 1]
8
+ }
9
+ },
10
+ "presentationKeys": ["nationalities.1"],
11
+ "presenatedClaims": {
12
+ "nationalities": ["CA", "DE"]
13
+ },
14
+ "requiredClaimKeys": ["nationalities.0", "nationalities.1"]
15
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "claims": {
3
+ "array_with_recursive_sd": [
4
+ "boring",
5
+ {
6
+ "foo": "bar",
7
+ "baz": {
8
+ "qux": "quxx"
9
+ }
10
+ },
11
+ ["foo", "bar"]
12
+ ],
13
+ "test2": ["foo", "bar"]
14
+ },
15
+ "disclosureFrame": {
16
+ "array_with_recursive_sd": {
17
+ "_sd": [1],
18
+ "1": {
19
+ "_sd": ["baz"]
20
+ },
21
+ "2": {
22
+ "_sd": [0, 1]
23
+ }
24
+ },
25
+ "test2": {
26
+ "_sd": [0, 1]
27
+ }
28
+ },
29
+ "presentationKeys": [],
30
+ "presenatedClaims": {
31
+ "array_with_recursive_sd": ["boring", []],
32
+ "test2": []
33
+ },
34
+ "requiredClaimKeys": ["array_with_recursive_sd.0", "test2"]
35
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "claims": {
3
+ "array_with_recursive_sd": [
4
+ "boring",
5
+ {
6
+ "foo": "bar",
7
+ "baz": {
8
+ "qux": "quxx"
9
+ }
10
+ },
11
+ ["foo", "bar"]
12
+ ],
13
+ "test2": ["foo", "bar"]
14
+ },
15
+ "disclosureFrame": {
16
+ "array_with_recursive_sd": {
17
+ "1": {
18
+ "_sd": ["baz"]
19
+ },
20
+ "2": {
21
+ "_sd": [0, 1]
22
+ }
23
+ },
24
+ "test2": {
25
+ "_sd": [0, 1]
26
+ }
27
+ },
28
+ "presentationKeys": [
29
+ "array_with_recursive_sd.1.baz",
30
+ "array_with_recursive_sd.2.1",
31
+ "test2.0",
32
+ "test2.1"
33
+ ],
34
+ "presenatedClaims": {
35
+ "array_with_recursive_sd": [
36
+ "boring",
37
+ {
38
+ "foo": "bar",
39
+ "baz": {
40
+ "qux": "quxx"
41
+ }
42
+ },
43
+ ["bar"]
44
+ ],
45
+ "test2": ["foo", "bar"]
46
+ },
47
+ "requiredClaimKeys": [
48
+ "array_with_recursive_sd.1",
49
+ "array_with_recursive_sd.2",
50
+ "array_with_recursive_sd.1.baz",
51
+ "array_with_recursive_sd.2.1",
52
+ "test2.0",
53
+ "test2.1"
54
+ ]
55
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "claims": {
3
+ "firstname": "John",
4
+ "lastname": "Doe",
5
+ "ssn": "123-45-6789",
6
+ "id": "1234",
7
+ "data": {
8
+ "firstname": "John",
9
+ "lastname": "Doe",
10
+ "ssn": "123-45-6789",
11
+ "list": [{ "r": "1" }, "b", "c"]
12
+ },
13
+ "data2": {
14
+ "hi": "bye"
15
+ }
16
+ },
17
+ "disclosureFrame": {
18
+ "_sd": ["firstname", "id", "data2"],
19
+ "data": {
20
+ "_sd": ["list"],
21
+ "_sd_decoy": 2,
22
+ "list": {
23
+ "_sd": [0, 2],
24
+ "_sd_decoy": 1,
25
+ "0": {
26
+ "_sd": ["r"]
27
+ }
28
+ }
29
+ },
30
+ "data2": {
31
+ "_sd": ["hi"]
32
+ }
33
+ },
34
+ "presentationKeys": ["firstname", "id"],
35
+ "presenatedClaims": {
36
+ "lastname": "Doe",
37
+ "ssn": "123-45-6789",
38
+ "data": { "firstname": "John", "lastname": "Doe", "ssn": "123-45-6789" },
39
+ "id": "1234",
40
+ "firstname": "John"
41
+ },
42
+ "requiredClaimKeys": ["firstname", "id", "data.ssn"]
43
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "claims": {
3
+ "sub": "john_deo_42",
4
+ "given_name": "John",
5
+ "family_name": "Deo",
6
+ "email": "johndeo@example.com",
7
+ "phone": "+1-202-555-0101",
8
+ "address": {
9
+ "street_address": "123 Main St",
10
+ "locality": "Anytown",
11
+ "region": "Anystate",
12
+ "country": "US"
13
+ },
14
+ "birthdate": "1940-01-01"
15
+ },
16
+ "disclosureFrame": {
17
+ "_sd": [
18
+ "sub",
19
+ "given_name",
20
+ "family_name",
21
+ "email",
22
+ "phone",
23
+ "address",
24
+ "birthdate"
25
+ ]
26
+ },
27
+ "presentationKeys": ["given_name", "family_name", "address"],
28
+ "presenatedClaims": {
29
+ "given_name": "John",
30
+ "family_name": "Deo",
31
+ "address": {
32
+ "street_address": "123 Main St",
33
+ "locality": "Anytown",
34
+ "region": "Anystate",
35
+ "country": "US"
36
+ }
37
+ },
38
+ "requiredClaimKeys": [
39
+ "given_name",
40
+ "family_name",
41
+ "address.street_address",
42
+ "address.country"
43
+ ]
44
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "claims": {
3
+ "sub": "john_deo_42",
4
+ "given_name": "John",
5
+ "family_name": "Deo",
6
+ "email": "johndeo@example.com",
7
+ "phone": "+1-202-555-0101",
8
+ "address": {
9
+ "street_address": "123 Main St",
10
+ "locality": "Anytown",
11
+ "region": "Anystate",
12
+ "country": "US"
13
+ },
14
+ "birthdate": "1940-01-01"
15
+ },
16
+ "disclosureFrame": {
17
+ "_sd": [
18
+ "sub",
19
+ "given_name",
20
+ "family_name",
21
+ "email",
22
+ "phone",
23
+ "address",
24
+ "birthdate"
25
+ ]
26
+ },
27
+ "presentationKeys": ["given_name", "family_name", "address"],
28
+ "presenatedClaims": {
29
+ "given_name": "John",
30
+ "family_name": "Deo",
31
+ "address": {
32
+ "street_address": "123 Main St",
33
+ "locality": "Anytown",
34
+ "region": "Anystate",
35
+ "country": "US"
36
+ }
37
+ },
38
+ "requiredClaimKeys": [
39
+ "given_name",
40
+ "family_name",
41
+ "address.street_address",
42
+ "address.country"
43
+ ]
44
+ }