unjwt 0.4.0 → 0.5.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 (49) hide show
  1. package/README.md +360 -190
  2. package/dist/index.d.mts +6 -5
  3. package/dist/index.d.ts +6 -5
  4. package/dist/index.mjs +5 -5
  5. package/dist/jwe.d.mts +3 -3
  6. package/dist/jwe.d.ts +3 -3
  7. package/dist/jwe.mjs +4 -3
  8. package/dist/jwk.d.mts +2 -2
  9. package/dist/jwk.d.ts +2 -2
  10. package/dist/jwk.mjs +2 -2
  11. package/dist/jws.d.mts +3 -3
  12. package/dist/jws.d.ts +3 -3
  13. package/dist/jws.mjs +4 -4
  14. package/dist/shared/unjwt.B4e_rwol.mjs +453 -0
  15. package/dist/shared/unjwt.B9uGngq-.d.mts +154 -0
  16. package/dist/shared/unjwt.BKG_8Y9G.d.ts +189 -0
  17. package/dist/shared/unjwt.BOEZHxi_.d.ts +154 -0
  18. package/dist/shared/unjwt.Bzcov22m.mjs +167 -0
  19. package/dist/shared/unjwt.C0bBN0AC.d.mts +200 -0
  20. package/dist/shared/unjwt.C0bBN0AC.d.ts +200 -0
  21. package/dist/shared/unjwt.CJ_ohCFC.d.mts +46 -0
  22. package/dist/shared/unjwt.CjHZv3F3.mjs +57 -0
  23. package/dist/shared/unjwt.D90BbcE1.d.mts +52 -0
  24. package/dist/shared/unjwt.DKq5VF7t.mjs +1186 -0
  25. package/dist/shared/unjwt.DYFIh1YE.d.mts +100 -0
  26. package/dist/shared/unjwt.DoVzOnty.d.ts +52 -0
  27. package/dist/shared/unjwt.Dz5Oe9BG.d.mts +189 -0
  28. package/dist/shared/unjwt.mc4AEAT7.d.ts +46 -0
  29. package/dist/shared/unjwt.ppm5Y0k7.d.ts +100 -0
  30. package/dist/shared/unjwt.sux3_Z19.mjs +101 -0
  31. package/dist/utils.d.mts +2 -0
  32. package/dist/utils.d.ts +2 -0
  33. package/dist/utils.mjs +1 -0
  34. package/package.json +8 -4
  35. package/dist/shared/unjwt.4uzJv0cA.d.mts +0 -110
  36. package/dist/shared/unjwt.A7HKFx3n.d.mts +0 -52
  37. package/dist/shared/unjwt.BdjvpIUY.d.mts +0 -79
  38. package/dist/shared/unjwt.BmjmfFWo.d.ts +0 -79
  39. package/dist/shared/unjwt.C9ELsKeK.mjs +0 -120
  40. package/dist/shared/unjwt.CVUxLNoy.d.ts +0 -52
  41. package/dist/shared/unjwt.ChP7VhqI.d.ts +0 -110
  42. package/dist/shared/unjwt.DD0p18Gd.mjs +0 -81
  43. package/dist/shared/unjwt.DGsIvgyn.d.ts +0 -106
  44. package/dist/shared/unjwt.DPTwlVpK.mjs +0 -28
  45. package/dist/shared/unjwt.Dr-mxtq9.d.mts +0 -106
  46. package/dist/shared/unjwt.KOd0vXyx.mjs +0 -90
  47. package/dist/shared/unjwt._GivNhs2.mjs +0 -213
  48. package/dist/shared/unjwt.zb2wBNCJ.d.mts +0 -82
  49. package/dist/shared/unjwt.zb2wBNCJ.d.ts +0 -82
package/README.md CHANGED
@@ -7,11 +7,11 @@
7
7
 
8
8
  <!-- /automd -->
9
9
 
10
- Low-level JWT utilities using the Web Crypto API. Currently supports:
10
+ A collection of low-level JWT ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)) utilities using the Web Crypto API. Supports:
11
11
 
