@vess-id/status-list 0.1.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.
- package/LICENSE +51 -0
- package/README.md +710 -0
- package/dist/index.cjs +1576 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1634 -0
- package/dist/index.d.ts +1634 -0
- package/dist/index.js +1508 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1634 @@
|
|
|
1
|
+
import { KeyLike } from 'jose';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Number of bits used per status entry in the status list.
|
|
5
|
+
* Must be one of: 1, 2, 4, or 8 bits per status.
|
|
6
|
+
*
|
|
7
|
+
* - 1 bit: 2 possible states (e.g., valid/invalid)
|
|
8
|
+
* - 2 bits: 4 possible states (e.g., valid/invalid/suspended/custom)
|
|
9
|
+
* - 4 bits: 16 possible states
|
|
10
|
+
* - 8 bits: 256 possible states
|
|
11
|
+
*/
|
|
12
|
+
type BitsPerStatus = 1 | 2 | 4 | 8;
|
|
13
|
+
/**
|
|
14
|
+
* Status value for a credential.
|
|
15
|
+
* Valid range: 0 to (2^bitsPerStatus - 1)
|
|
16
|
+
*
|
|
17
|
+
* For 1-bit: 0-1
|
|
18
|
+
* For 2-bit: 0-3
|
|
19
|
+
* For 4-bit: 0-15
|
|
20
|
+
* For 8-bit: 0-255
|
|
21
|
+
*/
|
|
22
|
+
type StatusValue = number;
|
|
23
|
+
/**
|
|
24
|
+
* Standard status values defined by the IETF OAuth Status List specification.
|
|
25
|
+
*
|
|
26
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-7
|
|
27
|
+
*/
|
|
28
|
+
declare enum StandardStatusValues {
|
|
29
|
+
/**
|
|
30
|
+
* 0x00: The status of the Referenced Token is VALID, CORRECT, or NOT_REVOKED.
|
|
31
|
+
*/
|
|
32
|
+
VALID = 0,
|
|
33
|
+
/**
|
|
34
|
+
* 0x01: The status of the Referenced Token is INVALID, REVOKED, ANNULLED, RECALLED, or CANCELLED.
|
|
35
|
+
*/
|
|
36
|
+
INVALID = 1,
|
|
37
|
+
/**
|
|
38
|
+
* 0x02: The status of the Referenced Token is SUSPENDED.
|
|
39
|
+
* The Issuer may decide to set this status temporarily to investigate
|
|
40
|
+
* or respond to specific situations (e.g., fraud investigation).
|
|
41
|
+
*/
|
|
42
|
+
SUSPENDED = 2,
|
|
43
|
+
/**
|
|
44
|
+
* 0x03: Application-specific status value.
|
|
45
|
+
*/
|
|
46
|
+
APPLICATION_SPECIFIC = 3
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format of the status list token.
|
|
50
|
+
*/
|
|
51
|
+
type StatusFormat = 'jwt' | 'cwt';
|
|
52
|
+
/**
|
|
53
|
+
* Configuration for creating a status list.
|
|
54
|
+
*/
|
|
55
|
+
interface StatusListConfig {
|
|
56
|
+
/**
|
|
57
|
+
* Number of entries in the status list.
|
|
58
|
+
*/
|
|
59
|
+
size: number;
|
|
60
|
+
/**
|
|
61
|
+
* Number of bits per status entry.
|
|
62
|
+
*/
|
|
63
|
+
bitsPerStatus: BitsPerStatus;
|
|
64
|
+
/**
|
|
65
|
+
* Optional URI for status list aggregation.
|
|
66
|
+
*/
|
|
67
|
+
aggregationUri?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Represents a Status List containing credential statuses in a compressed bit array.
|
|
72
|
+
*
|
|
73
|
+
* The StatusList class manages a byte array where each status entry uses a configurable
|
|
74
|
+
* number of bits (1, 2, 4, or 8). This allows efficient storage of credential status
|
|
75
|
+
* information for thousands or millions of credentials.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* // Create a status list with 1000 entries (all valid)
|
|
80
|
+
* const list = StatusList.fromArray(new Array(1000).fill(0), 1);
|
|
81
|
+
*
|
|
82
|
+
* // Revoke a credential at index 42
|
|
83
|
+
* list.setStatus(42, 1);
|
|
84
|
+
*
|
|
85
|
+
* // Compress for JWT
|
|
86
|
+
* const compressed = list.compressToBase64URL();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare class StatusList {
|
|
90
|
+
private readonly statusArray;
|
|
91
|
+
private readonly bitsPerStatus;
|
|
92
|
+
/**
|
|
93
|
+
* Creates a StatusList from a byte array.
|
|
94
|
+
*
|
|
95
|
+
* @param statusArray - Byte array containing packed status values
|
|
96
|
+
* @param bitsPerStatus - Number of bits per status entry
|
|
97
|
+
*
|
|
98
|
+
* @private Use static factory methods instead: fromArray(), decompressFromBase64URL(), decompressFromBytes()
|
|
99
|
+
*/
|
|
100
|
+
constructor(statusArray: Uint8Array, bitsPerStatus: BitsPerStatus);
|
|
101
|
+
/**
|
|
102
|
+
* Gets the status value at a specific index.
|
|
103
|
+
*
|
|
104
|
+
* @param index - Index of the status entry (0-based)
|
|
105
|
+
* @returns Status value at the given index
|
|
106
|
+
* @throws {IndexOutOfBoundsError} If index is out of bounds
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const status = list.getStatus(42);
|
|
111
|
+
* if (status === StandardStatusValues.INVALID) {
|
|
112
|
+
* console.log('Credential is revoked');
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
getStatus(index: number): StatusValue;
|
|
117
|
+
/**
|
|
118
|
+
* Sets the status value at a specific index.
|
|
119
|
+
*
|
|
120
|
+
* Note: This modifies the status list in place. After modifying statuses,
|
|
121
|
+
* you'll need to recompress and re-sign the status list token.
|
|
122
|
+
*
|
|
123
|
+
* @param index - Index of the status entry (0-based)
|
|
124
|
+
* @param value - New status value
|
|
125
|
+
* @throws {IndexOutOfBoundsError} If index is out of bounds
|
|
126
|
+
* @throws {InvalidStatusValueError} If value exceeds the maximum for the bit size
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```typescript
|
|
130
|
+
* // Revoke credential at index 42
|
|
131
|
+
* list.setStatus(42, StandardStatusValues.INVALID);
|
|
132
|
+
*
|
|
133
|
+
* // Suspend credential at index 100
|
|
134
|
+
* list.setStatus(100, StandardStatusValues.SUSPENDED);
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
setStatus(index: number, value: StatusValue): void;
|
|
138
|
+
/**
|
|
139
|
+
* Gets the number of bits used per status entry.
|
|
140
|
+
*
|
|
141
|
+
* @returns Number of bits per status (1, 2, 4, or 8)
|
|
142
|
+
*/
|
|
143
|
+
getBitsPerStatus(): BitsPerStatus;
|
|
144
|
+
/**
|
|
145
|
+
* Gets the total number of status entries in this list.
|
|
146
|
+
*
|
|
147
|
+
* @returns Total capacity of the status list
|
|
148
|
+
*/
|
|
149
|
+
getSize(): number;
|
|
150
|
+
/**
|
|
151
|
+
* Gets the raw byte array (for internal use).
|
|
152
|
+
*
|
|
153
|
+
* @returns The underlying byte array
|
|
154
|
+
*/
|
|
155
|
+
getBytes(): Uint8Array;
|
|
156
|
+
/**
|
|
157
|
+
* Compresses the status list to base64url format (for JWT).
|
|
158
|
+
*
|
|
159
|
+
* @returns Base64URL-encoded compressed status list
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const lst = list.compressToBase64URL();
|
|
164
|
+
* // Use in JWT payload: { status_list: { bits: 1, lst } }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
compressToBase64URL(): string;
|
|
168
|
+
/**
|
|
169
|
+
* Compresses the status list to raw bytes (for CWT).
|
|
170
|
+
*
|
|
171
|
+
* @returns Compressed status list as bytes
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const lst = list.compressToBytes();
|
|
176
|
+
* // Use in CWT payload: { 65533: { bits: 1, lst } }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
compressToBytes(): Uint8Array;
|
|
180
|
+
/**
|
|
181
|
+
* Creates a StatusList from an array of status values.
|
|
182
|
+
*
|
|
183
|
+
* @param statuses - Array of status values
|
|
184
|
+
* @param bitsPerStatus - Number of bits per status entry (1, 2, 4, or 8)
|
|
185
|
+
* @returns New StatusList instance
|
|
186
|
+
* @throws {InvalidBitSizeError} If bitsPerStatus is not 1, 2, 4, or 8
|
|
187
|
+
* @throws {InvalidStatusValueError} If any status value exceeds the maximum for the bit size
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* // Create list with 1-bit statuses (valid/invalid)
|
|
192
|
+
* const statuses = new Array(10000).fill(0); // All valid
|
|
193
|
+
* statuses[42] = 1; // Revoke one credential
|
|
194
|
+
* const list = StatusList.fromArray(statuses, 1);
|
|
195
|
+
*
|
|
196
|
+
* // Create list with 2-bit statuses (valid/invalid/suspended/custom)
|
|
197
|
+
* const statuses2 = [0, 1, 2, 0, 1, 3];
|
|
198
|
+
* const list2 = StatusList.fromArray(statuses2, 2);
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
static fromArray(statuses: StatusValue[], bitsPerStatus: BitsPerStatus): StatusList;
|
|
202
|
+
/**
|
|
203
|
+
* Creates a StatusList by decompressing a base64url-encoded string (from JWT).
|
|
204
|
+
*
|
|
205
|
+
* @param compressed - Base64URL-encoded compressed status list
|
|
206
|
+
* @param bitsPerStatus - Number of bits per status entry
|
|
207
|
+
* @returns New StatusList instance
|
|
208
|
+
* @throws {CompressionError} If decompression fails
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* // From JWT payload
|
|
213
|
+
* const payload = parseJWT(token);
|
|
214
|
+
* const list = StatusList.decompressFromBase64URL(
|
|
215
|
+
* payload.status_list.lst,
|
|
216
|
+
* payload.status_list.bits
|
|
217
|
+
* );
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
static decompressFromBase64URL(compressed: string, bitsPerStatus: BitsPerStatus): StatusList;
|
|
221
|
+
/**
|
|
222
|
+
* Creates a StatusList by decompressing raw bytes (from CWT).
|
|
223
|
+
*
|
|
224
|
+
* @param compressed - Compressed status list as bytes
|
|
225
|
+
* @param bitsPerStatus - Number of bits per status entry
|
|
226
|
+
* @returns New StatusList instance
|
|
227
|
+
* @throws {CompressionError} If decompression fails
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* // From CWT payload
|
|
232
|
+
* const payload = parseCWT(token);
|
|
233
|
+
* const statusListData = payload[65533]; // status_list claim
|
|
234
|
+
* const list = StatusList.decompressFromBytes(
|
|
235
|
+
* statusListData.lst,
|
|
236
|
+
* statusListData.bits
|
|
237
|
+
* );
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
static decompressFromBytes(compressed: Uint8Array, bitsPerStatus: BitsPerStatus): StatusList;
|
|
241
|
+
/**
|
|
242
|
+
* Creates an empty StatusList with a specified capacity.
|
|
243
|
+
*
|
|
244
|
+
* All status entries are initialized to 0 (VALID).
|
|
245
|
+
*
|
|
246
|
+
* @param capacity - Number of status entries
|
|
247
|
+
* @param bitsPerStatus - Number of bits per status entry (1, 2, 4, or 8)
|
|
248
|
+
* @returns New StatusList instance
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```typescript
|
|
252
|
+
* // Create empty list for 100,000 credentials
|
|
253
|
+
* const list = StatusList.create(100000, 1);
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
static create(capacity: number, bitsPerStatus: BitsPerStatus): StatusList;
|
|
257
|
+
/**
|
|
258
|
+
* Converts the status list to an array of status values (for debugging/testing).
|
|
259
|
+
*
|
|
260
|
+
* Warning: For large status lists, this can be memory-intensive.
|
|
261
|
+
*
|
|
262
|
+
* @returns Array of all status values
|
|
263
|
+
*/
|
|
264
|
+
toArray(): StatusValue[];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Compresses data using ZLIB/DEFLATE compression with maximum compression level.
|
|
269
|
+
*
|
|
270
|
+
* Uses the DEFLATE algorithm with ZLIB wrapper (as required by the IETF specification).
|
|
271
|
+
* Maximum compression level (9) is used as recommended by the spec for optimal size reduction.
|
|
272
|
+
*
|
|
273
|
+
* @param data - Uncompressed data to compress
|
|
274
|
+
* @returns Compressed data with ZLIB header
|
|
275
|
+
* @throws {CompressionError} If compression fails
|
|
276
|
+
*
|
|
277
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-4.1
|
|
278
|
+
*/
|
|
279
|
+
declare function compress(data: Uint8Array): Uint8Array;
|
|
280
|
+
/**
|
|
281
|
+
* Decompresses ZLIB/DEFLATE compressed data.
|
|
282
|
+
*
|
|
283
|
+
* @param compressed - Compressed data with ZLIB header
|
|
284
|
+
* @returns Decompressed data
|
|
285
|
+
* @throws {CompressionError} If decompression fails
|
|
286
|
+
*/
|
|
287
|
+
declare function decompress(compressed: Uint8Array): Uint8Array;
|
|
288
|
+
/**
|
|
289
|
+
* Compresses data and returns it as a base64url-encoded string (for JWT format).
|
|
290
|
+
*
|
|
291
|
+
* @param data - Uncompressed data
|
|
292
|
+
* @returns Base64URL-encoded compressed data
|
|
293
|
+
* @throws {CompressionError} If compression fails
|
|
294
|
+
*/
|
|
295
|
+
declare function compressToBase64URL(data: Uint8Array): string;
|
|
296
|
+
/**
|
|
297
|
+
* Decompresses base64url-encoded data (from JWT format).
|
|
298
|
+
*
|
|
299
|
+
* @param base64url - Base64URL-encoded compressed data
|
|
300
|
+
* @returns Decompressed data
|
|
301
|
+
* @throws {CompressionError} If decoding or decompression fails
|
|
302
|
+
*/
|
|
303
|
+
declare function decompressFromBase64URL(base64url: string): Uint8Array;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Packs an array of status values into a byte array using the specified bits per status.
|
|
307
|
+
*
|
|
308
|
+
* Bits are packed LSB-first within each byte (Least Significant Bit first).
|
|
309
|
+
* Example for 1-bit status with values [1,0,0,1,1,1,0,1]:
|
|
310
|
+
* - Byte 0: bits 0-7 → 1101 1001 = 0xD9 (LSB first)
|
|
311
|
+
*
|
|
312
|
+
* @param statuses - Array of status values
|
|
313
|
+
* @param bitsPerStatus - Number of bits per status (1, 2, 4, or 8)
|
|
314
|
+
* @returns Byte array containing packed status values
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* // 1-bit status: [1, 0, 0, 1, 1, 1, 0, 1]
|
|
319
|
+
* const bytes = packBits([1, 0, 0, 1, 1, 1, 0, 1], 1);
|
|
320
|
+
* // bytes[0] === 0xD9 (binary: 11011001, LSB first)
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare function packBits(statuses: StatusValue[], bitsPerStatus: BitsPerStatus): Uint8Array;
|
|
324
|
+
/**
|
|
325
|
+
* Unpacks a byte array into an array of status values.
|
|
326
|
+
*
|
|
327
|
+
* @param bytes - Byte array containing packed status values
|
|
328
|
+
* @param bitsPerStatus - Number of bits per status (1, 2, 4, or 8)
|
|
329
|
+
* @returns Array of status values
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* const bytes = new Uint8Array([0xD9]); // binary: 11011001
|
|
334
|
+
* const statuses = unpackBits(bytes, 1); // [1, 0, 0, 1, 1, 1, 0, 1]
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
declare function unpackBits(bytes: Uint8Array, bitsPerStatus: BitsPerStatus): StatusValue[];
|
|
338
|
+
/**
|
|
339
|
+
* Gets the status value at a specific index in the byte array.
|
|
340
|
+
*
|
|
341
|
+
* Implementation based on Python reference:
|
|
342
|
+
* ```python
|
|
343
|
+
* jump = self.bits * position # Bit offset
|
|
344
|
+
* mask = (1 << self.bits) - 1 # n-bit mask
|
|
345
|
+
* status = (self.status_list[jump // 8] >> (jump % 8)) & mask
|
|
346
|
+
* ```
|
|
347
|
+
*
|
|
348
|
+
* @param bytes - Byte array containing packed status values
|
|
349
|
+
* @param index - Index of the status to retrieve
|
|
350
|
+
* @param bitsPerStatus - Number of bits per status
|
|
351
|
+
* @param mask - Optional pre-calculated mask (for performance)
|
|
352
|
+
* @returns Status value at the given index
|
|
353
|
+
* @throws {IndexOutOfBoundsError} If index is out of bounds
|
|
354
|
+
*/
|
|
355
|
+
declare function getBitValue(bytes: Uint8Array, index: number, bitsPerStatus: BitsPerStatus, mask?: number): StatusValue;
|
|
356
|
+
/**
|
|
357
|
+
* Sets the status value at a specific index in the byte array.
|
|
358
|
+
*
|
|
359
|
+
* @param bytes - Byte array containing packed status values (modified in place)
|
|
360
|
+
* @param index - Index of the status to set
|
|
361
|
+
* @param value - New status value
|
|
362
|
+
* @param bitsPerStatus - Number of bits per status
|
|
363
|
+
* @throws {IndexOutOfBoundsError} If index is out of bounds
|
|
364
|
+
* @throws {InvalidStatusValueError} If value exceeds the maximum for the bit size
|
|
365
|
+
*/
|
|
366
|
+
declare function setBitValue(bytes: Uint8Array, index: number, value: StatusValue, bitsPerStatus: BitsPerStatus): void;
|
|
367
|
+
/**
|
|
368
|
+
* Calculates the total number of status entries that can be stored in a byte array.
|
|
369
|
+
*
|
|
370
|
+
* @param byteLength - Length of the byte array
|
|
371
|
+
* @param bitsPerStatus - Number of bits per status
|
|
372
|
+
* @returns Total number of status entries
|
|
373
|
+
*/
|
|
374
|
+
declare function calculateCapacity(byteLength: number, bitsPerStatus: BitsPerStatus): number;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Base error class for all status list errors.
|
|
378
|
+
*/
|
|
379
|
+
declare class StatusListError extends Error {
|
|
380
|
+
constructor(message: string);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Error thrown when attempting to access a status at an index that is out of bounds.
|
|
384
|
+
*/
|
|
385
|
+
declare class IndexOutOfBoundsError extends StatusListError {
|
|
386
|
+
constructor(index: number, maxIndex: number);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Error thrown when an invalid bit size is provided.
|
|
390
|
+
* Valid bit sizes are: 1, 2, 4, or 8.
|
|
391
|
+
*/
|
|
392
|
+
declare class InvalidBitSizeError extends StatusListError {
|
|
393
|
+
constructor(bits: number);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Error thrown when a status value exceeds the maximum allowed for the given bit size.
|
|
397
|
+
*/
|
|
398
|
+
declare class InvalidStatusValueError extends StatusListError {
|
|
399
|
+
constructor(value: number, maxValue: number, bitsPerStatus: number);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Error thrown when a token has an invalid format or cannot be parsed.
|
|
403
|
+
*/
|
|
404
|
+
declare class InvalidTokenFormatError extends StatusListError {
|
|
405
|
+
constructor(message: string);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Error thrown when a status list token has expired.
|
|
409
|
+
*/
|
|
410
|
+
declare class StatusListExpiredError extends StatusListError {
|
|
411
|
+
constructor(exp: number, currentTime: number);
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Error thrown when fetching a status list from a remote URI fails.
|
|
415
|
+
*/
|
|
416
|
+
declare class FetchError extends StatusListError {
|
|
417
|
+
constructor(message: string);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Error thrown when a status list URI is missing or invalid.
|
|
421
|
+
*/
|
|
422
|
+
declare class MissingStatusListUriError extends StatusListError {
|
|
423
|
+
constructor(message?: string);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Error thrown when compression or decompression fails.
|
|
427
|
+
*/
|
|
428
|
+
declare class CompressionError extends StatusListError {
|
|
429
|
+
constructor(message: string, cause?: Error);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Error thrown when validation of a status list token fails.
|
|
433
|
+
*/
|
|
434
|
+
declare class ValidationError extends StatusListError {
|
|
435
|
+
readonly errors: string[];
|
|
436
|
+
constructor(errors: string[]);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* JWT header for a Status List Token.
|
|
441
|
+
*
|
|
442
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-5.1
|
|
443
|
+
*/
|
|
444
|
+
interface StatusListJWTHeader {
|
|
445
|
+
/**
|
|
446
|
+
* REQUIRED: Type header parameter.
|
|
447
|
+
* Must be "statuslist+jwt" for Status List Tokens.
|
|
448
|
+
*/
|
|
449
|
+
typ: 'statuslist+jwt';
|
|
450
|
+
/**
|
|
451
|
+
* REQUIRED: Algorithm used for signing.
|
|
452
|
+
* Common values: "ES256", "ES384", "ES512", "RS256", etc.
|
|
453
|
+
*/
|
|
454
|
+
alg: string;
|
|
455
|
+
/**
|
|
456
|
+
* OPTIONAL: Key ID.
|
|
457
|
+
* Indicates which key was used to sign the JWT.
|
|
458
|
+
*/
|
|
459
|
+
kid?: string;
|
|
460
|
+
/**
|
|
461
|
+
* Allow additional header parameters.
|
|
462
|
+
*/
|
|
463
|
+
[key: string]: unknown;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Status List structure within the JWT payload.
|
|
467
|
+
*/
|
|
468
|
+
interface StatusListClaim {
|
|
469
|
+
/**
|
|
470
|
+
* REQUIRED: Number of bits per status entry.
|
|
471
|
+
* Must be 1, 2, 4, or 8.
|
|
472
|
+
*/
|
|
473
|
+
bits: BitsPerStatus;
|
|
474
|
+
/**
|
|
475
|
+
* REQUIRED: Base64URL-encoded compressed status list.
|
|
476
|
+
* The status list is compressed using DEFLATE/ZLIB before encoding.
|
|
477
|
+
*/
|
|
478
|
+
lst: string;
|
|
479
|
+
/**
|
|
480
|
+
* OPTIONAL: URI for status list aggregation.
|
|
481
|
+
* Points to a resource that aggregates multiple status lists.
|
|
482
|
+
*/
|
|
483
|
+
aggregation_uri?: string;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* JWT payload for a Status List Token.
|
|
487
|
+
*
|
|
488
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-5.1.1
|
|
489
|
+
*/
|
|
490
|
+
interface StatusListJWTPayload {
|
|
491
|
+
/**
|
|
492
|
+
* REQUIRED: Issuer of the status list token.
|
|
493
|
+
* Identifies the principal that issued the JWT.
|
|
494
|
+
*/
|
|
495
|
+
iss: string;
|
|
496
|
+
/**
|
|
497
|
+
* REQUIRED: Subject of the status list token.
|
|
498
|
+
* URI that identifies this specific status list.
|
|
499
|
+
* Must match the "uri" field in referenced tokens.
|
|
500
|
+
*/
|
|
501
|
+
sub: string;
|
|
502
|
+
/**
|
|
503
|
+
* REQUIRED: Issued at time.
|
|
504
|
+
* Timestamp (seconds since epoch) when the status list was created.
|
|
505
|
+
*/
|
|
506
|
+
iat: number;
|
|
507
|
+
/**
|
|
508
|
+
* RECOMMENDED: Expiration time.
|
|
509
|
+
* Timestamp (seconds since epoch) after which the status list is no longer valid.
|
|
510
|
+
*/
|
|
511
|
+
exp?: number;
|
|
512
|
+
/**
|
|
513
|
+
* RECOMMENDED: Time to live (in seconds).
|
|
514
|
+
* Indicates how long the status list should be cached.
|
|
515
|
+
*/
|
|
516
|
+
ttl?: number;
|
|
517
|
+
/**
|
|
518
|
+
* REQUIRED: The status list.
|
|
519
|
+
*/
|
|
520
|
+
status_list: StatusListClaim;
|
|
521
|
+
/**
|
|
522
|
+
* Allow additional claims.
|
|
523
|
+
*/
|
|
524
|
+
[key: string]: unknown;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Status list reference within a credential/token.
|
|
528
|
+
*
|
|
529
|
+
* This appears in the "status" claim of a referenced token (credential).
|
|
530
|
+
*
|
|
531
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-6
|
|
532
|
+
*/
|
|
533
|
+
interface StatusListReference {
|
|
534
|
+
/**
|
|
535
|
+
* REQUIRED: Index of this credential in the status list.
|
|
536
|
+
* Non-negative integer that identifies the position.
|
|
537
|
+
*/
|
|
538
|
+
idx: number;
|
|
539
|
+
/**
|
|
540
|
+
* REQUIRED: URI of the Status List Token.
|
|
541
|
+
* Must match the "sub" claim of the Status List Token.
|
|
542
|
+
*/
|
|
543
|
+
uri: string;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Status claim structure in a referenced token.
|
|
547
|
+
*/
|
|
548
|
+
interface TokenStatusClaim {
|
|
549
|
+
/**
|
|
550
|
+
* Status list reference.
|
|
551
|
+
*/
|
|
552
|
+
status_list: StatusListReference;
|
|
553
|
+
/**
|
|
554
|
+
* Allow additional status mechanisms.
|
|
555
|
+
*/
|
|
556
|
+
[key: string]: unknown;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Options for creating a JWT Status List Token payload.
|
|
560
|
+
*/
|
|
561
|
+
interface CreateJWTStatusListOptions {
|
|
562
|
+
/**
|
|
563
|
+
* REQUIRED: Issuer identifier.
|
|
564
|
+
*/
|
|
565
|
+
iss: string;
|
|
566
|
+
/**
|
|
567
|
+
* REQUIRED: Status list identifier (URI).
|
|
568
|
+
*/
|
|
569
|
+
sub: string;
|
|
570
|
+
/**
|
|
571
|
+
* REQUIRED: Compressed status list (base64url-encoded).
|
|
572
|
+
*/
|
|
573
|
+
lst: string;
|
|
574
|
+
/**
|
|
575
|
+
* REQUIRED: Number of bits per status.
|
|
576
|
+
*/
|
|
577
|
+
bits: BitsPerStatus;
|
|
578
|
+
/**
|
|
579
|
+
* OPTIONAL: Issued at timestamp.
|
|
580
|
+
* Defaults to current time if not provided.
|
|
581
|
+
*/
|
|
582
|
+
iat?: number;
|
|
583
|
+
/**
|
|
584
|
+
* OPTIONAL: Expiration timestamp.
|
|
585
|
+
*/
|
|
586
|
+
exp?: number;
|
|
587
|
+
/**
|
|
588
|
+
* OPTIONAL: Time to live in seconds.
|
|
589
|
+
*/
|
|
590
|
+
ttl?: number;
|
|
591
|
+
/**
|
|
592
|
+
* OPTIONAL: Aggregation URI.
|
|
593
|
+
*/
|
|
594
|
+
aggregation_uri?: string;
|
|
595
|
+
/**
|
|
596
|
+
* OPTIONAL: Additional claims to include in the payload.
|
|
597
|
+
*/
|
|
598
|
+
additionalClaims?: Record<string, unknown>;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Options for signing a Status List JWT.
|
|
602
|
+
*/
|
|
603
|
+
interface SignJWTOptions {
|
|
604
|
+
/**
|
|
605
|
+
* OPTIONAL: Algorithm to use for signing.
|
|
606
|
+
* Defaults to the algorithm specified in the key.
|
|
607
|
+
*/
|
|
608
|
+
alg?: string;
|
|
609
|
+
/**
|
|
610
|
+
* OPTIONAL: Key ID to include in the header.
|
|
611
|
+
*/
|
|
612
|
+
kid?: string;
|
|
613
|
+
/**
|
|
614
|
+
* OPTIONAL: Additional header parameters.
|
|
615
|
+
*/
|
|
616
|
+
additionalHeaders?: Record<string, unknown>;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Result of parsing a JWT Status List Token.
|
|
620
|
+
*/
|
|
621
|
+
interface ParsedJWTStatusList {
|
|
622
|
+
/**
|
|
623
|
+
* JWT header.
|
|
624
|
+
*/
|
|
625
|
+
header: StatusListJWTHeader;
|
|
626
|
+
/**
|
|
627
|
+
* JWT payload.
|
|
628
|
+
*/
|
|
629
|
+
payload: StatusListJWTPayload;
|
|
630
|
+
/**
|
|
631
|
+
* Raw JWT string.
|
|
632
|
+
*/
|
|
633
|
+
jwt: string;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Creates a JWT Status List payload (without signing).
|
|
638
|
+
*
|
|
639
|
+
* This function creates the header and payload objects for a Status List JWT.
|
|
640
|
+
* The actual signing should be done using the `jose` library or similar.
|
|
641
|
+
*
|
|
642
|
+
* @param options - Configuration for creating the status list payload
|
|
643
|
+
* @returns Object containing header and payload ready for signing
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```typescript
|
|
647
|
+
* import { StatusList } from '@vess-id/status-list';
|
|
648
|
+
* import { createJWTStatusListPayload } from '@vess-id/status-list/formats/jwt';
|
|
649
|
+
* import { SignJWT } from 'jose';
|
|
650
|
+
*
|
|
651
|
+
* const list = StatusList.create(1000, 1);
|
|
652
|
+
* const lst = list.compressToBase64URL();
|
|
653
|
+
*
|
|
654
|
+
* const { header, payload } = createJWTStatusListPayload({
|
|
655
|
+
* iss: 'https://issuer.example.com',
|
|
656
|
+
* sub: 'https://issuer.example.com/status/1',
|
|
657
|
+
* lst,
|
|
658
|
+
* bits: 1,
|
|
659
|
+
* iat: Math.floor(Date.now() / 1000),
|
|
660
|
+
* exp: Math.floor(Date.now() / 1000) + 86400,
|
|
661
|
+
* ttl: 3600,
|
|
662
|
+
* });
|
|
663
|
+
*
|
|
664
|
+
* // Sign with jose
|
|
665
|
+
* const jwt = await new SignJWT(payload)
|
|
666
|
+
* .setProtectedHeader(header)
|
|
667
|
+
* .sign(privateKey);
|
|
668
|
+
* ```
|
|
669
|
+
*/
|
|
670
|
+
declare function createJWTStatusListPayload(options: CreateJWTStatusListOptions): {
|
|
671
|
+
header: StatusListJWTHeader;
|
|
672
|
+
payload: StatusListJWTPayload;
|
|
673
|
+
};
|
|
674
|
+
/**
|
|
675
|
+
* Parses a JWT Status List Token (without verifying the signature).
|
|
676
|
+
*
|
|
677
|
+
* This function decodes and validates the structure of a Status List JWT.
|
|
678
|
+
* Signature verification should be done separately using `jose` or similar.
|
|
679
|
+
*
|
|
680
|
+
* @param jwt - JWT string to parse
|
|
681
|
+
* @returns Parsed header, payload, and decompressed status list
|
|
682
|
+
* @throws {InvalidTokenFormatError} If the JWT format is invalid
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```typescript
|
|
686
|
+
* import { parseJWTStatusList } from '@vess-id/status-list/formats/jwt';
|
|
687
|
+
* import { jwtVerify } from 'jose';
|
|
688
|
+
*
|
|
689
|
+
* // First verify the signature
|
|
690
|
+
* const { payload } = await jwtVerify(jwt, publicKey);
|
|
691
|
+
*
|
|
692
|
+
* // Then parse and decompress
|
|
693
|
+
* const { header, payload: parsed, statusList } = parseJWTStatusList(jwt);
|
|
694
|
+
*
|
|
695
|
+
* // Check credential status
|
|
696
|
+
* const status = statusList.getStatus(42);
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
declare function parseJWTStatusList(jwt: string): ParsedJWTStatusList & {
|
|
700
|
+
statusList: StatusList;
|
|
701
|
+
};
|
|
702
|
+
/**
|
|
703
|
+
* Extracts the status list reference from a credential JWT.
|
|
704
|
+
*
|
|
705
|
+
* @param credentialJWT - Credential JWT containing a status claim
|
|
706
|
+
* @returns Status list reference (idx and uri)
|
|
707
|
+
* @throws {InvalidTokenFormatError} If the status claim is missing or invalid
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* import { extractStatusListReference } from '@vess-id/status-list/formats/jwt';
|
|
712
|
+
*
|
|
713
|
+
* const credential = 'eyJ...'; // Credential JWT
|
|
714
|
+
* const { idx, uri } = extractStatusListReference(credential);
|
|
715
|
+
*
|
|
716
|
+
* // Fetch status list from uri and check status at idx
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
declare function extractStatusListReference(credentialJWT: string): StatusListReference;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Signs a Status List JWT using the provided private key.
|
|
723
|
+
*
|
|
724
|
+
* This function uses the `jose` library to create a signed JWT from a Status List payload.
|
|
725
|
+
*
|
|
726
|
+
* @param payload - Status List JWT payload
|
|
727
|
+
* @param privateKey - Private key for signing (KeyLike from jose)
|
|
728
|
+
* @param options - Optional signing options (algorithm, kid, additional headers)
|
|
729
|
+
* @returns Signed JWT string
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```typescript
|
|
733
|
+
* import { signStatusListJWT } from '@vess-id/status-list/formats/jwt';
|
|
734
|
+
* import { StatusList } from '@vess-id/status-list';
|
|
735
|
+
* import { importPKCS8 } from 'jose';
|
|
736
|
+
*
|
|
737
|
+
* // Create status list
|
|
738
|
+
* const list = StatusList.create(1000, 1);
|
|
739
|
+
* const lst = list.compressToBase64URL();
|
|
740
|
+
*
|
|
741
|
+
* // Create payload
|
|
742
|
+
* const payload = {
|
|
743
|
+
* iss: 'https://issuer.example.com',
|
|
744
|
+
* sub: 'https://issuer.example.com/status/1',
|
|
745
|
+
* iat: Math.floor(Date.now() / 1000),
|
|
746
|
+
* exp: Math.floor(Date.now() / 1000) + 86400,
|
|
747
|
+
* ttl: 3600,
|
|
748
|
+
* status_list: { bits: 1, lst },
|
|
749
|
+
* };
|
|
750
|
+
*
|
|
751
|
+
* // Import private key
|
|
752
|
+
* const privateKey = await importPKCS8(pemPrivateKey, 'ES256');
|
|
753
|
+
*
|
|
754
|
+
* // Sign
|
|
755
|
+
* const jwt = await signStatusListJWT(payload, privateKey, {
|
|
756
|
+
* alg: 'ES256',
|
|
757
|
+
* kid: 'key-123',
|
|
758
|
+
* });
|
|
759
|
+
* ```
|
|
760
|
+
*/
|
|
761
|
+
declare function signStatusListJWT(payload: StatusListJWTPayload, privateKey: KeyLike, options?: SignJWTOptions): Promise<string>;
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Options for verifying a Status List JWT.
|
|
765
|
+
*/
|
|
766
|
+
interface VerifyJWTOptions {
|
|
767
|
+
/**
|
|
768
|
+
* Expected issuer (iss claim).
|
|
769
|
+
* If provided, verification will fail if the token's iss doesn't match.
|
|
770
|
+
*/
|
|
771
|
+
issuer?: string;
|
|
772
|
+
/**
|
|
773
|
+
* Expected subject (sub claim).
|
|
774
|
+
* If provided, verification will fail if the token's sub doesn't match.
|
|
775
|
+
*/
|
|
776
|
+
subject?: string;
|
|
777
|
+
/**
|
|
778
|
+
* Current time for validation (in seconds since epoch).
|
|
779
|
+
* Defaults to current time if not provided.
|
|
780
|
+
*/
|
|
781
|
+
currentTime?: number;
|
|
782
|
+
/**
|
|
783
|
+
* Clock tolerance in seconds.
|
|
784
|
+
* Allows for small clock differences between issuer and verifier.
|
|
785
|
+
* Default: 0
|
|
786
|
+
*/
|
|
787
|
+
clockTolerance?: number;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Result of verifying a Status List JWT.
|
|
791
|
+
*/
|
|
792
|
+
interface VerifiedStatusListJWT {
|
|
793
|
+
/**
|
|
794
|
+
* Verified JWT header.
|
|
795
|
+
*/
|
|
796
|
+
header: StatusListJWTHeader;
|
|
797
|
+
/**
|
|
798
|
+
* Verified JWT payload.
|
|
799
|
+
*/
|
|
800
|
+
payload: StatusListJWTPayload;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Verifies the signature and validates a Status List JWT.
|
|
804
|
+
*
|
|
805
|
+
* This function:
|
|
806
|
+
* 1. Verifies the JWT signature using the provided public key
|
|
807
|
+
* 2. Validates the JWT structure and claims
|
|
808
|
+
* 3. Checks expiration (if exp is present)
|
|
809
|
+
* 4. Optionally validates issuer and subject
|
|
810
|
+
*
|
|
811
|
+
* @param jwt - JWT string to verify
|
|
812
|
+
* @param publicKey - Public key for verification (KeyLike from jose)
|
|
813
|
+
* @param options - Optional verification options
|
|
814
|
+
* @returns Verified header and payload
|
|
815
|
+
* @throws {InvalidTokenFormatError} If verification fails
|
|
816
|
+
*
|
|
817
|
+
* @example
|
|
818
|
+
* ```typescript
|
|
819
|
+
* import { verifyStatusListJWT } from '@vess-id/status-list/formats/jwt';
|
|
820
|
+
* import { importSPKI } from 'jose';
|
|
821
|
+
*
|
|
822
|
+
* // Import public key
|
|
823
|
+
* const publicKey = await importSPKI(pemPublicKey, 'ES256');
|
|
824
|
+
*
|
|
825
|
+
* try {
|
|
826
|
+
* const { header, payload } = await verifyStatusListJWT(jwt, publicKey, {
|
|
827
|
+
* issuer: 'https://issuer.example.com',
|
|
828
|
+
* subject: 'https://issuer.example.com/status/1',
|
|
829
|
+
* });
|
|
830
|
+
*
|
|
831
|
+
* console.log('JWT is valid');
|
|
832
|
+
* console.log('Issued at:', new Date(payload.iat * 1000));
|
|
833
|
+
* console.log('Expires at:', payload.exp ? new Date(payload.exp * 1000) : 'Never');
|
|
834
|
+
* } catch (error) {
|
|
835
|
+
* console.error('Verification failed:', error.message);
|
|
836
|
+
* }
|
|
837
|
+
* ```
|
|
838
|
+
*/
|
|
839
|
+
declare function verifyStatusListJWT(jwt: string, publicKey: KeyLike, options?: VerifyJWTOptions): Promise<VerifiedStatusListJWT>;
|
|
840
|
+
/**
|
|
841
|
+
* Verifies only the signature of a Status List JWT without validating claims.
|
|
842
|
+
*
|
|
843
|
+
* Useful when you want to verify the signature but handle claim validation separately.
|
|
844
|
+
*
|
|
845
|
+
* @param jwt - JWT string to verify
|
|
846
|
+
* @param publicKey - Public key for verification
|
|
847
|
+
* @returns Verified header and payload
|
|
848
|
+
* @throws {InvalidTokenFormatError} If signature verification fails
|
|
849
|
+
*/
|
|
850
|
+
declare function verifySignatureOnly(jwt: string, publicKey: KeyLike): Promise<VerifiedStatusListJWT>;
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* CBOR Web Token (CWT) claim keys as defined in RFC 8392.
|
|
854
|
+
*
|
|
855
|
+
* @see https://www.rfc-editor.org/rfc/rfc8392.html#section-3.1
|
|
856
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-5.2
|
|
857
|
+
*/
|
|
858
|
+
declare const CWT_CLAIMS: {
|
|
859
|
+
/** Issuer (iss) - Claim key 1 */
|
|
860
|
+
readonly ISS: 1;
|
|
861
|
+
/** Subject (sub) - Claim key 2 */
|
|
862
|
+
readonly SUB: 2;
|
|
863
|
+
/** Expiration Time (exp) - Claim key 4 */
|
|
864
|
+
readonly EXP: 4;
|
|
865
|
+
/** Issued At (iat) - Claim key 6 */
|
|
866
|
+
readonly IAT: 6;
|
|
867
|
+
/** Time to Live (ttl) - Claim key 65534 (custom) */
|
|
868
|
+
readonly TTL: 65534;
|
|
869
|
+
/** Status List - Claim key 65533 (custom) */
|
|
870
|
+
readonly STATUS_LIST: 65533;
|
|
871
|
+
};
|
|
872
|
+
/**
|
|
873
|
+
* COSE header parameters.
|
|
874
|
+
*
|
|
875
|
+
* @see https://www.rfc-editor.org/rfc/rfc8152.html#section-3.1
|
|
876
|
+
*/
|
|
877
|
+
declare const COSE_HEADERS: {
|
|
878
|
+
/** Algorithm - Header parameter 1 */
|
|
879
|
+
readonly ALG: 1;
|
|
880
|
+
/** Content Type - Header parameter 16 */
|
|
881
|
+
readonly CONTENT_TYPE: 16;
|
|
882
|
+
/** Key ID - Header parameter 4 */
|
|
883
|
+
readonly KID: 4;
|
|
884
|
+
};
|
|
885
|
+
/**
|
|
886
|
+
* COSE algorithms.
|
|
887
|
+
*
|
|
888
|
+
* @see https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
|
889
|
+
*/
|
|
890
|
+
declare const COSE_ALGORITHMS: {
|
|
891
|
+
/** ES256 - ECDSA with SHA-256 */
|
|
892
|
+
readonly ES256: -7;
|
|
893
|
+
/** ES384 - ECDSA with SHA-384 */
|
|
894
|
+
readonly ES384: -35;
|
|
895
|
+
/** ES512 - ECDSA with SHA-512 */
|
|
896
|
+
readonly ES512: -36;
|
|
897
|
+
/** EdDSA */
|
|
898
|
+
readonly EdDSA: -8;
|
|
899
|
+
};
|
|
900
|
+
/**
|
|
901
|
+
* Status List structure in CBOR format.
|
|
902
|
+
*/
|
|
903
|
+
interface StatusListCWTClaim {
|
|
904
|
+
/**
|
|
905
|
+
* REQUIRED: Number of bits per status entry.
|
|
906
|
+
* Must be 1, 2, 4, or 8.
|
|
907
|
+
*/
|
|
908
|
+
bits: BitsPerStatus;
|
|
909
|
+
/**
|
|
910
|
+
* REQUIRED: Compressed status list as raw bytes.
|
|
911
|
+
* Unlike JWT, this is NOT base64url-encoded.
|
|
912
|
+
*/
|
|
913
|
+
lst: Uint8Array;
|
|
914
|
+
/**
|
|
915
|
+
* OPTIONAL: URI for status list aggregation.
|
|
916
|
+
*/
|
|
917
|
+
aggregation_uri?: string;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* CWT payload for a Status List Token.
|
|
921
|
+
*
|
|
922
|
+
* Uses numeric claim keys as defined in RFC 8392 and the IETF spec.
|
|
923
|
+
*
|
|
924
|
+
* @see https://www.ietf.org/archive/id/draft-ietf-oauth-status-list-14.html#section-5.2
|
|
925
|
+
*/
|
|
926
|
+
interface StatusListCWTPayload {
|
|
927
|
+
/**
|
|
928
|
+
* OPTIONAL: Issuer (claim key 1).
|
|
929
|
+
*/
|
|
930
|
+
[CWT_CLAIMS.ISS]?: string;
|
|
931
|
+
/**
|
|
932
|
+
* REQUIRED: Subject (claim key 2).
|
|
933
|
+
* URI that identifies this specific status list.
|
|
934
|
+
*/
|
|
935
|
+
[CWT_CLAIMS.SUB]?: string;
|
|
936
|
+
/**
|
|
937
|
+
* OPTIONAL: Expiration time (claim key 4).
|
|
938
|
+
* Timestamp (seconds since epoch).
|
|
939
|
+
*/
|
|
940
|
+
[CWT_CLAIMS.EXP]?: number;
|
|
941
|
+
/**
|
|
942
|
+
* REQUIRED: Issued at time (claim key 6).
|
|
943
|
+
* Timestamp (seconds since epoch).
|
|
944
|
+
*/
|
|
945
|
+
[CWT_CLAIMS.IAT]?: number;
|
|
946
|
+
/**
|
|
947
|
+
* OPTIONAL: Time to live in seconds (claim key 65534).
|
|
948
|
+
*/
|
|
949
|
+
[CWT_CLAIMS.TTL]?: number;
|
|
950
|
+
/**
|
|
951
|
+
* REQUIRED: The status list (claim key 65533).
|
|
952
|
+
*/
|
|
953
|
+
[CWT_CLAIMS.STATUS_LIST]: StatusListCWTClaim;
|
|
954
|
+
/**
|
|
955
|
+
* Allow additional claims.
|
|
956
|
+
*/
|
|
957
|
+
[key: number]: unknown;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* COSE key in JWK-like format for elliptic curve keys.
|
|
961
|
+
*/
|
|
962
|
+
interface EC2Key {
|
|
963
|
+
/**
|
|
964
|
+
* Key type: EC2 for Elliptic Curve keys with x and y coordinates.
|
|
965
|
+
*/
|
|
966
|
+
kty: 'EC2';
|
|
967
|
+
/**
|
|
968
|
+
* Curve: P-256, P-384, or P-521.
|
|
969
|
+
*/
|
|
970
|
+
crv: 'P-256' | 'P-384' | 'P-521';
|
|
971
|
+
/**
|
|
972
|
+
* X coordinate (public key component).
|
|
973
|
+
*/
|
|
974
|
+
x: Uint8Array;
|
|
975
|
+
/**
|
|
976
|
+
* Y coordinate (public key component).
|
|
977
|
+
*/
|
|
978
|
+
y: Uint8Array;
|
|
979
|
+
/**
|
|
980
|
+
* Private key (d parameter).
|
|
981
|
+
* Only present for private keys.
|
|
982
|
+
*/
|
|
983
|
+
d?: Uint8Array;
|
|
984
|
+
/**
|
|
985
|
+
* Key ID.
|
|
986
|
+
*/
|
|
987
|
+
kid?: Uint8Array;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* COSE key for OKP (Octet Key Pair) keys like Ed25519.
|
|
991
|
+
*/
|
|
992
|
+
interface OKPKey {
|
|
993
|
+
/**
|
|
994
|
+
* Key type: OKP for Octet Key Pair.
|
|
995
|
+
*/
|
|
996
|
+
kty: 'OKP';
|
|
997
|
+
/**
|
|
998
|
+
* Curve: Ed25519 or Ed448.
|
|
999
|
+
*/
|
|
1000
|
+
crv: 'Ed25519' | 'Ed448';
|
|
1001
|
+
/**
|
|
1002
|
+
* Public key.
|
|
1003
|
+
*/
|
|
1004
|
+
x: Uint8Array;
|
|
1005
|
+
/**
|
|
1006
|
+
* Private key.
|
|
1007
|
+
* Only present for private keys.
|
|
1008
|
+
*/
|
|
1009
|
+
d?: Uint8Array;
|
|
1010
|
+
/**
|
|
1011
|
+
* Key ID.
|
|
1012
|
+
*/
|
|
1013
|
+
kid?: Uint8Array;
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* COSE key (either EC2 or OKP).
|
|
1017
|
+
*/
|
|
1018
|
+
type COSEKey = EC2Key | OKPKey;
|
|
1019
|
+
/**
|
|
1020
|
+
* Options for creating a CWT Status List.
|
|
1021
|
+
*/
|
|
1022
|
+
interface CreateCWTStatusListOptions {
|
|
1023
|
+
/**
|
|
1024
|
+
* OPTIONAL: Issuer identifier.
|
|
1025
|
+
*/
|
|
1026
|
+
iss?: string;
|
|
1027
|
+
/**
|
|
1028
|
+
* OPTIONAL: Status list identifier (URI).
|
|
1029
|
+
*/
|
|
1030
|
+
sub?: string;
|
|
1031
|
+
/**
|
|
1032
|
+
* REQUIRED: Compressed status list (raw bytes).
|
|
1033
|
+
*/
|
|
1034
|
+
lst: Uint8Array;
|
|
1035
|
+
/**
|
|
1036
|
+
* REQUIRED: Number of bits per status.
|
|
1037
|
+
*/
|
|
1038
|
+
bits: BitsPerStatus;
|
|
1039
|
+
/**
|
|
1040
|
+
* OPTIONAL: Issued at timestamp.
|
|
1041
|
+
* Defaults to current time if not provided.
|
|
1042
|
+
*/
|
|
1043
|
+
iat?: number;
|
|
1044
|
+
/**
|
|
1045
|
+
* OPTIONAL: Expiration timestamp.
|
|
1046
|
+
*/
|
|
1047
|
+
exp?: number;
|
|
1048
|
+
/**
|
|
1049
|
+
* OPTIONAL: Time to live in seconds.
|
|
1050
|
+
*/
|
|
1051
|
+
ttl?: number;
|
|
1052
|
+
/**
|
|
1053
|
+
* OPTIONAL: Aggregation URI.
|
|
1054
|
+
*/
|
|
1055
|
+
aggregation_uri?: string;
|
|
1056
|
+
/**
|
|
1057
|
+
* OPTIONAL: Additional claims (with numeric keys).
|
|
1058
|
+
*/
|
|
1059
|
+
additionalClaims?: Record<number, unknown>;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Options for signing a CWT with COSE Sign1.
|
|
1063
|
+
*/
|
|
1064
|
+
interface SignCWTOptions {
|
|
1065
|
+
/**
|
|
1066
|
+
* OPTIONAL: Algorithm to use for signing.
|
|
1067
|
+
* Defaults to ES256 (-7).
|
|
1068
|
+
*/
|
|
1069
|
+
alg?: number;
|
|
1070
|
+
/**
|
|
1071
|
+
* OPTIONAL: Key ID to include in the protected header.
|
|
1072
|
+
*/
|
|
1073
|
+
kid?: Uint8Array;
|
|
1074
|
+
/**
|
|
1075
|
+
* OPTIONAL: Additional protected header parameters.
|
|
1076
|
+
*/
|
|
1077
|
+
additionalHeaders?: Map<number, unknown>;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Result of parsing a CWT Status List Token.
|
|
1081
|
+
*/
|
|
1082
|
+
interface ParsedCWTStatusList {
|
|
1083
|
+
/**
|
|
1084
|
+
* Protected header.
|
|
1085
|
+
*/
|
|
1086
|
+
protectedHeader: Map<number, unknown>;
|
|
1087
|
+
/**
|
|
1088
|
+
* Unprotected header.
|
|
1089
|
+
*/
|
|
1090
|
+
unprotectedHeader: Map<number, unknown>;
|
|
1091
|
+
/**
|
|
1092
|
+
* CWT payload.
|
|
1093
|
+
*/
|
|
1094
|
+
payload: StatusListCWTPayload;
|
|
1095
|
+
/**
|
|
1096
|
+
* Raw CWT bytes.
|
|
1097
|
+
*/
|
|
1098
|
+
cwt: Uint8Array;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Status list reference in CBOR format (for mdoc).
|
|
1102
|
+
*/
|
|
1103
|
+
interface StatusListReferenceCBOR {
|
|
1104
|
+
/**
|
|
1105
|
+
* Index of this credential in the status list.
|
|
1106
|
+
*/
|
|
1107
|
+
idx: number;
|
|
1108
|
+
/**
|
|
1109
|
+
* URI of the Status List Token.
|
|
1110
|
+
*/
|
|
1111
|
+
uri: string;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* Creates a CWT Status List payload (CBOR map).
|
|
1116
|
+
*
|
|
1117
|
+
* This function creates the payload for a Status List CWT using numeric claim keys.
|
|
1118
|
+
* The payload can be signed separately using COSE Sign1.
|
|
1119
|
+
*
|
|
1120
|
+
* @param options - Configuration for creating the status list payload
|
|
1121
|
+
* @returns CBOR-encodable payload object
|
|
1122
|
+
*
|
|
1123
|
+
* @example
|
|
1124
|
+
* ```typescript
|
|
1125
|
+
* import { StatusList } from '@vess-id/status-list';
|
|
1126
|
+
* import { createCWTStatusListPayload } from '@vess-id/status-list/formats/cwt';
|
|
1127
|
+
*
|
|
1128
|
+
* const list = StatusList.create(1000, 1);
|
|
1129
|
+
* const lst = list.compressToBytes();
|
|
1130
|
+
*
|
|
1131
|
+
* const payload = createCWTStatusListPayload({
|
|
1132
|
+
* sub: 'https://issuer.example.com/status/1',
|
|
1133
|
+
* lst,
|
|
1134
|
+
* bits: 1,
|
|
1135
|
+
* iat: Math.floor(Date.now() / 1000),
|
|
1136
|
+
* exp: Math.floor(Date.now() / 1000) + 86400,
|
|
1137
|
+
* ttl: 3600,
|
|
1138
|
+
* });
|
|
1139
|
+
* ```
|
|
1140
|
+
*/
|
|
1141
|
+
declare function createCWTStatusListPayload(options: CreateCWTStatusListOptions): StatusListCWTPayload;
|
|
1142
|
+
/**
|
|
1143
|
+
* Encodes a CWT Status List payload to CBOR format (without signing).
|
|
1144
|
+
*
|
|
1145
|
+
* @param payload - CWT payload
|
|
1146
|
+
* @returns CBOR-encoded payload
|
|
1147
|
+
*
|
|
1148
|
+
* @example
|
|
1149
|
+
* ```typescript
|
|
1150
|
+
* const payload = createCWTStatusListPayload({ ... });
|
|
1151
|
+
* const cbor = encodeCWTPayload(payload);
|
|
1152
|
+
* ```
|
|
1153
|
+
*/
|
|
1154
|
+
declare function encodeCWTPayload(payload: StatusListCWTPayload): Uint8Array;
|
|
1155
|
+
/**
|
|
1156
|
+
* Parses a CWT Status List (CBOR-encoded, unsigned).
|
|
1157
|
+
*
|
|
1158
|
+
* This function decodes and validates the structure of an unsigned Status List CWT.
|
|
1159
|
+
* For signed CWTs (COSE Sign1), use `parseCWTStatusListSigned()` instead.
|
|
1160
|
+
*
|
|
1161
|
+
* @param cwtBytes - CBOR-encoded CWT bytes
|
|
1162
|
+
* @returns Parsed payload and decompressed status list
|
|
1163
|
+
* @throws {InvalidTokenFormatError} If the CWT format is invalid
|
|
1164
|
+
*
|
|
1165
|
+
* @example
|
|
1166
|
+
* ```typescript
|
|
1167
|
+
* import { parseCWTStatusList } from '@vess-id/status-list/formats/cwt';
|
|
1168
|
+
*
|
|
1169
|
+
* const { payload, statusList } = parseCWTStatusList(cwtBytes);
|
|
1170
|
+
*
|
|
1171
|
+
* // Check credential status
|
|
1172
|
+
* const status = statusList.getStatus(42);
|
|
1173
|
+
* ```
|
|
1174
|
+
*/
|
|
1175
|
+
declare function parseCWTStatusList(cwtBytes: Uint8Array): ParsedCWTStatusList & {
|
|
1176
|
+
statusList: StatusList;
|
|
1177
|
+
};
|
|
1178
|
+
/**
|
|
1179
|
+
* Parses a signed CWT Status List (COSE Sign1).
|
|
1180
|
+
*
|
|
1181
|
+
* This function verifies the COSE Sign1 signature and extracts the payload.
|
|
1182
|
+
*
|
|
1183
|
+
* @param cwtBytes - CBOR-encoded COSE Sign1 structure
|
|
1184
|
+
* @param publicKey - Public key for verification
|
|
1185
|
+
* @returns Parsed payload and decompressed status list
|
|
1186
|
+
* @throws {InvalidTokenFormatError} If verification fails
|
|
1187
|
+
*
|
|
1188
|
+
* @example
|
|
1189
|
+
* ```typescript
|
|
1190
|
+
* const publicKey = { kty: 'EC2', crv: 'P-256', x: ..., y: ... };
|
|
1191
|
+
* const { payload, statusList } = parseCWTStatusListSigned(cwtBytes, publicKey);
|
|
1192
|
+
* ```
|
|
1193
|
+
*/
|
|
1194
|
+
declare function parseCWTStatusListSigned(cwtBytes: Uint8Array, publicKey: COSEKey): ParsedCWTStatusList & {
|
|
1195
|
+
statusList: StatusList;
|
|
1196
|
+
};
|
|
1197
|
+
/**
|
|
1198
|
+
* Signs a CWT Status List using COSE Sign1.
|
|
1199
|
+
*
|
|
1200
|
+
* Note: This is a basic implementation. For production use with real cryptographic
|
|
1201
|
+
* keys, consider using a dedicated COSE library like `@transmute/cose`.
|
|
1202
|
+
*
|
|
1203
|
+
* @param payload - CWT payload
|
|
1204
|
+
* @param privateKey - COSE private key
|
|
1205
|
+
* @param options - Signing options
|
|
1206
|
+
* @returns CBOR-encoded COSE Sign1 structure
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* ```typescript
|
|
1210
|
+
* const payload = createCWTStatusListPayload({ ... });
|
|
1211
|
+
* const privateKey = { kty: 'EC2', crv: 'P-256', x: ..., y: ..., d: ... };
|
|
1212
|
+
*
|
|
1213
|
+
* const cwt = signCWTStatusList(payload, privateKey, {
|
|
1214
|
+
* alg: -7, // ES256
|
|
1215
|
+
* kid: new TextEncoder().encode('key-123'),
|
|
1216
|
+
* });
|
|
1217
|
+
* ```
|
|
1218
|
+
*/
|
|
1219
|
+
declare function signCWTStatusList(payload: StatusListCWTPayload, privateKey: COSEKey, options?: SignCWTOptions): Uint8Array;
|
|
1220
|
+
/**
|
|
1221
|
+
* Extracts the status list reference from an mdoc or CBOR-encoded credential.
|
|
1222
|
+
*
|
|
1223
|
+
* @param credentialCBOR - CBOR-encoded credential containing a status claim
|
|
1224
|
+
* @returns Status list reference (idx and uri)
|
|
1225
|
+
* @throws {InvalidTokenFormatError} If the status claim is missing or invalid
|
|
1226
|
+
*/
|
|
1227
|
+
declare function extractStatusListReferenceCBOR(credentialCBOR: Uint8Array): StatusListReferenceCBOR;
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* Creates a COSE Sign1 signature structure.
|
|
1231
|
+
*
|
|
1232
|
+
* COSE_Sign1 = [
|
|
1233
|
+
* protected: bstr, // Serialized protected header
|
|
1234
|
+
* unprotected: {}, // Unprotected header map
|
|
1235
|
+
* payload: bstr, // The payload
|
|
1236
|
+
* signature: bstr // The signature
|
|
1237
|
+
* ]
|
|
1238
|
+
*
|
|
1239
|
+
* @param payload - Payload to sign (CBOR-encoded)
|
|
1240
|
+
* @param protectedHeader - Protected header parameters
|
|
1241
|
+
* @param privateKey - COSE private key
|
|
1242
|
+
* @returns CBOR-encoded COSE Sign1 structure with tag
|
|
1243
|
+
*/
|
|
1244
|
+
declare function signCOSE(payload: Uint8Array, protectedHeader: Map<number, unknown>, privateKey: COSEKey): Uint8Array;
|
|
1245
|
+
/**
|
|
1246
|
+
* Verifies a COSE Sign1 signature.
|
|
1247
|
+
*
|
|
1248
|
+
* @param coseSign1 - CBOR-encoded COSE Sign1 structure
|
|
1249
|
+
* @param publicKey - COSE public key
|
|
1250
|
+
* @returns Verified payload
|
|
1251
|
+
* @throws {InvalidTokenFormatError} If verification fails
|
|
1252
|
+
*/
|
|
1253
|
+
declare function verifyCOSE(coseSign1: Uint8Array, publicKey: COSEKey): Uint8Array;
|
|
1254
|
+
|
|
1255
|
+
/**
|
|
1256
|
+
* Options for fetching a Status List Token.
|
|
1257
|
+
*/
|
|
1258
|
+
interface FetchOptions {
|
|
1259
|
+
/**
|
|
1260
|
+
* Request timeout in milliseconds.
|
|
1261
|
+
* Default: 10000 (10 seconds)
|
|
1262
|
+
*/
|
|
1263
|
+
timeout?: number;
|
|
1264
|
+
/**
|
|
1265
|
+
* Additional HTTP headers to include in the request.
|
|
1266
|
+
*/
|
|
1267
|
+
headers?: Record<string, string>;
|
|
1268
|
+
/**
|
|
1269
|
+
* Maximum number of redirects to follow.
|
|
1270
|
+
* Per IETF spec Section 11.4, limit redirects to prevent DoS attacks.
|
|
1271
|
+
* Default: 3
|
|
1272
|
+
*/
|
|
1273
|
+
maxRedirects?: number;
|
|
1274
|
+
/**
|
|
1275
|
+
* Custom fetch implementation.
|
|
1276
|
+
* Useful for testing or custom environments.
|
|
1277
|
+
* Default: global fetch
|
|
1278
|
+
*/
|
|
1279
|
+
fetchImpl?: typeof fetch;
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Fetches a Status List Token from a URI via HTTP GET.
|
|
1283
|
+
*
|
|
1284
|
+
* Automatically detects JWT vs CWT format based on Content-Type header.
|
|
1285
|
+
* Follows IETF spec security recommendations:
|
|
1286
|
+
* - Enforces timeout to prevent DoS
|
|
1287
|
+
* - Limits redirects (default: 3)
|
|
1288
|
+
* - Validates Content-Type header
|
|
1289
|
+
*
|
|
1290
|
+
* @param uri - URI of the Status List Token
|
|
1291
|
+
* @param options - Fetch options
|
|
1292
|
+
* @returns JWT as string or CWT as Uint8Array
|
|
1293
|
+
* @throws {MissingStatusListUriError} If URI is missing or invalid
|
|
1294
|
+
* @throws {FetchError} If HTTP request fails
|
|
1295
|
+
*
|
|
1296
|
+
* @example
|
|
1297
|
+
* ```typescript
|
|
1298
|
+
* // Fetch JWT Status List
|
|
1299
|
+
* const jwt = await fetchStatusListToken('https://issuer.example.com/status/1');
|
|
1300
|
+
* console.log(typeof jwt); // 'string'
|
|
1301
|
+
*
|
|
1302
|
+
* // Fetch CWT Status List
|
|
1303
|
+
* const cwt = await fetchStatusListToken('https://issuer.example.com/status/2');
|
|
1304
|
+
* console.log(cwt instanceof Uint8Array); // true
|
|
1305
|
+
* ```
|
|
1306
|
+
*/
|
|
1307
|
+
declare function fetchStatusListToken(uri: string, options?: FetchOptions): Promise<string | Uint8Array>;
|
|
1308
|
+
/**
|
|
1309
|
+
* Validates a status list URI.
|
|
1310
|
+
*
|
|
1311
|
+
* Checks:
|
|
1312
|
+
* - URI is not empty
|
|
1313
|
+
* - URI is a valid URL
|
|
1314
|
+
* - Protocol is HTTP or HTTPS
|
|
1315
|
+
*
|
|
1316
|
+
* @param uri - URI to validate
|
|
1317
|
+
* @returns True if valid
|
|
1318
|
+
*
|
|
1319
|
+
* @example
|
|
1320
|
+
* ```typescript
|
|
1321
|
+
* if (!isValidStatusListUri(uri)) {
|
|
1322
|
+
* throw new Error('Invalid status list URI');
|
|
1323
|
+
* }
|
|
1324
|
+
* ```
|
|
1325
|
+
*/
|
|
1326
|
+
declare function isValidStatusListUri(uri: string): boolean;
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* High-level helper for working with Status List Tokens.
|
|
1330
|
+
*
|
|
1331
|
+
* Provides a unified interface for both JWT and CWT formats.
|
|
1332
|
+
* Similar to the Python implementation's StatusListTokenHelper.
|
|
1333
|
+
*
|
|
1334
|
+
* @example
|
|
1335
|
+
* ```typescript
|
|
1336
|
+
* // From a credential's status reference
|
|
1337
|
+
* const reference = { idx: 42, uri: 'https://issuer.example.com/status/1' };
|
|
1338
|
+
* const helper = await StatusListTokenHelper.fromStatusReference(reference);
|
|
1339
|
+
*
|
|
1340
|
+
* // Check credential status
|
|
1341
|
+
* if (helper.isExpired()) {
|
|
1342
|
+
* throw new Error('Status list expired');
|
|
1343
|
+
* }
|
|
1344
|
+
*
|
|
1345
|
+
* const status = helper.getStatus(reference.idx);
|
|
1346
|
+
* if (status === StandardStatusValues.INVALID) {
|
|
1347
|
+
* console.log('Credential is revoked');
|
|
1348
|
+
* }
|
|
1349
|
+
* ```
|
|
1350
|
+
*/
|
|
1351
|
+
declare class StatusListTokenHelper {
|
|
1352
|
+
private readonly header;
|
|
1353
|
+
private readonly payload;
|
|
1354
|
+
private readonly statusList;
|
|
1355
|
+
private readonly format;
|
|
1356
|
+
private constructor();
|
|
1357
|
+
/**
|
|
1358
|
+
* Creates a helper from a Status List Token (JWT or CWT).
|
|
1359
|
+
*
|
|
1360
|
+
* Automatically detects the format:
|
|
1361
|
+
* - JWT: starts with "eyJ" (base64url-encoded header)
|
|
1362
|
+
* - CWT: CBOR binary format
|
|
1363
|
+
*
|
|
1364
|
+
* @param token - JWT string or CWT Uint8Array
|
|
1365
|
+
* @returns StatusListTokenHelper instance
|
|
1366
|
+
* @throws {InvalidTokenFormatError} If token format is invalid
|
|
1367
|
+
*
|
|
1368
|
+
* @example
|
|
1369
|
+
* ```typescript
|
|
1370
|
+
* // From JWT
|
|
1371
|
+
* const helper = StatusListTokenHelper.fromToken(jwtString);
|
|
1372
|
+
*
|
|
1373
|
+
* // From CWT
|
|
1374
|
+
* const helper = StatusListTokenHelper.fromToken(cwtBytes);
|
|
1375
|
+
* ```
|
|
1376
|
+
*/
|
|
1377
|
+
static fromToken(token: string | Uint8Array): StatusListTokenHelper;
|
|
1378
|
+
/**
|
|
1379
|
+
* Creates a helper by fetching a Status List Token from a URI.
|
|
1380
|
+
*
|
|
1381
|
+
* Fetches the token via HTTP GET and automatically detects the format.
|
|
1382
|
+
*
|
|
1383
|
+
* @param reference - Status reference containing idx and uri
|
|
1384
|
+
* @param options - Fetch options
|
|
1385
|
+
* @returns StatusListTokenHelper instance
|
|
1386
|
+
* @throws {FetchError} If HTTP request fails
|
|
1387
|
+
* @throws {InvalidTokenFormatError} If token format is invalid
|
|
1388
|
+
*
|
|
1389
|
+
* @example
|
|
1390
|
+
* ```typescript
|
|
1391
|
+
* const reference = {
|
|
1392
|
+
* idx: 42,
|
|
1393
|
+
* uri: 'https://issuer.example.com/status/1'
|
|
1394
|
+
* };
|
|
1395
|
+
*
|
|
1396
|
+
* const helper = await StatusListTokenHelper.fromStatusReference(reference);
|
|
1397
|
+
* const status = helper.getStatus(reference.idx);
|
|
1398
|
+
* ```
|
|
1399
|
+
*/
|
|
1400
|
+
static fromStatusReference(reference: StatusListReference, options?: FetchOptions): Promise<StatusListTokenHelper>;
|
|
1401
|
+
/**
|
|
1402
|
+
* Gets the status value at a specific index.
|
|
1403
|
+
*
|
|
1404
|
+
* @param index - Index in the status list
|
|
1405
|
+
* @returns Status value (0-255 depending on bits per status)
|
|
1406
|
+
* @throws {IndexOutOfBoundsError} If index is out of bounds
|
|
1407
|
+
*
|
|
1408
|
+
* @example
|
|
1409
|
+
* ```typescript
|
|
1410
|
+
* const status = helper.getStatus(42);
|
|
1411
|
+
* if (status === StandardStatusValues.INVALID) {
|
|
1412
|
+
* console.log('Credential 42 is revoked');
|
|
1413
|
+
* }
|
|
1414
|
+
* ```
|
|
1415
|
+
*/
|
|
1416
|
+
getStatus(index: number): StatusValue;
|
|
1417
|
+
/**
|
|
1418
|
+
* Checks if the status list token is expired.
|
|
1419
|
+
*
|
|
1420
|
+
* A token is expired if:
|
|
1421
|
+
* - exp is set and is in the past, OR
|
|
1422
|
+
* - ttl is set and iat + ttl is in the past
|
|
1423
|
+
*
|
|
1424
|
+
* @param currentTime - Current time (seconds since epoch). Defaults to now.
|
|
1425
|
+
* @returns True if expired
|
|
1426
|
+
*
|
|
1427
|
+
* @example
|
|
1428
|
+
* ```typescript
|
|
1429
|
+
* if (helper.isExpired()) {
|
|
1430
|
+
* throw new Error('Status list expired - fetch a new one');
|
|
1431
|
+
* }
|
|
1432
|
+
* ```
|
|
1433
|
+
*/
|
|
1434
|
+
isExpired(currentTime?: number): boolean;
|
|
1435
|
+
/**
|
|
1436
|
+
* Gets the issuer identifier (iss claim).
|
|
1437
|
+
*
|
|
1438
|
+
* @returns Issuer string or undefined if not set
|
|
1439
|
+
*/
|
|
1440
|
+
get iss(): string | undefined;
|
|
1441
|
+
/**
|
|
1442
|
+
* Gets the subject identifier (sub claim).
|
|
1443
|
+
*
|
|
1444
|
+
* @returns Subject string or undefined if not set
|
|
1445
|
+
*/
|
|
1446
|
+
get sub(): string | undefined;
|
|
1447
|
+
/**
|
|
1448
|
+
* Gets the issued at time (iat claim).
|
|
1449
|
+
*
|
|
1450
|
+
* @returns Timestamp in seconds since epoch, or undefined if not set
|
|
1451
|
+
*/
|
|
1452
|
+
get iat(): number | undefined;
|
|
1453
|
+
/**
|
|
1454
|
+
* Gets the expiration time (exp claim).
|
|
1455
|
+
*
|
|
1456
|
+
* @returns Timestamp in seconds since epoch, or undefined if not set
|
|
1457
|
+
*/
|
|
1458
|
+
get exp(): number | undefined;
|
|
1459
|
+
/**
|
|
1460
|
+
* Gets the time to live (ttl claim).
|
|
1461
|
+
*
|
|
1462
|
+
* @returns TTL in seconds, or undefined if not set
|
|
1463
|
+
*/
|
|
1464
|
+
get ttl(): number | undefined;
|
|
1465
|
+
/**
|
|
1466
|
+
* Gets the number of bits per status entry.
|
|
1467
|
+
*
|
|
1468
|
+
* @returns Bits per status (1, 2, 4, or 8)
|
|
1469
|
+
*/
|
|
1470
|
+
get bits(): BitsPerStatus;
|
|
1471
|
+
/**
|
|
1472
|
+
* Gets the aggregation URI if set.
|
|
1473
|
+
*
|
|
1474
|
+
* @returns Aggregation URI or undefined if not set
|
|
1475
|
+
*/
|
|
1476
|
+
get aggregationUri(): string | undefined;
|
|
1477
|
+
/**
|
|
1478
|
+
* Gets the format of the token.
|
|
1479
|
+
*
|
|
1480
|
+
* @returns 'jwt' or 'cwt'
|
|
1481
|
+
*/
|
|
1482
|
+
get tokenFormat(): 'jwt' | 'cwt';
|
|
1483
|
+
/**
|
|
1484
|
+
* Gets the size of the status list.
|
|
1485
|
+
*
|
|
1486
|
+
* @returns Number of status entries
|
|
1487
|
+
*/
|
|
1488
|
+
get size(): number;
|
|
1489
|
+
/**
|
|
1490
|
+
* Gets the raw header.
|
|
1491
|
+
*
|
|
1492
|
+
* @returns Header as object (JWT) or Map (CWT)
|
|
1493
|
+
*/
|
|
1494
|
+
getHeader(): Record<string, unknown> | Map<number, unknown>;
|
|
1495
|
+
/**
|
|
1496
|
+
* Gets the raw payload.
|
|
1497
|
+
*
|
|
1498
|
+
* @returns Payload object
|
|
1499
|
+
*/
|
|
1500
|
+
getPayload(): StatusListJWTPayload | StatusListCWTPayload;
|
|
1501
|
+
/**
|
|
1502
|
+
* Gets the underlying StatusList instance.
|
|
1503
|
+
*
|
|
1504
|
+
* @returns StatusList instance
|
|
1505
|
+
*/
|
|
1506
|
+
getStatusList(): StatusList;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* Validation result with detailed error messages.
|
|
1511
|
+
*/
|
|
1512
|
+
interface ValidationResult {
|
|
1513
|
+
/**
|
|
1514
|
+
* Whether the validation passed.
|
|
1515
|
+
*/
|
|
1516
|
+
valid: boolean;
|
|
1517
|
+
/**
|
|
1518
|
+
* List of validation error messages.
|
|
1519
|
+
* Empty if valid is true.
|
|
1520
|
+
*/
|
|
1521
|
+
errors: string[];
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Validates a JWT Status List payload.
|
|
1525
|
+
*
|
|
1526
|
+
* Checks:
|
|
1527
|
+
* - Required claims (iss, sub, iat, status_list)
|
|
1528
|
+
* - status_list structure (bits, lst)
|
|
1529
|
+
* - bits value (must be 1, 2, 4, or 8)
|
|
1530
|
+
* - lst format (must be a non-empty string)
|
|
1531
|
+
* - Optional claims format (exp, ttl)
|
|
1532
|
+
*
|
|
1533
|
+
* @param payload - JWT payload to validate
|
|
1534
|
+
* @returns Validation result
|
|
1535
|
+
*
|
|
1536
|
+
* @example
|
|
1537
|
+
* ```typescript
|
|
1538
|
+
* const result = validateJWTPayload(payload);
|
|
1539
|
+
* if (!result.valid) {
|
|
1540
|
+
* console.error('Validation errors:', result.errors);
|
|
1541
|
+
* }
|
|
1542
|
+
* ```
|
|
1543
|
+
*/
|
|
1544
|
+
declare function validateJWTPayload(payload: StatusListJWTPayload): ValidationResult;
|
|
1545
|
+
/**
|
|
1546
|
+
* Validates a CWT Status List payload.
|
|
1547
|
+
*
|
|
1548
|
+
* Checks:
|
|
1549
|
+
* - Required claims (status_list with numeric key 65533)
|
|
1550
|
+
* - status_list structure (bits, lst)
|
|
1551
|
+
* - bits value (must be 1, 2, 4, or 8)
|
|
1552
|
+
* - lst format (must be a Uint8Array)
|
|
1553
|
+
* - Optional claims format (iss, sub, iat, exp, ttl)
|
|
1554
|
+
*
|
|
1555
|
+
* @param payload - CWT payload to validate
|
|
1556
|
+
* @returns Validation result
|
|
1557
|
+
*
|
|
1558
|
+
* @example
|
|
1559
|
+
* ```typescript
|
|
1560
|
+
* const result = validateCWTPayload(payload);
|
|
1561
|
+
* if (!result.valid) {
|
|
1562
|
+
* console.error('Validation errors:', result.errors);
|
|
1563
|
+
* }
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
declare function validateCWTPayload(payload: StatusListCWTPayload): ValidationResult;
|
|
1567
|
+
/**
|
|
1568
|
+
* Validates expiration and freshness of a status list token.
|
|
1569
|
+
*
|
|
1570
|
+
* Checks:
|
|
1571
|
+
* - If exp is set, validates it's not in the past
|
|
1572
|
+
* - If ttl is set, validates freshness based on iat + ttl
|
|
1573
|
+
* - Ensures iat is not in the future
|
|
1574
|
+
*
|
|
1575
|
+
* @param exp - Expiration time (seconds since epoch)
|
|
1576
|
+
* @param iat - Issued at time (seconds since epoch)
|
|
1577
|
+
* @param ttl - Time to live in seconds
|
|
1578
|
+
* @param currentTime - Current time (seconds since epoch). Defaults to now.
|
|
1579
|
+
* @returns Validation result
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* const result = validateExpiry(payload.exp, payload.iat, payload.ttl);
|
|
1584
|
+
* if (!result.valid) {
|
|
1585
|
+
* console.error('Token expired or not yet valid');
|
|
1586
|
+
* }
|
|
1587
|
+
* ```
|
|
1588
|
+
*/
|
|
1589
|
+
declare function validateExpiry(exp?: number, iat?: number, ttl?: number, currentTime?: number): ValidationResult;
|
|
1590
|
+
/**
|
|
1591
|
+
* Validates TTL value is within recommended bounds.
|
|
1592
|
+
*
|
|
1593
|
+
* Per IETF spec Section 11.5, excessively large TTL values can be used
|
|
1594
|
+
* for DoS attacks by causing verifiers to cache stale status lists.
|
|
1595
|
+
*
|
|
1596
|
+
* Recommended bounds:
|
|
1597
|
+
* - Minimum: 60 seconds (1 minute)
|
|
1598
|
+
* - Maximum: 31536000 seconds (1 year)
|
|
1599
|
+
*
|
|
1600
|
+
* @param ttl - Time to live in seconds
|
|
1601
|
+
* @returns Validation result
|
|
1602
|
+
*
|
|
1603
|
+
* @example
|
|
1604
|
+
* ```typescript
|
|
1605
|
+
* const result = validateTTLBounds(payload.ttl);
|
|
1606
|
+
* if (!result.valid) {
|
|
1607
|
+
* console.warn('TTL outside recommended bounds:', result.errors);
|
|
1608
|
+
* }
|
|
1609
|
+
* ```
|
|
1610
|
+
*/
|
|
1611
|
+
declare function validateTTLBounds(ttl?: number): ValidationResult;
|
|
1612
|
+
/**
|
|
1613
|
+
* Checks if a status list token is expired.
|
|
1614
|
+
*
|
|
1615
|
+
* A token is considered expired if:
|
|
1616
|
+
* - exp is set and is in the past, OR
|
|
1617
|
+
* - ttl is set and iat + ttl is in the past
|
|
1618
|
+
*
|
|
1619
|
+
* @param exp - Expiration time (seconds since epoch)
|
|
1620
|
+
* @param iat - Issued at time (seconds since epoch)
|
|
1621
|
+
* @param ttl - Time to live in seconds
|
|
1622
|
+
* @param currentTime - Current time (seconds since epoch). Defaults to now.
|
|
1623
|
+
* @returns True if expired
|
|
1624
|
+
*
|
|
1625
|
+
* @example
|
|
1626
|
+
* ```typescript
|
|
1627
|
+
* if (isExpired(payload.exp, payload.iat, payload.ttl)) {
|
|
1628
|
+
* throw new Error('Status list expired');
|
|
1629
|
+
* }
|
|
1630
|
+
* ```
|
|
1631
|
+
*/
|
|
1632
|
+
declare function isExpired(exp?: number, iat?: number, ttl?: number, currentTime?: number): boolean;
|
|
1633
|
+
|
|
1634
|
+
export { type BitsPerStatus, type COSEKey, COSE_ALGORITHMS, COSE_HEADERS, CWT_CLAIMS, CompressionError, type CreateCWTStatusListOptions, type CreateJWTStatusListOptions, type EC2Key, FetchError, type FetchOptions, IndexOutOfBoundsError, InvalidBitSizeError, InvalidStatusValueError, InvalidTokenFormatError, MissingStatusListUriError, type OKPKey, type ParsedCWTStatusList, type ParsedJWTStatusList, type SignCWTOptions, type SignJWTOptions, StandardStatusValues, type StatusFormat, StatusList, type StatusListCWTClaim, type StatusListCWTPayload, type StatusListClaim, type StatusListConfig, StatusListError, StatusListExpiredError, type StatusListJWTHeader, type StatusListJWTPayload, type StatusListReference, type StatusListReferenceCBOR, StatusListTokenHelper, type StatusValue, type TokenStatusClaim, ValidationError, type ValidationResult, type VerifiedStatusListJWT, type VerifyJWTOptions, calculateCapacity, compress, compressToBase64URL, createCWTStatusListPayload, createJWTStatusListPayload, decompress, decompressFromBase64URL, encodeCWTPayload, extractStatusListReference, extractStatusListReferenceCBOR, fetchStatusListToken, getBitValue, isExpired, isValidStatusListUri, packBits, parseCWTStatusList, parseCWTStatusListSigned, parseJWTStatusList, setBitValue, signCOSE, signCWTStatusList, signStatusListJWT, unpackBits, validateCWTPayload, validateExpiry, validateJWTPayload, validateTTLBounds, verifyCOSE, verifySignatureOnly, verifyStatusListJWT };
|