@sd-jwt/core 0.1.0 → 0.1.1-next.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.
Files changed (170) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +201 -0
  3. package/README.md +106 -0
  4. package/dist/index.d.mts +659 -0
  5. package/dist/index.d.ts +659 -0
  6. package/dist/index.js +1906 -0
  7. package/dist/index.mjs +1846 -0
  8. package/package.json +59 -47
  9. package/src/decode/decode.ts +376 -0
  10. package/src/decode/index.ts +1 -0
  11. package/src/decoy.ts +15 -0
  12. package/src/flattenJSON.ts +99 -0
  13. package/src/generalJSON.ts +148 -0
  14. package/src/index.ts +945 -0
  15. package/src/jwt.ts +190 -0
  16. package/src/kbjwt.ts +81 -0
  17. package/src/present/index.ts +1 -0
  18. package/src/present/present.ts +210 -0
  19. package/src/sdjwt.ts +354 -0
  20. package/src/test/decode/decode.spec.ts +202 -0
  21. package/src/test/decoy.spec.ts +30 -0
  22. package/src/test/flattenJSON.spec.ts +122 -0
  23. package/src/test/generalJSON.spec.ts +198 -0
  24. package/src/test/index.spec.ts +974 -0
  25. package/src/test/jwt.spec.ts +301 -0
  26. package/src/test/kbjwt.spec.ts +565 -0
  27. package/src/test/pass.spec.ts +7 -0
  28. package/src/test/present/present.spec.ts +305 -0
  29. package/src/test/sdjwt.spec.ts +384 -0
  30. package/src/test/types/type.spec.ts +88 -0
  31. package/src/test/utils/base64url.spec.ts +33 -0
  32. package/src/test/utils/disclosure.spec.ts +170 -0
  33. package/src/test/utils/error.spec.ts +15 -0
  34. package/src/types/index.ts +2 -0
  35. package/src/types/type.ts +249 -0
  36. package/src/types/verification-error.ts +55 -0
  37. package/src/utils/base64url.ts +6 -0
  38. package/src/utils/disclosure.ts +101 -0
  39. package/src/utils/error.ts +25 -0
  40. package/src/utils/index.ts +3 -0
  41. package/src/utils/strict-json.ts +17 -0
  42. package/test/app-e2e.spec.ts +292 -0
  43. package/test/array_data_types.json +31 -0
  44. package/test/array_full_sd.json +21 -0
  45. package/test/array_in_sd.json +13 -0
  46. package/test/array_nested_in_plain.json +29 -0
  47. package/test/array_none_disclosed.json +17 -0
  48. package/test/array_of_nulls.json +15 -0
  49. package/test/array_of_objects.json +65 -0
  50. package/test/array_of_scalars.json +19 -0
  51. package/test/array_recursive_sd.json +35 -0
  52. package/test/array_recursive_sd_some_disclosed.json +63 -0
  53. package/test/complex.json +43 -0
  54. package/test/header_mod.json +48 -0
  55. package/test/json_serialization.json +48 -0
  56. package/test/key_binding.json +48 -0
  57. package/test/no_sd.json +36 -0
  58. package/test/object_data_types.json +62 -0
  59. package/test/recursions.json +117 -0
  60. package/test/rfc9901-validation.spec.ts +150 -0
  61. package/tsconfig.json +7 -0
  62. package/vitest.config.mts +4 -0
  63. package/build/base64url.d.ts +0 -28
  64. package/build/base64url.js +0 -40
  65. package/build/base64url.js.map +0 -1
  66. package/build/hasherAlgorithm.d.ts +0 -70
  67. package/build/hasherAlgorithm.js +0 -75
  68. package/build/hasherAlgorithm.js.map +0 -1
  69. package/build/index.d.ts +0 -11
  70. package/build/index.js +0 -20
  71. package/build/index.js.map +0 -1
  72. package/build/jwt/compact.d.ts +0 -6
  73. package/build/jwt/compact.js +0 -27
  74. package/build/jwt/compact.js.map +0 -1
  75. package/build/jwt/error.d.ts +0 -2
  76. package/build/jwt/error.js +0 -7
  77. package/build/jwt/error.js.map +0 -1
  78. package/build/jwt/index.d.ts +0 -3
  79. package/build/jwt/index.js +0 -20
  80. package/build/jwt/index.js.map +0 -1
  81. package/build/jwt/jwt.d.ts +0 -204
  82. package/build/jwt/jwt.js +0 -324
  83. package/build/jwt/jwt.js.map +0 -1
  84. package/build/keyBinding/index.d.ts +0 -1
  85. package/build/keyBinding/index.js +0 -18
  86. package/build/keyBinding/index.js.map +0 -1
  87. package/build/keyBinding/keyBinding.d.ts +0 -56
  88. package/build/keyBinding/keyBinding.js +0 -99
  89. package/build/keyBinding/keyBinding.js.map +0 -1
  90. package/build/sdJwt/compact.d.ts +0 -8
  91. package/build/sdJwt/compact.js +0 -39
  92. package/build/sdJwt/compact.js.map +0 -1
  93. package/build/sdJwt/decoys.d.ts +0 -2
  94. package/build/sdJwt/decoys.js +0 -35
  95. package/build/sdJwt/decoys.js.map +0 -1
  96. package/build/sdJwt/disclosureFrame.d.ts +0 -7
  97. package/build/sdJwt/disclosureFrame.js +0 -89
  98. package/build/sdJwt/disclosureFrame.js.map +0 -1
  99. package/build/sdJwt/disclosureMapping.d.ts +0 -44
  100. package/build/sdJwt/disclosureMapping.js +0 -289
  101. package/build/sdJwt/disclosureMapping.js.map +0 -1
  102. package/build/sdJwt/disclosures.d.ts +0 -14
  103. package/build/sdJwt/disclosures.js +0 -59
  104. package/build/sdJwt/disclosures.js.map +0 -1
  105. package/build/sdJwt/error.d.ts +0 -2
  106. package/build/sdJwt/error.js +0 -7
  107. package/build/sdJwt/error.js.map +0 -1
  108. package/build/sdJwt/index.d.ts +0 -7
  109. package/build/sdJwt/index.js +0 -24
  110. package/build/sdJwt/index.js.map +0 -1
  111. package/build/sdJwt/presentationFrame.d.ts +0 -4
  112. package/build/sdJwt/presentationFrame.js +0 -73
  113. package/build/sdJwt/presentationFrame.js.map +0 -1
  114. package/build/sdJwt/sdJwt.d.ts +0 -204
  115. package/build/sdJwt/sdJwt.js +0 -383
  116. package/build/sdJwt/sdJwt.js.map +0 -1
  117. package/build/sdJwt/swapClaim.d.ts +0 -3
  118. package/build/sdJwt/swapClaim.js +0 -90
  119. package/build/sdJwt/swapClaim.js.map +0 -1
  120. package/build/sdJwt/types.d.ts +0 -5
  121. package/build/sdJwt/types.js +0 -3
  122. package/build/sdJwt/types.js.map +0 -1
  123. package/build/sdJwtVc/error.d.ts +0 -2
  124. package/build/sdJwtVc/error.js +0 -7
  125. package/build/sdJwtVc/error.js.map +0 -1
  126. package/build/sdJwtVc/index.d.ts +0 -2
  127. package/build/sdJwtVc/index.js +0 -19
  128. package/build/sdJwtVc/index.js.map +0 -1
  129. package/build/sdJwtVc/sdJwtVc.d.ts +0 -47
  130. package/build/sdJwtVc/sdJwtVc.js +0 -125
  131. package/build/sdJwtVc/sdJwtVc.js.map +0 -1
  132. package/build/signatureAndEncryptionAlgorithm.d.ts +0 -105
  133. package/build/signatureAndEncryptionAlgorithm.js +0 -110
  134. package/build/signatureAndEncryptionAlgorithm.js.map +0 -1
  135. package/build/types/disclosure.d.ts +0 -5
  136. package/build/types/disclosure.js +0 -3
  137. package/build/types/disclosure.js.map +0 -1
  138. package/build/types/frame.d.ts +0 -5
  139. package/build/types/frame.js +0 -3
  140. package/build/types/frame.js.map +0 -1
  141. package/build/types/hasher.d.ts +0 -14
  142. package/build/types/hasher.js +0 -3
  143. package/build/types/hasher.js.map +0 -1
  144. package/build/types/index.d.ts +0 -6
  145. package/build/types/index.js +0 -23
  146. package/build/types/index.js.map +0 -1
  147. package/build/types/present.d.ts +0 -2
  148. package/build/types/present.js +0 -3
  149. package/build/types/present.js.map +0 -1
  150. package/build/types/saltGenerator.d.ts +0 -17
  151. package/build/types/saltGenerator.js +0 -3
  152. package/build/types/saltGenerator.js.map +0 -1
  153. package/build/types/signer.d.ts +0 -2
  154. package/build/types/signer.js +0 -3
  155. package/build/types/signer.js.map +0 -1
  156. package/build/types/utils.d.ts +0 -5
  157. package/build/types/utils.js +0 -3
  158. package/build/types/utils.js.map +0 -1
  159. package/build/types/verifier.d.ts +0 -14
  160. package/build/types/verifier.js +0 -3
  161. package/build/types/verifier.js.map +0 -1
  162. package/build/utils/index.d.ts +0 -2
  163. package/build/utils/index.js +0 -19
  164. package/build/utils/index.js.map +0 -1
  165. package/build/utils/traverse.d.ts +0 -8
  166. package/build/utils/traverse.js +0 -29
  167. package/build/utils/traverse.js.map +0 -1
  168. package/build/utils/utils.d.ts +0 -7
  169. package/build/utils/utils.js +0 -109
  170. package/build/utils/utils.js.map +0 -1