12
- - JWE (JSON Web Encryption) with password-based key derivation (PBES2).
13
- - JWS (JSON Web Signature) with symmetric keys (HMAC).
14
- - JWK (JSON Web Key) generation, import, and export for symmetric keys (`oct`).
12
+ - **JWS (JSON Web Signature, [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515))**: sign and verify tokens using HMAC, RSA (RSASSA-PKCS1-v1_5 & RSA-PSS), and ECDSA algorithms.
13
+ - **JWE (JSON Web Encryption, [RFC 7516](https://datatracker.ietf.org/doc/html/rfc7516))**: encrypt and decrypt data using various key management algorithms (AES Key Wrap, AES-GCM Key Wrap, RSA-OAEP, PBES2, ECDH-ES) and content encryption algorithms (AES-GCM, AES-CBC HMAC-SHA2).
14
+ - **JWK (JSON Web Key, [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517))**: generate, import, export, wrap, and unwrap keys in JWK format or as `CryptoKey` objects.
15
15
 
16
16
  ## Usage
17
17
 
@@ -28,308 +28,477 @@ Import:
28
28
 
29
29
  ```js
30
30
  import { jws, jwe, jwk } from "unjwt";
31
- import { seal, unseal } from "unjwt/jwe";
31
+ // JWS functions
32
32
  import { sign, verify } from "unjwt/jws";
33
- import { generateKey, exportKey, importKey } from "unjwt/jwk";
33
+ // JWE functions
34
+ import { encrypt, decrypt } from "unjwt/jwe";
35
+ // JWK functions
36
+ import {
37
+ generateKey,
38
+ deriveKeyFromPassword,
39
+ importKey,
40
+ exportKey,
41
+ wrapKey,
42
+ unwrapKey,
43
+ } from "unjwt/jwk";
44
+ // Utility functions
45
+ import { base64UrlEncode, base64UrlDecode, randomBytes } from "unjwt/utils";
34
46
  ```
35
47
 
36
48
  **CDN** (Deno, Bun and Browsers)
37
49
 
38
50
  ```js
51
+ // JWS functions
39
52
  import { jws, jwe, jwk } from "https://esm.sh/unjwt";
40
- import { seal, unseal } from "https://esm.sh/unjwt/jwe";
41
53
  import { sign, verify } from "https://esm.sh/unjwt/jws";
42
- import { generateKey, exportKey, importKey } from "https://esm.sh/unjwt/jwk";
54
+ // JWE functions
55
+ import { encrypt, decrypt } from "https://esm.sh/unjwt/jwe";
56
+ // JWK functions
57
+ import {
58
+ generateKey,
59
+ deriveKeyFromPassword,
60
+ importKey,
61
+ exportKey,
62
+ wrapKey,
63
+ unwrapKey,
64
+ } from "https://esm.sh/unjwt/jwk";
65
+ // Utility functions
66
+ import {
67
+ base64UrlEncode,
68
+ base64UrlDecode,
69
+ randomBytes,
70
+ } from "https://esm.sh/unjwt/utils";
43
71
  ```
44
72
 
45
- ### JWE (JSON Web Encryption)
73
+ ### JWS (JSON Web Signature, [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515))
46
74
 
47
- This library provides functions to encrypt (seal) and decrypt (unseal) data according to the JWE specification using password-based encryption.
75
+ Functions to sign and verify data according to the JWS specification.
48
76
 
49
- #### `seal(data, password, options?)`
77
+ #### `sign(payload, key, options)`
50
78
 
51
- Encrypts the provided data using a password.
79
+ Creates a JWS token.
52
80
 
53
- - `data`: The data to encrypt (string or `Uint8Array`).
54
- - `password`: The password to use for encryption (string or `Uint8Array`).
55
- - `options` (optional):
56
- - `iterations`: Number of PBKDF2 iterations (default: `2048`).
57
- - `saltSize`: Size of the random salt in bytes (default: `16`).
58
- - `protectedHeader`: An object containing JWE header parameters to include.
59
- - `alg`: Key Wrapping Algorithm (default: `PBES2-HS256+A128KW`). Supported: `PBES2-HS256+A128KW`, `PBES2-HS384+A192KW`, `PBES2-HS512+A256KW`.
60
- - `enc`: Content Encryption Algorithm (default: `A256GCM`). Supported: `A128GCM`, `A192GCM`, `A256GCM`, `A128CBC-HS256`, `A192CBC-HS384`, `A256CBC-HS512`.
61
- - Other standard JWE or custom header parameters can be added here.
81
+ - `payload`: The data to sign (`string`, `Uint8Array`, or a JSON-serializable `object`).
82
+ - `key`: The signing key (`CryptoKey`, `JWK`, or `Uint8Array` for symmetric keys).
83
+ - `options`:
84
+ - `alg`: (Required) The JWS algorithm (e.g., `"HS256"`, `"RS256"`, `"ES256"`, `"PS256"`).
85
+ - `protectedHeader`: An object for additional JWS Protected Header parameters (e.g., `kid`, `typ`, `cty`, `crit`, `b64`). If `payload` is an object and `typ` is not set, it defaults to `"JWT"`. The `b64` parameter ([RFC7797 section-3](https://datatracker.ietf.org/doc/html/rfc7797#section-3)) controls payload encoding (defaults to `true`, meaning Base64URL encoded).
62
86
 
63
- Returns a Promise resolving to the JWE token string in Compact Serialization format.
87
+ Returns a `Promise<string>` resolving to the JWS token in Compact Serialization format.
64
88
 
65
- **Example:**
89
+ **Example (HS256 with string secret):**
66
90
 
67
91
  ```ts
68
- import { seal } from "unjwt/jwe";
92
+ import { sign } from "unjwt/jws";
69
93
 
70
- const plaintext = "My secret data";
71
- const password = "strongpassword";
94
+ const payload = { message: "My important data" }; // Object payload
95
+ const secret = "supersecretkey"; // String secret, will be imported (length depends on choosen alg)
72
96
 
73
- const token = await seal(plaintext, password, {
74
- protectedHeader: {
75
- alg: "PBES2-HS512+A256KW",
76
- enc: "A256GCM",
77
- },
78
- });
97
+ const token = await sign(payload, secret, { alg: "HS256" });
79
98
 
80
99
  console.log(token);
81
- // eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJzIjoi...
100
+ // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
82
101
  ```
83
102
 
84
- #### `unseal(token, password, options?)`
103
+ **Example (RS256 with CryptoKey):**
104
+
105
+ ```ts
106
+ import { sign } from "unjwt/jws";
107
+ import { generateKey } from "unjwt/jwk";
85
108
 
86
- Decrypts a JWE token using a password.
109
+ const payload = { userId: 123, permissions: ["read"] };
110
+ const { privateKey } = await generateKey("RS256"); // Generates a CryptoKeyPair
87
111
 
88
- - `token`: The JWE token string (Compact Serialization).
89
- - `password`: The password used for encryption (string or `Uint8Array`).
112
+ const token = await sign(payload, privateKey, { alg: "RS256" });
113
+
114
+ console.log(token);
115
+ // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
116
+ ```
117
+
118
+ #### `verify(jws, key, options?)`
119
+
120
+ Verifies a JWS token.
121
+
122
+ - `jws`: The JWS token string.
123
+ - `key`: The verification key (`CryptoKey`, `JWK`, `Uint8Array`, or a `KeyLookupFunction`).
124
+ A `KeyLookupFunction` has the signature `(header: JWSProtectedHeader) => Promise<CryptoKey | JWK | Uint8Array> | CryptoKey | JWK | Uint8Array`.
90
125
  - `options` (optional):
91
- - `textOutput`: If `false`, returns the decrypted data as a `Uint8Array` instead of a string (default: `true`).
126
+ - `algorithms`: An array of allowed JWS `alg` values. If not provided, the `alg` from the JWS header is used.
127
+ - `critical`: An array of JWS header parameter names that the application understands and processes.
92
128
 
93
- Returns a Promise resolving to the decrypted data (string or `Uint8Array`).
129
+ Returns a `Promise<JWSVerifyResult<T>>` which is an object `{ payload: T, protectedHeader: JWSProtectedHeader }`.
130
+ The `payload` type `T` can be `JWTClaims` (object), `string`, or `Uint8Array` depending on the JWS content and headers.
94
131
 
95
- **Example:**
132
+ **Example (HS256):**
96
133
 
97
134
  ```ts
98
- import { unseal } from "unjwt/jwe";
135
+ import { verify } from "unjwt/jws";
99
136
 
100
- const token =
101
- "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJzIjoi..."; // From seal example
102
- const password = "strongpassword";
137
+ const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // From HS256 sign example
138
+ const secret = "supersecretkey";
103
139
 
104
- // Decrypt as string (default)
105
- const decryptedString = await unseal(token, password);
106
- console.log(decryptedString); // "My secret data"
140
+ const { payload, protectedHeader } = await verify(token, secret);
141
+ console.log(payload); // { message: "My important data" }
142
+ console.log(protectedHeader); // { alg: "HS256", typ: "JWT" }
143
+ ```
144
+
145
+ **Example (RS256 with key lookup):**
107
146
 
108
- // Decrypt as Uint8Array
109
- const decryptedBytes = await unseal(token, password, { textOutput: false });
110
- console.log(decryptedBytes); // Uint8Array [ 77, 121, 32, ... ]
147
+ ```ts
148
+ import { verify } from "unjwt/jws";
149
+ import { generateKey } from "unjwt/jwk";
150
+
151
+ const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."; // From RS256 sign example
152
+
153
+ // Example: using a key lookup function
154
+ const { publicKey: rsaPublicKey } = await generateKey("RS256"); // For example purposes, assume publicKey is stored/fetched during lookup
155
+
156
+ const keyLookup = async (header) => {
157
+ if (header.alg === "RS256" /* && header.kid === 'expected-kid' */) {
158
+ return rsaPublicKey;
159
+ }
160
+ throw new Error("Unsupported algorithm or key not found");
161
+ };
162
+
163
+ const { payload } = await verify(token, keyLookup, { algorithms: ["RS256"] });
164
+ console.log(payload); // { userId: 123, permissions: ["read"] }
111
165
  ```
112
166
 
113
- ### JWS (JSON Web Signature)
167
+ ### JWE (JSON Web Encryption, [RFC 7516](https://datatracker.ietf.org/doc/html/rfc7516))
114
168
 
115
- This library provides functions to sign (sign) and verify (verify) data according to the JWS specification using HMAC with symmetric keys.
169
+ Functions to encrypt and decrypt data according to the JWE specification.
116
170
 
117
- #### `sign(data, secret, options?)`
171
+ #### `encrypt(payload, key, options)`
118
172
 
119
- Signs the provided payload using a symmetric secret.
173
+ Encrypts data to produce a JWE token.
120
174
 
121
- - `payload`: The data to sign (string or `Uint8Array`).
122
- - `secret`: The symmetric secret key to use for signing. Can be:
123
- - A `string`.
124
- - A `Uint8Array` containing the raw key bytes.
125
- - A symmetric JSON Web Key (`JWK`) object (with `kty: "oct"`).
126
- - `options` (optional):
127
- - `protectedHeader`: An object containing JWS header parameters to include.
128
- - alg: Signing Algorithm (default: `HS256`). Supported: `HS256`, `HS384`, `HS512`.
129
- - Other standard JWS or custom header parameters can be added here.
175
+ - `payload`: The data to encrypt (`string`, `Uint8Array`, or a JSON-serializable `object`).
176
+ - `key`: The Key Encryption Key (KEK) or password (`CryptoKey`, `JWK`, `string`, or `Uint8Array`).
177
+ - `options`:
178
+ - `alg`: (Required) The JWE Key Management algorithm (e.g., `"A128KW"`, `"RSA-OAEP-256"`, `"PBES2-HS256+A128KW"`, `"ECDH-ES+A128KW"`), defaults depends on the key provided.
179
+ - `enc`: (Required) The JWE Content Encryption algorithm (e.g., `"A128GCM"`, `"A256CBC-HS512"`), defaults depends on the key provided.
180
+ - `protectedHeader`: An object for JWE Protected Header parameters (e.g., `kid`, `typ`, `cty`, `crit`, `apu`, `apv`, `p2s`, `p2c`). If `payload` is an object and `typ` is not set, it defaults to `"JWT"`.
181
+ - `cek`: (Optional) Provide your own Content Encryption Key (`CryptoKey` or `Uint8Array`).
182
+ - `contentEncryptionIV`: (Optional) Provide your own Initialization Vector for content encryption (`Uint8Array`).
183
+ - Other algorithm-specific options like `p2s`, `p2c` (for PBES2), `keyManagementIV`, `ecdhPartyUInfo`, `ecdhPartyVInfo`.
130
184
 
131
- Returns a Promise resolving to the JWS token string in Compact Serialization format.
185
+ Returns a `Promise<string>` resolving to the JWE token in Compact Serialization format.
132
186
 
133
- **Example (using string secret):**
187
+ **Example (PBES2 password-based encryption):**
134
188
 
135
189
  ```ts
136
- import { sign } from "unjwt/jws";
190
+ import { encrypt } from "unjwt/jwe";
137
191
 
138
- const payload = JSON.stringify({ message: "My important data" });
139
- const secret = "supersecretkey";
192
+ const plaintext = "Secret message for password protection";
193
+ const password = "myVeryStrongPassword123!";
140
194
 
141
- const token = await sign(payload, secret, {
142
- protectedHeader: {
143
- alg: "HS512",
144
- },
145
- });
146
-
147
- console.log(token);
148
- // eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9...
195
+ // Fallback to PBES2-HS256+A128KW and A128GCM if no `alg` and `end` are provided
196
+ const jweToken = await encrypt(plaintext, password);
197
+ console.log(jweToken);
198
+ // JWE token string...
149
199
  ```
150
200
 
151
- **Example (using JWK secret):**
201
+ **Example (A128KW with A128GCM):**
152
202
 
153
203
  ```ts
154
- import { sign } from "unjwt/jws";
204
+ import { encrypt } from "unjwt/jwe";
155
205
  import { generateKey } from "unjwt/jwk";
156
206
 
157
- const payload = JSON.stringify({ message: "Data signed with JWK" });
158
- // Generate a JWK suitable for HS256
159
- const secretJwk = await generateKey("oct", 256, { alg: "HS256" });
207
+ const payload = { data: "sensitive information" };
208
+ const kek = await generateKey("A128KW"); // AES Key Wrap key
160
209
 
161
- const token = await sign(payload, secretJwk); // alg defaults to HS256
210
+ const jweToken = await encrypt(payload, kek, {
211
+ alg: "A128KW",
212
+ enc: "A128GCM",
213
+ protectedHeader: { kid: "aes-key-1" },
214
+ });
162
215
 
163
- console.log(token);
164
- // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
216
+ console.log(jweToken);
217
+ // eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiYWVzLWtleS0xIn0...
165
218
  ```
166
219
 
167
- #### `verify(token, secret, options?)`
220
+ #### `decrypt(jwe, key, options?)`
168
221
 
169
- Verifies a JWS token using a symmetric secret.
222
+ Decrypts a JWE token.
170
223
 
171
- - `token`: The JWS token string (Compact Serialization).
172
- - `secret`: The symmetric secret key used for signing. Can be:
173
- - A `string`.
174
- - A `Uint8Array` containing the raw key bytes.
175
- - A symmetric JSON Web Key (`JWK`) object (with `kty: "oct"`).
224
+ - `jwe`: The JWE token string.
225
+ - `key`: The Key Decryption Key (KDK) or password (`CryptoKey`, `JWK`, `string`, `Uint8Array`, or a `JWEKeyLookupFunction`).
226
+ A `JWEKeyLookupFunction` has the signature `(header: JWEHeaderParameters) => Promise<CryptoKey | JWK | string | Uint8Array> | CryptoKey | JWK | string | Uint8Array`.
176
227
  - `options` (optional):
177
- - `textOutput`: If `false`, returns the verified payload as a `Uint8Array` instead of a string (default: `true`).
228
+ - `algorithms`: Array of allowed JWE Key Management `alg` values.
229
+ - `encryptionAlgorithms`: Array of allowed JWE Content Encryption `enc` values.
230
+ - `critical`: Array of JWE header parameter names that the application understands.
231
+ - `unwrappedKeyAlgorithm`: (For `unwrapKey` internally) Algorithm details for the CEK after unwrapping.
232
+ - `keyUsage`: (For `unwrapKey` internally) Intended usages for the unwrapped CEK.
178
233
 
179
- Returns a Promise resolving to the verified payload (string or `Uint8Array`). Throws an error if the signature is invalid or the token is malformed.
234
+ Returns a `Promise<JWEDecryptResult<T>>` which is an object `{ payload: T, protectedHeader: JWEHeaderParameters, cek: Uint8Array, aad: Uint8Array }`.
235
+ The `payload` type `T` can be `JWTClaims` (object) or `string`.
180
236
 
181
- **Example (using string secret):**
237
+ **Example (PBES2 password-based decryption):**
182
238
 
183
239
  ```ts
184
- import { verify } from "unjwt/jws";
240
+ import { decrypt } from "unjwt/jwe";
241
+ // const jweToken = ...; // From PBES2 encrypt example
242
+ // const password = "myVeryStrongPassword123!";
185
243
 
186
- const token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..."; // From sign example
187
- const secret = "supersecretkey";
244
+ const { payload } = await decrypt(jweToken, password);
245
+ ```
188
246
 
189
- // Verify as string (default)
190
- const verifiedString = await verify(token, secret);
191
- console.log(verifiedString); // {"message":"My important data"}
247
+ **Example (A128KW with A128GCM):**
192
248
 
193
- // Verify as Uint8Array
194
- const verifiedBytes = await verify(token, secret, { textOutput: false });
195
- console.log(verifiedBytes); // Uint8Array [ 123, 34, ... ]
249
+ ```ts
250
+ import { decrypt } from "unjwt/jwe";
251
+ // const jweToken = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwia2lkIjoiYWVzLWtleS0xIn0...";
252
+ // const kek = ...; // The same AES Key Wrap key used for encryption
253
+
254
+ async function decryptData(jweToken: string, kek: CryptoKey) {
255
+ try {
256
+ const { payload, protectedHeader, cek } = await decrypt(jweToken, kek, {
257
+ algorithms: ["A128KW"],
258
+ encryptionAlgorithms: ["A128GCM"],
259
+ });
260
+ console.log("Decrypted Plaintext:", payload);
261
+ console.log("Protected Header:", protectedHeader);
262
+ // console.log("CEK (Content Encryption Key):", cek);
263
+ } catch (error) {
264
+ console.error("Decryption failed:", error);
265
+ }
266
+ }
196
267
  ```
197
268
 
198
- **Example (using JWK secret):**
269
+ ### JWK (JSON Web Key, [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517))
270
+
271
+ Utilities for working with JSON Web Keys.
272
+
273
+ #### `generateKey(alg, options?)`
274
+
275
+ Generates a cryptographic key.
276
+
277
+ - `alg`: The JWA algorithm identifier for the key to be generated (e.g., `"HS256"`, `"RS256"`, `"ES256"`, `"A128KW"`, `"A128GCM"`, `"A128CBC-HS256"`).
278
+ - `options` (optional):
279
+ - `toJWK`: If `true`, returns the key(s) in JWK format. Otherwise, returns `CryptoKey`(s) or `Uint8Array` (for composite keys like AES-CBC-HS\*). Default `false`.
280
+ - `extractable`: Boolean, whether the generated `CryptoKey` can be exported. Default `true`.
281
+ - `keyUsage`: Array of `KeyUsage` strings. Defaults are algorithm-specific.
282
+ - `modulusLength`: For RSA keys (e.g., `2048`, `4096`). Default `2048`.
283
+ - `publicExponent`: For RSA keys. Default `new Uint8Array([0x01, 0x00, 0x01])`.
284
+
285
+ Returns a `Promise` resolving to `CryptoKey`, `CryptoKeyPair`, `Uint8Array` (for composite keys), `JWK`, or `{ privateKey: JWK, publicKey: JWK }` depending on `alg` and `options.toJWK`.
286
+
287
+ **Examples:**
199
288
 
200
289
  ```ts
201
- import { verify } from "unjwt/jws";
202
290
  import { generateKey } from "unjwt/jwk";
203
291
 
204
- const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // From sign example
205
- const secretJwk = { kty: "oct", k: "...", alg: "HS256" }; // Load or use the generated JWK
292
+ // Generate an HS256 CryptoKey
293
+ const hmacKey = await generateKey("HS256");
294
+ console.log(hmacKey); // CryptoKey
206
295
 
207
- const verifiedPayload = await verify(token, secretJwk);
208
- console.log(verifiedPayload); // {"message":"Data signed with JWK"}
209
- ```
296
+ // Generate an RS256 CryptoKeyPair
297
+ const rsaKeyPair = await generateKey("RS256", { modulusLength: 2048 });
298
+ console.log(rsaKeyPair.publicKey); // CryptoKey
299
+ console.log(rsaKeyPair.privateKey); // CryptoKey
210
300
 
211
- ### JWK (JSON Web Key)
301
+ // Generate an ES384 key pair as JWKs
302
+ const ecJwks = await generateKey("ES384", { toJWK: true });
303
+ console.log(ecJwks.publicKey); // JWK
304
+ console.log(ecJwks.privateKey); // JWK
212
305
 
213
- This library provides functions to generate (`generateKey`), export (`exportKey`), and import (`importKey`) symmetric keys (`kty: "oct"`) using the Web Crypto API.
306
+ // Generate a composite key for A128CBC-HS256 as Uint8Array
307
+ const aesCbcHsKeyBytes = await generateKey("A128CBC-HS256");
308
+ console.log(aesCbcHsKeyBytes); // Uint8Array (32 bytes: 16 for AES, 16 for HMAC)
214
309
 
215
- #### `generateKey(type, length, jwk?)`
310
+ // Generate an A256GCM key as a JWK
311
+ const aesGcmJwk = await generateKey("A256GCM", { toJWK: true });
312
+ console.log(aesGcmJwk); // JWK
313
+ ```
314
+
315
+ #### `deriveKeyFromPassword(password, alg, options)`
216
316
 
217
- Generates a new key as a JWK object.
317
+ Derives a key from a password using PBKDF2 for PBES2 algorithms.
218
318
 
219
- - `type`: The key type. Currently only supports `"oct"` (symmetric).
220
- - `length`(for `"oct"`): Key length in bits (e.g., `128`, `192`, `256`, `512`). Choose a length appropriate for the intended algorithm (e.g., >= 256 for HS256, >= 384 for HS384, >= 512 for HS512).
221
- - `jwk` (optional, for `"oct"`): Any valid JWK param such as a JWA algorithm identifier (e.g., `HS256`, `A128KW`) to include in the generated JWK.
319
+ - `password`: The password (`string` or `Uint8Array`).
320
+ - `alg`: The PBES2 algorithm (e.g., `"PBES2-HS256+A128KW"`).
321
+ - `options`:
322
+ - `salt`: The salt (`Uint8Array`, at least 8 octets).
323
+ - `iterations`: The iteration count (positive integer).
324
+ - `toJWK`: If `true`, returns a `JWK_oct`. Otherwise, `CryptoKey`. Default `false`.
325
+ - `extractable`: Boolean for `CryptoKey`. Default `false` unless `toJWK` is true.
326
+ - `keyUsage`: For `CryptoKey`. Default `["wrapKey", "unwrapKey"]`.
222
327
 
223
- Returns a Promise resolving to the generated key as a JWK object (`{ kty: "oct", k: "...", ... }`).
328
+ Returns a `Promise` resolving to `CryptoKey` or `JWK_oct`.
224
329
 
225
330
  **Example:**
226
331
 
227
332
  ```ts
228
- import { generateKey } from "unjwt/jwk";
333
+ import { deriveKeyFromPassword } from "unjwt/jwk";
334
+ import { randomBytes, textEncoder } from "unjwt/utils";
229
335
 
230
- // Generate a 512-bit key suitable for HS512
231
- const jwk = await generateKey("oct", 512, { alg: "HS512" });
336
+ const password = "mySecretPassword";
337
+ const salt = randomBytes(16);
338
+ const iterations = 4096;
232
339
 
233
- console.log(jwk);
234
- // {
235
- // kty: 'oct',
236
- // k: '...', // base64url encoded key material
237
- // ext: true,
238
- // alg: 'HS512'
239
- // }
340
+ const derivedKey = await deriveKeyFromPassword(password, "PBES2-HS384+A192KW", {
341
+ salt,
342
+ iterations,
343
+ });
344
+ console.log(derivedKey); // CryptoKey for AES-KW (192-bit)
345
+
346
+ const derivedJwk = await deriveKeyFromPassword(password, "PBES2-HS512+A256KW", {
347
+ salt,
348
+ iterations,
349
+ toJWK: true,
350
+ });
351
+ console.log(derivedJwk); // JWK_oct { kty: "oct", k: "...", alg: "A256KW" }
352
+ ```
353
+
354
+ #### `importKey(keyMaterial, alg?)`
355
+
356
+ Imports a key from various formats. This is a flexible wrapper.
357
+
358
+ - `keyMaterial`: The key to import. Can be:
359
+ - `CryptoKey`: Returned directly.
360
+ - `Uint8Array`: Returned directly (treated as raw symmetric key bytes).
361
+ - `string`: Encoded to `Uint8Array` and returned.
362
+ - `JWK_oct` (symmetric JWK with `k` property): The `k` value is Base64URL decoded and returned as `Uint8Array`.
363
+ - Other `JWK` types (asymmetric): Imported into a `CryptoKey`.
364
+ - `alg` (optional): The JWA algorithm string. **Required** when importing asymmetric JWKs (e.g., RSA, EC) to provide context for `crypto.subtle.importKey`.
365
+
366
+ Returns a `Promise` resolving to `CryptoKey` or `Uint8Array`.
367
+
368
+ **Examples:**
369
+
370
+ ```ts
371
+ import { importKey } from "unjwt/jwk";
372
+ import { textEncoder, base64UrlDecode } from "unjwt/utils";
373
+
374
+ // Import raw symmetric key bytes
375
+ const rawBytes = textEncoder.encode("a-32-byte-long-secret-key-123"); // 32 bytes for AES-256 or HS256
376
+ const symmetricKeyBytes = await importKey(rawBytes);
377
+ console.log(symmetricKeyBytes); // Uint8Array
378
+
379
+ // Import a symmetric JWK (kty: "oct")
380
+ const octJwk = {
381
+ kty: "oct",
382
+ k: "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr0", // Example key
383
+ };
384
+ const importedOctBytes = await importKey(octJwk); // Returns Uint8Array
385
+ console.log(importedOctBytes); // Uint8Array (decoded from k)
386
+
387
+ // Import an RSA Public Key JWK
388
+ const rsaPublicJwk = {
389
+ kty: "RSA",
390
+ n: "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3ok92YEnjsADC4Ue87zwRzH2J-TCwlcQrY3E9gGZJZL2g_2_5QjLhL0gR0xYj04_N4M",
391
+ e: "AQAB",
392
+ alg: "RS256",
393
+ kid: "rsa-pub-1",
394
+ };
395
+ const rsaPublicKey = await importKey(rsaPublicJwk, "RS256"); // 'alg' is crucial here
396
+ console.log(rsaPublicKey); // CryptoKey
240
397
  ```
241
398
 
242
- #### `exportKey(key)`
399
+ #### `exportKey(key, jwk?)`
243
400
 
244
- Exports a symmetric `CryptoKey` to JWK format.
401
+ Exports a `CryptoKey` to JWK format.
245
402
 
246
- - `key`: The `CryptoKey` to export. It must be `extractable` and currently only of type `"secret"`.
403
+ - `key`: The `CryptoKey` to export (must be `extractable`).
404
+ - `jwk` (optional): A partial `JWK` object to merge with the exported properties (e.g., to add `kid`, `use`, or override `alg`).
247
405
 
248
- Returns a Promise resolving to the exported key as a JWK object. The JWK will include `kty`, `k`, `key_ops`, `ext`, and potentially `alg` based on the `CryptoKey`'s algorithm.
406
+ Returns a `Promise<JWK>`.
249
407
 
250
408
  **Example:**
251
409
 
252
410
  ```ts
253
- import { importKey, exportKey } from "unjwt/jwk";
254
-
255
- // First, import or generate a CryptoKey
256
- const rawKeyBytes = new TextEncoder().encode(
257
- "a-very-secure-secret-key-for-hmac",
258
- );
259
- const cryptoKey = await importKey(
260
- rawKeyBytes,
261
- { name: "HMAC", hash: "SHA-256" },
262
- true, // Must be extractable to export
263
- ["sign", "verify"],
264
- );
265
-
266
- // Now export the CryptoKey to JWK
267
- const jwk = await exportKey(cryptoKey);
411
+ import { generateKey, exportKey } from "unjwt/jwk";
412
+
413
+ const { publicKey } = await generateKey("ES256"); // Generates an extractable CryptoKey
268
414
 
415
+ const jwk = await exportKey(publicKey, { kid: "ec-key-001", use: "sig" });
269
416
  console.log(jwk);
270
417
  // {
271
- // kty: 'oct',
272
- // k: 'YS12ZXJ5LXNlY3VyZS1zZWNyZXQta2V5LWZvci1obWFj',
273
- // alg: 'HS256',
274
- // key_ops: [ 'sign', 'verify' ],
275
- // ext: true
418
+ // kty: 'EC',
419
+ // crv: 'P-256',
420
+ // x: '...',
421
+ // y: '...',
422
+ // ext: true,
423
+ // key_ops: [ 'verify' ], // or as per generation
424
+ // kid: 'ec-key-001',
425
+ // use: 'sig'
276
426
  // }
277
427
  ```
278
428
 
279
- #### `importKey(key, algorithm, extractable, keyUsages)`
429
+ #### `wrapKey(alg, keyToWrap, wrappingKey, options?)`
280
430
 
281
- Imports a symmetric key from various formats into a `CryptoKey` object.
431
+ Wraps a Content Encryption Key (CEK).
282
432
 
283
- - `key`: The key material to import. Can be:
284
- - A symmetric JSON Web Key (`JWK`) object (with `kty: "oct"` and a `k` property).
285
- - A `string` representing the raw secret.
286
- - A `Uint8Array` containing the raw key bytes.
287
- - `algorithm`: The Web Crypto `AlgorithmIdentifier` object specifying the algorithm the key will be used for (e.g., `{ name: "HMAC", hash: "SHA-256" }`, `{ name: "AES-GCM", length: 256 }`).
288
- - `extractable`: A boolean indicating whether the generated `CryptoKey` can be exported later (using `exportKey` or `crypto.subtle.exportKey`).
289
- - `keyUsages`: An array of strings indicating the allowed operations for the key (e.g., `["sign", "verify"]`, `["encrypt", "decrypt"]`, `["wrapKey", "unwrapKey"]`).
433
+ - `alg`: The JWA key management algorithm (e.g., `"A128KW"`, `"RSA-OAEP"`).
434
+ - `keyToWrap`: The CEK to wrap (`CryptoKey` or `Uint8Array`).
435
+ - `wrappingKey`: The Key Encryption Key (KEK) (`CryptoKey`, `JWK`, or password `string`/`Uint8Array` for PBES2).
436
+ - `options` (optional): Algorithm-specific options (e.g., `p2s`, `p2c` for PBES2; `iv` for AES-GCMKW).
290
437
 
291
- Returns a Promise resolving to the imported `CryptoKey` object.
438
+ Returns a `Promise<WrapKeyResult>` containing `encryptedKey` and other parameters like `iv`, `tag`, `epk`, `p2s`, `p2c` as needed by the algorithm.
292
439
 
293
- **Example (importing a JWK):**
440
+ **Example (AES Key Wrap):**
294
441
 
295
442
  ```ts
296
- import { importKey } from "unjwt/jwk";
443
+ import { wrapKey, generateKey } from "unjwt/jwk";
444
+ import { randomBytes } from "unjwt/utils";
297
445
 
298
- const jwk = {
299
- kty: "oct",
300
- k: "AyMee--70a4a4xED9qS4sN0KxBas6KTMx7_Q9Gf6nvM", // Example base64url key
301
- alg: "HS256", // Optional hint, but algorithm param below is definitive
302
- };
303
-
304
- const cryptoKey = await importKey(
305
- jwk,
306
- { name: "HMAC", hash: "SHA-256" }, // Algorithm for the CryptoKey
307
- false, // Make this key non-extractable
308
- ["verify"], // Allow only verification usage
309
- );
446
+ const cekToWrap = randomBytes(32); // e.g., a 256-bit AES key as Uint8Array
447
+ const kek = await generateKey("A128KW"); // 128-bit AES Key Wrap key
310
448
 
311
- console.log(cryptoKey);
312
- // CryptoKey { type: 'secret', extractable: false, algorithm: { ... }, usages: [ 'verify' ] }
449
+ const { encryptedKey } = await wrapKey("A128KW", cekToWrap, kek);
450
+ console.log("Wrapped CEK:", encryptedKey); // Uint8Array
313
451
  ```
314
452
 
315
- **Example (importing a raw string):**
453
+ #### `unwrapKey(alg, wrappedKey, unwrappingKey, options?)`
316
454
 
317
- ```ts
318
- import { importKey } from "unjwt/jwk";
455
+ Unwraps a Content Encryption Key (CEK).
319
456
 
320
- const rawSecret = "my-raw-password-string";
457
+ - `alg`: The JWA key management algorithm.
458
+ - `wrappedKey`: The encrypted CEK (`Uint8Array`).
459
+ - `unwrappingKey`: The Key Decryption Key (KDK).
460
+ - `options` (optional):
461
+ - `returnAs`: If `false`, returns `Uint8Array`. If `true` (default) or undefined, returns `CryptoKey`.
462
+ - `unwrappedKeyAlgorithm`: `AlgorithmIdentifier` for the imported CEK if `returnAs` is `true`.
463
+ - `keyUsage`: `KeyUsage[]` for the imported CEK if `returnAs` is `true`.
464
+ - `extractable`: Boolean for the imported CEK.
465
+ - Other algorithm-specific options (e.g., `p2s`, `p2c`, `iv`, `tag`, `epk`).
321
466
 
322
- const cryptoKey = await importKey(
323
- rawSecret,
324
- { name: "HMAC", hash: "SHA-384" },
325
- true,
326
- ["sign"],
327
- );
467
+ Returns a `Promise` resolving to the unwrapped CEK as `CryptoKey` or `Uint8Array`.
328
468
 
329
- console.log(cryptoKey);
330
- // CryptoKey { type: 'secret', extractable: true, algorithm: { ... }, usages: [ 'sign' ] }
469
+ **Example (AES Key Unwrap):**
470
+
471
+ ```ts
472
+ import { unwrapKey, generateKey } from "unjwt/jwk";
473
+ // const encryptedKey = ...; // From wrapKey example
474
+ // const kdk = ...; // Same KEK used for wrapping
475
+
476
+ async function unwrapMyKey(encryptedKey: Uint8Array, kdk: CryptoKey) {
477
+ const unwrappedCekBytes = await unwrapKey("A128KW", encryptedKey, kdk, {
478
+ returnAs: false, // Get raw bytes
479
+ });
480
+ console.log("Unwrapped CEK (bytes):", unwrappedCekBytes); // Uint8Array
481
+
482
+ const unwrappedCekCryptoKey = await unwrapKey("A128KW", encryptedKey, kdk, {
483
+ returnAs: true, // Get CryptoKey
484
+ unwrappedKeyAlgorithm: { name: "AES-GCM", length: 256 }, // Specify CEK's intended alg
485
+ keyUsage: ["encrypt", "decrypt"],
486
+ });
487
+ console.log("Unwrapped CEK (CryptoKey):", unwrappedCekCryptoKey); // CryptoKey
488
+ }
331
489
  ```
332
490
 
491
+ ### Utility Functions
492
+
493
+ `unjwt/utils` exports several helpful functions:
494
+
495
+ - `base64UrlEncode(data: Uint8Array | string): string`
496
+ - `base64UrlDecode(str?: string, toString?: boolean): Uint8Array | string` (Decodes to string by default, or `Uint8Array` if `toString` is `false`)
497
+ - `randomBytes(length: number): Uint8Array`
498
+ - `textEncoder: TextEncoder`
499
+ - `textDecoder: TextDecoder`
500
+ - Type guards: `isJWK(key)`, `isCryptoKey(key)`, `isCryptoKeyPair(keyPair)`
501
+
333
502
  ## Development
334
503
 
335
504
  <details>
@@ -347,6 +516,7 @@ console.log(cryptoKey);
347
516
  ## Credits
348
517
 
349
518
  Originally developed by [Johann Schopplich](https://github.com/johannschopplich/unjwt).
519
+ Heavily inspired by [Filip Skokan's work](https://github.com/panva/jose).
350
520
 
351
521
  ## License
352
522