@@ -0,0 +1,101 @@
1
+ import type { DisclosureData, HasherAndAlg, HasherAndAlgSync } from '../types';
2
+ import { base64urlEncode, uint8ArrayToBase64Url } from './base64url';
3
+ import { SDJWTException } from './error';
4
+ import { decodeBase64urlJsonStrict } from './strict-json';
5
+
6
+ export class Disclosure<T = unknown> {
7
+ public salt: string;
8
+ public key?: string;
9
+ public value: T;
10
+ public _digest: string | undefined;
11
+ private _encoded: string | undefined;
12
+
13
+ public constructor(
14
+ data: DisclosureData<T>,
15
+ _meta?: { digest: string; encoded: string },
16
+ ) {
17
+ // If the meta is provided, then we assume that the data is already encoded and digested
18
+ this._digest = _meta?.digest;
19
+ this._encoded = _meta?.encoded;
20
+
21
+ if (data.length === 2) {
22
+ this.salt = data[0];
23
+ this.value = data[1];
24
+ return;
25
+ }
26
+ if (data.length === 3) {
27
+ this.salt = data[0];
28
+ this.key = data[1];
29
+ this.value = data[2];
30
+ return;
31
+ }
32
+ throw new SDJWTException('Invalid disclosure data');
33
+ }
34
+
35
+ // We need to digest of the original encoded data.
36
+ // After decode process, we use JSON.stringify to encode the data.
37
+ // This can be different from the original encoded data.
38
+ public static async fromEncode<T>(s: string, hash: HasherAndAlg) {
39
+ const { hasher, alg } = hash;
40
+ const digest = await hasher(s, alg);
41
+ const digestStr = uint8ArrayToBase64Url(digest);
42
+ const item = decodeBase64urlJsonStrict<DisclosureData<T>>(
43
+ s,
44
+ 'Invalid disclosure data',
45
+ );
46
+ return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
47
+ }
48
+
49
+ public static fromEncodeSync<T>(s: string, hash: HasherAndAlgSync) {
50
+ const { hasher, alg } = hash;
51
+ const digest = hasher(s, alg);
52
+ const digestStr = uint8ArrayToBase64Url(digest);
53
+ const item = decodeBase64urlJsonStrict<DisclosureData<T>>(
54
+ s,
55
+ 'Invalid disclosure data',
56
+ );
57
+ return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
58
+ }
59
+
60
+ public static fromArray<T>(
61
+ item: DisclosureData<T>,
62
+ _meta?: { digest: string; encoded: string },
63
+ ) {
64
+ return new Disclosure(item, _meta);
65
+ }
66
+
67
+ public encode() {
68
+ if (!this._encoded) {
69
+ // we use JSON.stringify to encode the data
70
+ // It's the most reliable and universal way to encode JSON object
71
+ this._encoded = base64urlEncode(JSON.stringify(this.decode()));
72
+ }
73
+ return this._encoded;
74
+ }
75
+
76
+ public decode(): DisclosureData<T> {
77
+ return this.key
78
+ ? [this.salt, this.key, this.value]
79
+ : [this.salt, this.value];
80
+ }
81
+
82
+ public async digest(hash: HasherAndAlg): Promise<string> {
83
+ const { hasher, alg } = hash;
84
+ if (!this._digest) {
85
+ const hash = await hasher(this.encode(), alg);
86
+ this._digest = uint8ArrayToBase64Url(hash);
87
+ }
88
+
89
+ return this._digest;
90
+ }
91
+
92
+ public digestSync(hash: HasherAndAlgSync): string {
93
+ const { hasher, alg } = hash;
94
+ if (!this._digest) {
95
+ const hash = hasher(this.encode(), alg);
96
+ this._digest = uint8ArrayToBase64Url(hash);
97
+ }
98
+
99
+ return this._digest;
100
+ }
101
+ }
@@ -0,0 +1,25 @@
1
+ export class SDJWTException extends Error {
2
+ public details?: unknown;
3
+
4
+ constructor(message: string, details?: unknown) {
5
+ super(message);
6
+ Object.setPrototypeOf(this, SDJWTException.prototype);
7
+ this.name = 'SDJWTException';
8
+ this.details = details;
9
+ }
10
+
11
+ getFullMessage(): string {
12
+ return `${this.name}: ${this.message} ${
13
+ this.details ? `- ${JSON.stringify(this.details)}` : ''
14
+ }`;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Narrows an unknown caught value to an Error instance.
20
+ */
21
+ export function ensureError(value: unknown): Error {
22
+ if (value instanceof Error) return value;
23
+ if (typeof value === 'string') return new Error(value);
24
+ return new Error(String(value));
25
+ }
@@ -0,0 +1,3 @@
1
+ export * from './base64url';
2
+ export * from './disclosure';
3
+ export * from './error';
@@ -0,0 +1,17 @@
1
+ import { base64UrlToUint8Array } from './base64url';
2
+ import { SDJWTException } from './error';
3
+
4
+ const utf8Decoder = new TextDecoder('utf-8', { fatal: true });
5
+
6
+ export const decodeBase64urlJsonStrict = <T>(
7
+ encoded: string,
8
+ errorMessage: string,
9
+ ): T => {
10
+ try {
11
+ const bytes = base64UrlToUint8Array(encoded);
12
+ const decoded = utf8Decoder.decode(bytes);
13
+ return JSON.parse(decoded) as T;
14
+ } catch {
15
+ throw new SDJWTException(errorMessage);
16
+ }
17
+ };
@@ -0,0 +1,292 @@
1
+ import Crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { hasher as digest, generateSalt, type Signer } from '@owf/crypto';
5
+ import { describe, expect, test } from 'vitest';
6
+ import {
7
+ type DisclosureFrame,
8
+ type PresentationFrame,
9
+ SDJwtInstance,
10
+ type SdJwtPayload,
11
+ type Verifier,
12
+ } from '../src';
13
+
14
+ const createSignerVerifier = () => {
15
+ const { privateKey, publicKey } = Crypto.generateKeyPairSync('ed25519');
16
+ const signer: Signer = async (data: string) => {
17
+ const sig = Crypto.sign(null, Buffer.from(data), privateKey);
18
+ return Buffer.from(sig).toString('base64url');
19
+ };
20
+ const verifier: Verifier = async (data: string, sig: string) => {
21
+ return Crypto.verify(
22
+ null,
23
+ Buffer.from(data),
24
+ publicKey,
25
+ Buffer.from(sig, 'base64url'),
26
+ );
27
+ };
28
+ return { signer, verifier };
29
+ };
30
+
31
+ describe('App', () => {
32
+ test('Example', async () => {
33
+ const { signer, verifier } = createSignerVerifier();
34
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
35
+ signer,
36
+ signAlg: 'EdDSA',
37
+ verifier,
38
+ hasher: digest,
39
+ hashAlg: 'sha-256',
40
+ saltGenerator: generateSalt,
41
+ });
42
+
43
+ const claims = {
44
+ firstname: 'John',
45
+ lastname: 'Doe',
46
+ ssn: '123-45-6789',
47
+ id: '1234',
48
+ data: {
49
+ firstname: 'John',
50
+ lastname: 'Doe',
51
+ ssn: '123-45-6789',
52
+ list: [{ r: '1' }, 'b', 'c'],
53
+ },
54
+ data2: {
55
+ hi: 'bye',
56
+ },
57
+ };
58
+ const disclosureFrame: DisclosureFrame<typeof claims> = {
59
+ _sd: ['firstname', 'id', 'data2'],
60
+ data: {
61
+ _sd: ['list'],
62
+ _sd_decoy: 2,
63
+ list: {
64
+ _sd: [0, 2],
65
+ _sd_decoy: 1,
66
+ 0: {
67
+ _sd: ['r'],
68
+ },
69
+ },
70
+ },
71
+ data2: {
72
+ _sd: ['hi'],
73
+ },
74
+ };
75
+ const encodedSdjwt = await sdjwt.issue(claims, disclosureFrame);
76
+ expect(encodedSdjwt).toBeDefined();
77
+ const validated = await sdjwt.validate(encodedSdjwt);
78
+ expect(validated).toBeDefined();
79
+
80
+ const decoded = await sdjwt.decode(encodedSdjwt);
81
+ const keys = await decoded.keys(digest);
82
+ expect(keys).toEqual([
83
+ 'data',
84
+ 'data.firstname',
85
+ 'data.lastname',
86
+ 'data.list',
87
+ 'data.list.0',
88
+ 'data.list.0.r',
89
+ 'data.list.1',
90
+ 'data.list.2',
91
+ 'data.ssn',
92
+ 'data2',
93
+ 'data2.hi',
94
+ 'firstname',
95
+ 'id',
96
+ 'lastname',
97
+ 'ssn',
98
+ ]);
99
+ const payloads = await decoded.getClaims(digest);
100
+ expect(payloads).toEqual(claims);
101
+ const presentableKeys = await decoded.presentableKeys(digest);
102
+ expect(presentableKeys).toEqual([
103
+ 'data.list',
104
+ 'data.list.0',
105
+ 'data.list.0.r',
106
+ 'data.list.2',
107
+ 'data2',
108
+ 'data2.hi',
109
+ 'firstname',
110
+ 'id',
111
+ ]);
112
+
113
+ const presentationFrame = {
114
+ firstname: true,
115
+ id: true,
116
+ };
117
+ const presentedSDJwt = await sdjwt.present<typeof claims>(
118
+ encodedSdjwt,
119
+ presentationFrame,
120
+ );
121
+ expect(presentedSDJwt).toBeDefined();
122
+
123
+ const presentationClaims = await sdjwt.getClaims(presentedSDJwt);
124
+ expect(presentationClaims).toBeDefined();
125
+ expect(presentationClaims).toEqual({
126
+ lastname: 'Doe',
127
+ ssn: '123-45-6789',
128
+ data: { firstname: 'John', lastname: 'Doe', ssn: '123-45-6789' },
129
+ id: '1234',
130
+ firstname: 'John',
131
+ });
132
+
133
+ const requiredClaimKeys = ['firstname', 'id', 'data.ssn'];
134
+ const verified = await sdjwt.verify(encodedSdjwt, { requiredClaimKeys });
135
+ expect(verified).toBeDefined();
136
+ });
137
+
138
+ test('rejects tampered disclosure bytes', async () => {
139
+ const { signer, verifier } = createSignerVerifier();
140
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
141
+ signer,
142
+ signAlg: 'EdDSA',
143
+ verifier,
144
+ hasher: digest,
145
+ hashAlg: 'sha-256',
146
+ saltGenerator: generateSalt,
147
+ });
148
+
149
+ const claims = {
150
+ firstname: 'John',
151
+ lastname: 'Doe',
152
+ };
153
+ const disclosureFrame: DisclosureFrame<typeof claims> = {
154
+ _sd: ['firstname', 'lastname'],
155
+ };
156
+
157
+ const encodedSdjwt = await sdjwt.issue(claims, disclosureFrame);
158
+ const parts = encodedSdjwt.split('~');
159
+ const disclosureBytes = Buffer.from(parts[1], 'base64url');
160
+ disclosureBytes[2] = 0xf2;
161
+ parts[1] = Buffer.from(disclosureBytes).toString('base64url');
162
+ const tamperedSdjwt = parts.join('~');
163
+
164
+ await expect(sdjwt.verify(tamperedSdjwt)).rejects.toThrow(
165
+ 'Invalid disclosure data',
166
+ );
167
+ });
168
+
169
+ test('From JSON (complex)', async () => {
170
+ await JSONtest('./complex.json');
171
+ });
172
+
173
+ test('From JSON (array_data_types)', async () => {
174
+ await JSONtest('./array_data_types.json');
175
+ });
176
+
177
+ test('From JSON (array_full_sd)', async () => {
178
+ await JSONtest('./array_full_sd.json');
179
+ });
180
+
181
+ test('From JSON (array_in_sd)', async () => {
182
+ await JSONtest('./array_in_sd.json');
183
+ });
184
+
185
+ test('From JSON (array_recursive_sd_some_disclosed)', async () => {
186
+ await JSONtest('./array_recursive_sd_some_disclosed.json');
187
+ });
188
+
189
+ test('From JSON (header_mod)', async () => {
190
+ await JSONtest('./header_mod.json');
191
+ });
192
+
193
+ test('From JSON (json_serialization)', async () => {
194
+ await JSONtest('./json_serialization.json');
195
+ });
196
+
197
+ test('From JSON (key_binding)', async () => {
198
+ await JSONtest('./key_binding.json');
199
+ });
200
+
201
+ test('From JSON (no_sd)', async () => {
202
+ await JSONtest('./no_sd.json');
203
+ });
204
+
205
+ test('From JSON (object_data_types)', async () => {
206
+ await JSONtest('./object_data_types.json');
207
+ });
208
+
209
+ test('From JSON (recursions)', async () => {
210
+ await JSONtest('./recursions.json');
211
+ });
212
+
213
+ test('From JSON (array_recursive_sd)', async () => {
214
+ await JSONtest('./array_recursive_sd.json');
215
+ });
216
+
217
+ test('From JSON (array_of_scalars)', async () => {
218
+ await JSONtest('./array_of_scalars.json');
219
+ });
220
+
221
+ test('From JSON (array_of_objects)', async () => {
222
+ await JSONtest('./array_of_objects.json');
223
+ });
224
+
225
+ test('From JSON (array_of_nulls)', async () => {
226
+ await JSONtest('./array_of_nulls.json');
227
+ });
228
+
229
+ test('From JSON (array_nested_in_plain)', async () => {
230
+ await JSONtest('./array_nested_in_plain.json');
231
+ });
232
+ });
233
+
234
+ async function JSONtest(filename: string) {
235
+ const test = loadTestJsonFile(filename);
236
+ const { signer, verifier } = createSignerVerifier();
237
+ const sdjwt = new SDJwtInstance<SdJwtPayload>({
238
+ signer,
239
+ signAlg: 'EdDSA',
240
+ verifier,
241
+ hasher: digest,
242
+ hashAlg: 'sha-256',
243
+ saltGenerator: generateSalt,
244
+ });
245
+
246
+ const encodedSdjwt = await sdjwt.issue(test.claims, test.disclosureFrame);
247
+
248
+ expect(encodedSdjwt).toBeDefined();
249
+
250
+ const validated = await sdjwt.validate(encodedSdjwt);
251
+
252
+ expect(validated).toBeDefined();
253
+ expect(validated).toStrictEqual({
254
+ header: { alg: 'EdDSA' },
255
+ payload: test.claims,
256
+ });
257
+
258
+ const presentedSDJwt = await sdjwt.present<typeof test.claims>(
259
+ encodedSdjwt,
260
+ test.presentationFrames,
261
+ );
262
+
263
+ expect(presentedSDJwt).toBeDefined();
264
+
265
+ const presentationClaims = await sdjwt.getClaims(presentedSDJwt);
266
+
267
+ expect(presentationClaims).toEqual(test.presentedClaims);
268
+
269
+ const verified = await sdjwt.verify(encodedSdjwt, {
270
+ requiredClaimKeys: test.requiredClaimKeys,
271
+ });
272
+
273
+ expect(verified).toBeDefined();
274
+ expect(verified).toStrictEqual({
275
+ header: { alg: 'EdDSA' },
276
+ payload: test.claims,
277
+ });
278
+ }
279
+
280
+ type TestJson = {
281
+ claims: SdJwtPayload;
282
+ disclosureFrame: DisclosureFrame<SdJwtPayload>;
283
+ presentationFrames: PresentationFrame<SdJwtPayload>;
284
+ presentedClaims: object;
285
+ requiredClaimKeys: string[];
286
+ };
287
+
288
+ function loadTestJsonFile(filename: string) {
289
+ const filepath = path.join(__dirname, filename);
290
+ const fileContents = fs.readFileSync(filepath, 'utf8');
291
+ return JSON.parse(fileContents) as TestJson;
292
+ }
@@ -0,0 +1,31 @@
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
+ "presentationFrames": {
11
+ "data_types": {
12
+ "0": true,
13
+ "1": true,
14
+ "2": true,
15
+ "3": true,
16
+ "4": true,
17
+ "5": true
18
+ }
19
+ },
20
+ "presentedClaims": {
21
+ "data_types": [null, 42, 3.14, "foo", ["Test"], { "foo": "bar" }]
22
+ },
23
+ "requiredClaimKeys": [
24
+ "data_types.0",
25
+ "data_types.1",
26
+ "data_types.2",
27
+ "data_types.3",
28
+ "data_types.4",
29
+ "data_types.5"
30
+ ]
31
+ }
@@ -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
+ "presentationFrames": { "is_over": { "18": true } },
15
+ "presentedClaims": {
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
+ "presentationFrames": { "sd_array": true },
9
+ "presentedClaims": {
10
+ "sd_array": ["32", "23"]
11
+ },
12
+ "requiredClaimKeys": ["sd_array"]
13
+ }
@@ -0,0 +1,29 @@
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
+ "presentationFrames": {
16
+ "nested_array": {
17
+ "0": {
18
+ "0": true
19
+ },
20
+ "1": {
21
+ "1": true
22
+ }
23
+ }
24
+ },
25
+ "presentedClaims": {
26
+ "nested_array": [["foo"], ["qux"]]
27
+ },
28
+ "requiredClaimKeys": ["nested_array.0.0", "nested_array.1.0"]
29
+ }
@@ -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
+ "presentationFrames": {},
15
+ "presentedClaims": {},
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
+ "presentationFrames": {},
11
+ "presentedClaims": {
12
+ "null_values": [null, null]
13
+ },
14
+ "requiredClaimKeys": ["null_values.0", "null_values.1"]
15
+ }
@@ -0,0 +1,65 @@
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
+ "presentationFrames": {
32
+ "addresses": {
33
+ "1": true
34
+ },
35
+ "array_with_one_sd_object": {
36
+ "foo": true
37
+ }
38
+ },
39
+ "presentedClaims": {
40
+ "addresses": [
41
+ {
42
+ "street": "123 Main St",
43
+ "city": "Anytown",
44
+ "state": "NY",
45
+ "zip": "12345",
46
+ "type": "main_address"
47
+ },
48
+ {
49
+ "street": "456 Main St",
50
+ "city": "Anytown",
51
+ "state": "NY",
52
+ "zip": "12345",
53
+ "type": "secondary_address"
54
+ }
55
+ ],
56
+ "array_with_one_sd_object": {
57
+ "foo": "bar"
58
+ }
59
+ },
60
+ "requiredClaimKeys": [
61
+ "addresses.0.type",
62
+ "addresses.1.city",
63
+ "array_with_one_sd_object.foo"
64
+ ]
65
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "claims": {
3
+ "nationalities": ["US", "CA", "DE"]
4
+ },
5
+ "disclosureFrame": {
6
+ "nationalities": {
7
+ "_sd": [0, 1]
8
+ }
9
+ },
10
+ "presentationFrames": {
11
+ "nationalities": {
12
+ "1": true
13
+ }
14
+ },
15
+ "presentedClaims": {
16
+ "nationalities": ["CA", "DE"]
17
+ },
18
+ "requiredClaimKeys": ["nationalities.0", "nationalities.1"]
19
+ }
@@ -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
+ "presentationFrames": {},
30
+ "presentedClaims": {
31
+ "array_with_recursive_sd": ["boring", []],
32
+ "test2": []
33
+ },
34
+ "requiredClaimKeys": ["array_with_recursive_sd.0", "test2"]
35
+ }