@enbox/crypto 0.0.2 → 0.0.3

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 (182) hide show
  1. package/README.md +34 -102
  2. package/dist/browser.js +6 -10
  3. package/dist/browser.js.map +4 -4
  4. package/dist/browser.mjs +6 -10
  5. package/dist/browser.mjs.map +4 -4
  6. package/dist/esm/algorithms/aes-gcm.js +1 -1
  7. package/dist/esm/algorithms/aes-gcm.js.map +1 -1
  8. package/dist/esm/algorithms/ecdsa.js +9 -5
  9. package/dist/esm/algorithms/ecdsa.js.map +1 -1
  10. package/dist/esm/algorithms/eddsa.js +9 -5
  11. package/dist/esm/algorithms/eddsa.js.map +1 -1
  12. package/dist/esm/algorithms/sha-2.js +1 -1
  13. package/dist/esm/algorithms/sha-2.js.map +1 -1
  14. package/dist/esm/crypto-error.js +41 -0
  15. package/dist/esm/crypto-error.js.map +1 -0
  16. package/dist/esm/index.js +3 -0
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/jose/jwk.js +52 -26
  19. package/dist/esm/jose/jwk.js.map +1 -1
  20. package/dist/esm/local-key-manager.js +3 -2
  21. package/dist/esm/local-key-manager.js.map +1 -1
  22. package/dist/esm/primitives/aes-ctr.js.map +1 -1
  23. package/dist/esm/primitives/aes-gcm.js.map +1 -1
  24. package/dist/esm/primitives/aes-kw.js +246 -0
  25. package/dist/esm/primitives/aes-kw.js.map +1 -0
  26. package/dist/esm/primitives/concat-kdf.js +1 -1
  27. package/dist/esm/primitives/concat-kdf.js.map +1 -1
  28. package/dist/esm/primitives/ed25519.js +3 -3
  29. package/dist/esm/primitives/ed25519.js.map +1 -1
  30. package/dist/esm/primitives/hkdf.js +79 -0
  31. package/dist/esm/primitives/hkdf.js.map +1 -0
  32. package/dist/esm/primitives/pbkdf2.js +49 -0
  33. package/dist/esm/primitives/pbkdf2.js.map +1 -1
  34. package/dist/esm/primitives/secp256k1.js +4 -4
  35. package/dist/esm/primitives/secp256k1.js.map +1 -1
  36. package/dist/esm/primitives/secp256r1.js +4 -4
  37. package/dist/esm/primitives/secp256r1.js.map +1 -1
  38. package/dist/esm/primitives/x25519.js +1 -1
  39. package/dist/esm/primitives/x25519.js.map +1 -1
  40. package/dist/esm/primitives/xchacha20-poly1305.js +48 -3
  41. package/dist/esm/primitives/xchacha20-poly1305.js.map +1 -1
  42. package/dist/esm/primitives/xchacha20.js +1 -1
  43. package/dist/esm/primitives/xchacha20.js.map +1 -1
  44. package/dist/esm/utils.js.map +1 -1
  45. package/dist/types/algorithms/aes-ctr.d.ts +1 -1
  46. package/dist/types/algorithms/aes-ctr.d.ts.map +1 -1
  47. package/dist/types/algorithms/aes-gcm.d.ts +2 -2
  48. package/dist/types/algorithms/aes-gcm.d.ts.map +1 -1
  49. package/dist/types/algorithms/ecdsa.d.ts +1 -1
  50. package/dist/types/algorithms/ecdsa.d.ts.map +1 -1
  51. package/dist/types/algorithms/eddsa.d.ts +2 -2
  52. package/dist/types/algorithms/eddsa.d.ts.map +1 -1
  53. package/dist/types/algorithms/sha-2.d.ts +1 -1
  54. package/dist/types/algorithms/sha-2.d.ts.map +1 -1
  55. package/dist/types/crypto-error.d.ts +29 -0
  56. package/dist/types/crypto-error.d.ts.map +1 -0
  57. package/dist/types/index.d.ts +3 -0
  58. package/dist/types/index.d.ts.map +1 -1
  59. package/dist/types/jose/jwk.d.ts.map +1 -1
  60. package/dist/types/local-key-manager.d.ts +3 -3
  61. package/dist/types/local-key-manager.d.ts.map +1 -1
  62. package/dist/types/primitives/aes-kw.d.ts +103 -0
  63. package/dist/types/primitives/aes-kw.d.ts.map +1 -0
  64. package/dist/types/primitives/concat-kdf.d.ts +1 -1
  65. package/dist/types/primitives/concat-kdf.d.ts.map +1 -1
  66. package/dist/types/primitives/hkdf.d.ts +90 -0
  67. package/dist/types/primitives/hkdf.d.ts.map +1 -0
  68. package/dist/types/primitives/pbkdf2.d.ts +58 -0
  69. package/dist/types/primitives/pbkdf2.d.ts.map +1 -1
  70. package/dist/types/primitives/xchacha20-poly1305.d.ts +47 -0
  71. package/dist/types/primitives/xchacha20-poly1305.d.ts.map +1 -1
  72. package/dist/types/types/cipher.d.ts +1 -1
  73. package/dist/types/types/crypto-api.d.ts +3 -3
  74. package/dist/types/types/crypto-api.d.ts.map +1 -1
  75. package/dist/types/types/params-direct.d.ts +79 -1
  76. package/dist/types/types/params-direct.d.ts.map +1 -1
  77. package/dist/utils.js.map +2 -2
  78. package/package.json +26 -39
  79. package/src/algorithms/aes-ctr.ts +1 -1
  80. package/src/algorithms/aes-gcm.ts +3 -2
  81. package/src/algorithms/ecdsa.ts +13 -7
  82. package/src/algorithms/eddsa.ts +9 -9
  83. package/src/algorithms/sha-2.ts +2 -2
  84. package/src/crypto-error.ts +45 -0
  85. package/src/index.ts +3 -0
  86. package/src/jose/jwk.ts +32 -32
  87. package/src/local-key-manager.ts +14 -13
  88. package/src/primitives/aes-ctr.ts +1 -1
  89. package/src/primitives/aes-gcm.ts +5 -5
  90. package/src/primitives/aes-kw.ts +269 -0
  91. package/src/primitives/concat-kdf.ts +4 -2
  92. package/src/primitives/ed25519.ts +6 -6
  93. package/src/primitives/hkdf.ts +121 -0
  94. package/src/primitives/pbkdf2.ts +91 -0
  95. package/src/primitives/secp256k1.ts +6 -6
  96. package/src/primitives/secp256r1.ts +6 -6
  97. package/src/primitives/x25519.ts +3 -3
  98. package/src/primitives/xchacha20-poly1305.ts +57 -4
  99. package/src/primitives/xchacha20.ts +1 -1
  100. package/src/types/cipher.ts +1 -1
  101. package/src/types/crypto-api.ts +5 -5
  102. package/src/types/params-direct.ts +97 -1
  103. package/src/utils.ts +2 -2
  104. package/dist/cjs/algorithms/aes-ctr.js +0 -188
  105. package/dist/cjs/algorithms/aes-ctr.js.map +0 -1
  106. package/dist/cjs/algorithms/aes-gcm.js +0 -196
  107. package/dist/cjs/algorithms/aes-gcm.js.map +0 -1
  108. package/dist/cjs/algorithms/crypto-algorithm.js +0 -13
  109. package/dist/cjs/algorithms/crypto-algorithm.js.map +0 -1
  110. package/dist/cjs/algorithms/ecdsa.js +0 -352
  111. package/dist/cjs/algorithms/ecdsa.js.map +0 -1
  112. package/dist/cjs/algorithms/eddsa.js +0 -325
  113. package/dist/cjs/algorithms/eddsa.js.map +0 -1
  114. package/dist/cjs/algorithms/sha-2.js +0 -119
  115. package/dist/cjs/algorithms/sha-2.js.map +0 -1
  116. package/dist/cjs/index.js +0 -41
  117. package/dist/cjs/index.js.map +0 -1
  118. package/dist/cjs/jose/jwe.js +0 -3
  119. package/dist/cjs/jose/jwe.js.map +0 -1
  120. package/dist/cjs/jose/jwk.js +0 -278
  121. package/dist/cjs/jose/jwk.js.map +0 -1
  122. package/dist/cjs/jose/jws.js +0 -3
  123. package/dist/cjs/jose/jws.js.map +0 -1
  124. package/dist/cjs/jose/jwt.js +0 -3
  125. package/dist/cjs/jose/jwt.js.map +0 -1
  126. package/dist/cjs/jose/utils.js +0 -60
  127. package/dist/cjs/jose/utils.js.map +0 -1
  128. package/dist/cjs/local-key-manager.js +0 -521
  129. package/dist/cjs/local-key-manager.js.map +0 -1
  130. package/dist/cjs/package.json +0 -1
  131. package/dist/cjs/primitives/aes-ctr.js +0 -398
  132. package/dist/cjs/primitives/aes-ctr.js.map +0 -1
  133. package/dist/cjs/primitives/aes-gcm.js +0 -425
  134. package/dist/cjs/primitives/aes-gcm.js.map +0 -1
  135. package/dist/cjs/primitives/concat-kdf.js +0 -215
  136. package/dist/cjs/primitives/concat-kdf.js.map +0 -1
  137. package/dist/cjs/primitives/ed25519.js +0 -651
  138. package/dist/cjs/primitives/ed25519.js.map +0 -1
  139. package/dist/cjs/primitives/pbkdf2.js +0 -120
  140. package/dist/cjs/primitives/pbkdf2.js.map +0 -1
  141. package/dist/cjs/primitives/secp256k1.js +0 -958
  142. package/dist/cjs/primitives/secp256k1.js.map +0 -1
  143. package/dist/cjs/primitives/secp256r1.js +0 -959
  144. package/dist/cjs/primitives/secp256r1.js.map +0 -1
  145. package/dist/cjs/primitives/sha256.js +0 -93
  146. package/dist/cjs/primitives/sha256.js.map +0 -1
  147. package/dist/cjs/primitives/x25519.js +0 -498
  148. package/dist/cjs/primitives/x25519.js.map +0 -1
  149. package/dist/cjs/primitives/xchacha20-poly1305.js +0 -340
  150. package/dist/cjs/primitives/xchacha20-poly1305.js.map +0 -1
  151. package/dist/cjs/primitives/xchacha20.js +0 -316
  152. package/dist/cjs/primitives/xchacha20.js.map +0 -1
  153. package/dist/cjs/types/cipher.js +0 -3
  154. package/dist/cjs/types/cipher.js.map +0 -1
  155. package/dist/cjs/types/crypto-api.js +0 -3
  156. package/dist/cjs/types/crypto-api.js.map +0 -1
  157. package/dist/cjs/types/hasher.js +0 -3
  158. package/dist/cjs/types/hasher.js.map +0 -1
  159. package/dist/cjs/types/identifier.js +0 -3
  160. package/dist/cjs/types/identifier.js.map +0 -1
  161. package/dist/cjs/types/key-compressor.js +0 -3
  162. package/dist/cjs/types/key-compressor.js.map +0 -1
  163. package/dist/cjs/types/key-converter.js +0 -3
  164. package/dist/cjs/types/key-converter.js.map +0 -1
  165. package/dist/cjs/types/key-deriver.js +0 -3
  166. package/dist/cjs/types/key-deriver.js.map +0 -1
  167. package/dist/cjs/types/key-generator.js +0 -3
  168. package/dist/cjs/types/key-generator.js.map +0 -1
  169. package/dist/cjs/types/key-io.js +0 -3
  170. package/dist/cjs/types/key-io.js.map +0 -1
  171. package/dist/cjs/types/key-wrapper.js +0 -3
  172. package/dist/cjs/types/key-wrapper.js.map +0 -1
  173. package/dist/cjs/types/params-direct.js +0 -3
  174. package/dist/cjs/types/params-direct.js.map +0 -1
  175. package/dist/cjs/types/params-enclosed.js +0 -3
  176. package/dist/cjs/types/params-enclosed.js.map +0 -1
  177. package/dist/cjs/types/params-kms.js +0 -3
  178. package/dist/cjs/types/params-kms.js.map +0 -1
  179. package/dist/cjs/types/signer.js +0 -3
  180. package/dist/cjs/types/signer.js.map +0 -1
  181. package/dist/cjs/utils.js +0 -173
  182. package/dist/cjs/utils.js.map +0 -1
@@ -0,0 +1,269 @@
1
+ import type { Jwk } from '../jose/jwk.js';
2
+ import type { UnwrapKeyParams, WrapKeyParams } from '../types/params-direct.js';
3
+
4
+ import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto';
5
+
6
+ import { Convert } from '@enbox/common';
7
+ import { computeJwkThumbprint, isOctPrivateJwk } from '../jose/jwk.js';
8
+ import { CryptoError, CryptoErrorCode } from '../crypto-error.js';
9
+
10
+ /**
11
+ * Constant defining the AES key length values in bits.
12
+ *
13
+ * @remarks
14
+ * NIST publication FIPS 197 states:
15
+ * > The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt
16
+ * > and decrypt data in blocks of 128 bits.
17
+ *
18
+ * This implementation does not support key lengths that are different from the three values
19
+ * defined by this constant.
20
+ *
21
+ * @see {@link https://doi.org/10.6028/NIST.FIPS.197-upd1 | NIST FIPS 197}
22
+ */
23
+ const AES_KEY_LENGTHS = [128, 192, 256] as const;
24
+
25
+ export class AesKw {
26
+ /**
27
+ * Converts a raw private key in bytes to its corresponding JSON Web Key (JWK) format.
28
+ *
29
+ * @remarks
30
+ * This method takes a symmetric key represented as a byte array (Uint8Array) and
31
+ * converts it into a JWK object for use with AES (Advanced Encryption Standard)
32
+ * for key wrapping. The conversion process involves encoding the key into
33
+ * base64url format and setting the appropriate JWK parameters.
34
+ *
35
+ * The resulting JWK object includes the following properties:
36
+ * - `kty`: Key Type, set to 'oct' for Octet Sequence (representing a symmetric key).
37
+ * - `k`: The symmetric key, base64url-encoded.
38
+ * - `kid`: Key ID, generated based on the JWK thumbprint.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const privateKeyBytes = new Uint8Array([...]); // Replace with actual symmetric key bytes
43
+ * const privateKey = await AesKw.bytesToPrivateKey({ privateKeyBytes });
44
+ * ```
45
+ *
46
+ * @param params - The parameters for the symmetric key conversion.
47
+ * @param params.privateKeyBytes - The raw symmetric key as a Uint8Array.
48
+ *
49
+ * @returns A Promise that resolves to the symmetric key in JWK format.
50
+ */
51
+ public static async bytesToPrivateKey({ privateKeyBytes }: {
52
+ privateKeyBytes: Uint8Array;
53
+ }): Promise<Jwk> {
54
+ // Construct the private key in JWK format.
55
+ const privateKey: Jwk = {
56
+ k : Convert.uint8Array(privateKeyBytes).toBase64Url(),
57
+ kty : 'oct'
58
+ };
59
+
60
+ // Compute the JWK thumbprint and set as the key ID.
61
+ privateKey.kid = await computeJwkThumbprint({ jwk: privateKey });
62
+
63
+ // Add algorithm identifier based on key length.
64
+ const lengthInBits = privateKeyBytes.length * 8;
65
+ privateKey.alg = { 128: 'A128KW', 192: 'A192KW', 256: 'A256KW' }[lengthInBits];
66
+
67
+ return privateKey;
68
+ }
69
+
70
+ /**
71
+ * Generates a symmetric key for AES for key wrapping in JSON Web Key (JWK) format.
72
+ *
73
+ * @remarks
74
+ * This method creates a new symmetric key of a specified length suitable for use with
75
+ * AES key wrapping. It uses cryptographically secure random number generation to
76
+ * ensure the uniqueness and security of the key. The generated key adheres to the JWK
77
+ * format, making it compatible with common cryptographic standards and easy to use in
78
+ * various cryptographic processes.
79
+ *
80
+ * The generated key includes the following components:
81
+ * - `kty`: Key Type, set to 'oct' for Octet Sequence.
82
+ * - `k`: The symmetric key component, base64url-encoded.
83
+ * - `kid`: Key ID, generated based on the JWK thumbprint.
84
+ * - `alg`: Algorithm, set to 'A128KW', 'A192KW', or 'A256KW' for AES Key Wrap with the
85
+ * specified key length.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * const length = 256; // Length of the key in bits (e.g., 128, 192, 256)
90
+ * const privateKey = await AesKw.generateKey({ length });
91
+ * ```
92
+ *
93
+ * @param params - The parameters for the key generation.
94
+ * @param params.length - The length of the key in bits. Common lengths are 128, 192, and 256 bits.
95
+ *
96
+ * @returns A Promise that resolves to the generated symmetric key in JWK format.
97
+ */
98
+ public static async generateKey({ length }: {
99
+ length: typeof AES_KEY_LENGTHS[number];
100
+ }): Promise<Jwk> {
101
+ // Validate the key length.
102
+ if (!(AES_KEY_LENGTHS as readonly number[]).includes(length)) {
103
+ throw new RangeError(`The key length is invalid: Must be ${AES_KEY_LENGTHS.join(', ')} bits`);
104
+ }
105
+
106
+ // Get the Web Crypto API interface.
107
+ const webCrypto = getWebcryptoSubtle() as SubtleCrypto;
108
+
109
+ // Generate a random private key.
110
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#usage_notes for
111
+ // an explanation for why Web Crypto generateKey() is used instead of getRandomValues().
112
+ const webCryptoKey = await webCrypto.generateKey( { name: 'AES-KW', length }, true, ['wrapKey', 'unwrapKey']);
113
+
114
+ // Export the private key in JWK format.
115
+ const { ext, key_ops, ...privateKey } = await webCrypto.exportKey('jwk', webCryptoKey) as Jwk;
116
+
117
+ // Compute the JWK thumbprint and set as the key ID.
118
+ privateKey.kid = await computeJwkThumbprint({ jwk: privateKey });
119
+
120
+ return privateKey;
121
+ }
122
+
123
+ /**
124
+ * Converts a private key from JSON Web Key (JWK) format to a raw byte array (Uint8Array).
125
+ *
126
+ * @remarks
127
+ * This method takes a symmetric key in JWK format and extracts its raw byte representation.
128
+ * It decodes the 'k' parameter of the JWK value, which represents the symmetric key in base64url
129
+ * encoding, into a byte array.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const privateKey = { ... }; // A symmetric key in JWK format
134
+ * const privateKeyBytes = await AesKw.privateKeyToBytes({ privateKey });
135
+ * ```
136
+ *
137
+ * @param params - The parameters for the symmetric key conversion.
138
+ * @param params.privateKey - The symmetric key in JWK format.
139
+ *
140
+ * @returns A Promise that resolves to the symmetric key as a Uint8Array.
141
+ */
142
+ public static async privateKeyToBytes({ privateKey }: {
143
+ privateKey: Jwk;
144
+ }): Promise<Uint8Array> {
145
+ // Verify the provided JWK represents a valid oct private key.
146
+ if (!isOctPrivateJwk(privateKey)) {
147
+ throw new Error(`AesKw: The provided key is not a valid oct private key.`);
148
+ }
149
+
150
+ // Decode the provided private key to bytes.
151
+ const privateKeyBytes = Convert.base64Url(privateKey.k).toUint8Array();
152
+
153
+ return privateKeyBytes;
154
+ }
155
+
156
+ public static async unwrapKey({ wrappedKeyBytes, wrappedKeyAlgorithm, decryptionKey }:
157
+ UnwrapKeyParams
158
+ ): Promise<Jwk> {
159
+ if (!('alg' in decryptionKey && decryptionKey.alg)) {
160
+ throw new CryptoError(CryptoErrorCode.InvalidJwk, `The decryption key is missing the 'alg' property.`);
161
+ }
162
+
163
+ if (!['A128KW', 'A192KW', 'A256KW'].includes(decryptionKey.alg)) {
164
+ throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'decryptionKey' algorithm is not supported: ${decryptionKey.alg}`);
165
+ }
166
+
167
+ // Get the Web Crypto API interface.
168
+ const webCrypto = getWebcryptoSubtle() as SubtleCrypto;
169
+
170
+ // Import the decryption key for use with the Web Crypto API.
171
+ const decryptionCryptoKey = await webCrypto.importKey(
172
+ 'jwk', // key format
173
+ decryptionKey as JsonWebKey, // key data
174
+ { name: 'AES-KW' }, // algorithm identifier
175
+ true, // key is extractable
176
+ ['unwrapKey'] // key usages
177
+ );
178
+
179
+ // Map the private key's JOSE algorithm name to the Web Crypto API algorithm identifier.
180
+ const webCryptoAlgorithm = {
181
+ A128KW : 'AES-KW', A192KW : 'AES-KW', A256KW : 'AES-KW',
182
+ A128GCM : 'AES-GCM', A192GCM : 'AES-GCM', A256GCM : 'AES-GCM',
183
+ }[wrappedKeyAlgorithm];
184
+
185
+ if (!webCryptoAlgorithm) {
186
+ throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'wrappedKeyAlgorithm' is not supported: ${wrappedKeyAlgorithm}`);
187
+ }
188
+
189
+ // Unwrap the key using the Web Crypto API.
190
+ const unwrappedCryptoKey = await webCrypto.unwrapKey(
191
+ 'raw', // output format
192
+ wrappedKeyBytes.buffer, // key to unwrap
193
+ decryptionCryptoKey, // unwrapping key
194
+ 'AES-KW', // algorithm identifier
195
+ { name: webCryptoAlgorithm }, // unwrapped key algorithm identifier
196
+ true, // key is extractable
197
+ ['unwrapKey'] // key usages
198
+ );
199
+
200
+ // Export the unwrapped key in JWK format.
201
+ const { ext, key_ops, ...unwrappedJsonWebKey } = await webCrypto.exportKey('jwk', unwrappedCryptoKey);
202
+ const unwrappedKey = unwrappedJsonWebKey as Jwk;
203
+
204
+ // Compute the JWK thumbprint and set as the key ID.
205
+ unwrappedKey.kid = await computeJwkThumbprint({ jwk: unwrappedKey });
206
+
207
+ return unwrappedKey;
208
+ }
209
+
210
+ public static async wrapKey({ unwrappedKey, encryptionKey }:
211
+ WrapKeyParams
212
+ ): Promise<Uint8Array> {
213
+ if (!('alg' in encryptionKey && encryptionKey.alg)) {
214
+ throw new CryptoError(CryptoErrorCode.InvalidJwk, `The encryption key is missing the 'alg' property.`);
215
+ }
216
+
217
+ if (!['A128KW', 'A192KW', 'A256KW'].includes(encryptionKey.alg)) {
218
+ throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'encryptionKey' algorithm is not supported: ${encryptionKey.alg}`);
219
+ }
220
+
221
+ if (!('alg' in unwrappedKey && unwrappedKey.alg)) {
222
+ throw new CryptoError(CryptoErrorCode.InvalidJwk, `The private key to wrap is missing the 'alg' property.`);
223
+ }
224
+
225
+ // Get the Web Crypto API interface.
226
+ const webCrypto = getWebcryptoSubtle() as SubtleCrypto;
227
+
228
+ // Import the encryption key for use with the Web Crypto API.
229
+ const encryptionCryptoKey = await webCrypto.importKey(
230
+ 'jwk', // key format
231
+ encryptionKey as JsonWebKey, // key data
232
+ { name: 'AES-KW' }, // algorithm identifier
233
+ true, // key is extractable
234
+ ['wrapKey'] // key usages
235
+ );
236
+
237
+ // Map the private key's JOSE algorithm name to the Web Crypto API algorithm identifier.
238
+ const webCryptoAlgorithm = {
239
+ A128KW : 'AES-KW', A192KW : 'AES-KW', A256KW : 'AES-KW',
240
+ A128GCM : 'AES-GCM', A192GCM : 'AES-GCM', A256GCM : 'AES-GCM',
241
+ }[unwrappedKey.alg];
242
+
243
+ if (!webCryptoAlgorithm) {
244
+ throw new CryptoError(CryptoErrorCode.AlgorithmNotSupported, `The 'unwrappedKey' algorithm is not supported: ${unwrappedKey.alg}`);
245
+ }
246
+
247
+ // Import the private key to wrap for use with the Web Crypto API.
248
+ const unwrappedCryptoKey = await webCrypto.importKey(
249
+ 'jwk', // key format
250
+ unwrappedKey as JsonWebKey, // key data
251
+ { name: webCryptoAlgorithm }, // algorithm identifier
252
+ true, // key is extractable
253
+ ['unwrapKey'] // key usages
254
+ );
255
+
256
+ // Wrap the key using the Web Crypto API.
257
+ const wrappedKeyBuffer = await webCrypto.wrapKey(
258
+ 'raw', // output format
259
+ unwrappedCryptoKey, // key to wrap
260
+ encryptionCryptoKey, // wrapping key
261
+ 'AES-KW' // algorithm identifier
262
+ );
263
+
264
+ // Convert from ArrayBuffer to Uint8Array.
265
+ const wrappedKeyBytes = new Uint8Array(wrappedKeyBuffer);
266
+
267
+ return wrappedKeyBytes;
268
+ }
269
+ }
@@ -1,6 +1,8 @@
1
+ import type { TypedArray } from '@noble/hashes/utils';
2
+
3
+ import { concatBytes } from '@noble/hashes/utils';
1
4
  import { sha256 } from '@noble/hashes/sha256';
2
5
  import { Convert, universalTypeOf } from '@enbox/common';
3
- import { TypedArray, concatBytes } from '@noble/hashes/utils';
4
6
 
5
7
  /**
6
8
  * ConcatKDF FixedInfo Parameters.
@@ -45,7 +47,7 @@ export type ConcatKdfFixedInfo = {
45
47
  * to the key agreement.
46
48
  */
47
49
  suppPrivInfo?: string | TypedArray;
48
- }
50
+ };
49
51
 
50
52
  /**
51
53
  * An implementation of the Concatenation Key Derivation Function (ConcatKDF)
@@ -1,5 +1,5 @@
1
1
  import { Convert } from '@enbox/common';
2
- import { ed25519, edwardsToMontgomeryPub, edwardsToMontgomeryPriv, x25519 } from '@noble/curves/ed25519';
2
+ import { ed25519, edwardsToMontgomeryPriv, edwardsToMontgomeryPub, x25519 } from '@noble/curves/ed25519';
3
3
 
4
4
  import type { Jwk } from '../jose/jwk.js';
5
5
  import type { ComputePublicKeyParams, GetPublicKeyParams, SignParams, VerifyParams } from '../types/params-direct.js';
@@ -86,7 +86,7 @@ export class Ed25519 {
86
86
  privateKeyBytes: Uint8Array;
87
87
  }): Promise<Jwk> {
88
88
  // Derive the public key from the private key.
89
- const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
89
+ const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
90
90
 
91
91
  // Construct the private key in JWK format.
92
92
  const privateKey: Jwk = {
@@ -167,10 +167,10 @@ export class Ed25519 {
167
167
  ComputePublicKeyParams
168
168
  ): Promise<Jwk> {
169
169
  // Convert the provided private key to a byte array.
170
- const privateKeyBytes = await Ed25519.privateKeyToBytes({ privateKey: key });
170
+ const privateKeyBytes = await Ed25519.privateKeyToBytes({ privateKey: key });
171
171
 
172
172
  // Derive the public key from the private key.
173
- const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
173
+ const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes);
174
174
 
175
175
  // Construct the public key in JWK format.
176
176
  const publicKey: Jwk = {
@@ -355,7 +355,7 @@ export class Ed25519 {
355
355
  }
356
356
 
357
357
  // Remove the private key property ('d') and make a shallow copy of the provided key.
358
- let { d, ...publicKey } = key;
358
+ const { d, ...publicKey } = key;
359
359
 
360
360
  // If the key ID is undefined, set it to the JWK thumbprint.
361
361
  publicKey.kid ??= await computeJwkThumbprint({ jwk: publicKey });
@@ -502,7 +502,7 @@ export class Ed25519 {
502
502
  // Check if points are on the Twisted Edwards curve.
503
503
  point.assertValidity();
504
504
 
505
- } catch(error: any) {
505
+ } catch {
506
506
  return false;
507
507
  }
508
508
 
@@ -0,0 +1,121 @@
1
+ import type { DeriveKeyBytesParams } from '../types/params-direct.js';
2
+
3
+ import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto';
4
+
5
+ import { Convert } from '@enbox/common';
6
+
7
+ /**
8
+ * The object that should be passed into `Hkdf.deriveKeyBytes()`, when using the HKDF algorithm.
9
+ */
10
+ export type HkdfParams = {
11
+ /**
12
+ * A string representing the digest algorithm to use. This may be one of:
13
+ * - 'SHA-256'
14
+ * - 'SHA-384'
15
+ * - 'SHA-512'
16
+ */
17
+ hash: 'SHA-256' | 'SHA-384' | 'SHA-512';
18
+
19
+ /**
20
+ * The salt value to use in the derivation process.
21
+ *
22
+ * Ideally, the salt is a random or pseudo-random value with the same length as the output of the
23
+ * digest function. Unlike the input key material passed into deriveKey(), salt does not need to
24
+ * be kept secret.
25
+ *
26
+ * Note: The {@link https://datatracker.ietf.org/doc/html/rfc5869 | HKDF specification} states
27
+ * that adding salt "adds significantly to the strength of HKDF".
28
+ */
29
+ salt: string | Uint8Array;
30
+
31
+ /**
32
+ * Optional application-specific information to use in the HKDF.
33
+ *
34
+ * If given, this value is used to bind the derived key to application-specific contextual
35
+ * information. This makes it possible to derive different keys for different contexts while using
36
+ * the same input key material.
37
+ *
38
+ * If not provided, the `info` value is set to an empty array.
39
+ *
40
+ * Note: It is important that the `info` value be independent and unrelated to the input key
41
+ * material.
42
+ */
43
+ info?: string | Uint8Array,
44
+ };
45
+
46
+ /**
47
+ * The `Hkdf` class provides an interface for HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
48
+ * as defined in RFC 5869.
49
+ *
50
+ * Note: The `baseKeyBytes` that will be the input key material for HKDF should be a high-entropy secret
51
+ * value, such as a cryptographic key. It should be kept confidential and not be derived from a
52
+ * low-entropy value, such as a password.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const info = new Uint8Array([...]);
57
+ * const derivedKeyBytes = await Hkdf.deriveKeyBytes({
58
+ * baseKeyBytes: new Uint8Array([...]), // Input keying material
59
+ * hash: 'SHA-256', // The hash function to use ('SHA-256', 'SHA-384', 'SHA-512')
60
+ * salt: new Uint8Array([...]), // The salt value
61
+ * info: new Uint8Array([...]), // Optional application-specific information
62
+ * length: 256 // The length of the derived key in bits
63
+ * });
64
+ * ```
65
+ */
66
+ export class Hkdf {
67
+ /**
68
+ * Derives a key using the HMAC-based Extract-and-Expand Key Derivation Function (HKDF).
69
+ *
70
+ * This method generates a derived key using a hash function from input keying material given as
71
+ * `baseKeyBytes`. The length of the derived key can be specified. Optionally, it can also use a salt
72
+ * and info for the derivation process.
73
+ *
74
+ * HKDF is useful in various cryptographic applications and protocols, especially when
75
+ * there's a need to derive multiple keys from a single source of key material.
76
+ *
77
+ * Note: The `baseKeyBytes` that will be the input key material for HKDF should be a high-entropy
78
+ * secret value, such as a cryptographic key. It should be kept confidential and not be derived
79
+ * from a low-entropy value, such as a password.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const info = new Uint8Array([...]);
84
+ * const derivedKeyBytes = await Hkdf.deriveKeyBytes({
85
+ * baseKeyBytes: new Uint8Array([...]), // Input keying material
86
+ * hash: 'SHA-256', // The hash function to use ('SHA-256', 'SHA-384', 'SHA-512')
87
+ * salt: new Uint8Array([...]), // The salt value
88
+ * info: new Uint8Array([...]), // Optional application-specific information
89
+ * length: 256 // The length of the derived key in bits
90
+ * });
91
+ * ```
92
+ *
93
+ * @param params - The parameters for key derivation.
94
+ * @returns A Promise that resolves to the derived key as a byte array.
95
+ */
96
+ public static async deriveKeyBytes({ baseKeyBytes, length, hash, salt, info = new Uint8Array() }:
97
+ DeriveKeyBytesParams & HkdfParams
98
+ ): Promise<Uint8Array> {
99
+ // Get the Web Crypto API interface.
100
+ const webCrypto = getWebcryptoSubtle() as SubtleCrypto;
101
+
102
+ // Import the baseKeyBytes into the Web Crypto API to use for the key derivation operation.
103
+ const webCryptoKey = await webCrypto.importKey('raw', baseKeyBytes, { name: 'HKDF' }, false, ['deriveBits']);
104
+
105
+ // Convert the salt and info to Uint8Array if they are provided as strings.
106
+ const saltBytes = typeof salt === 'string' ? Convert.string(salt).toUint8Array() : salt;
107
+ const infoBytes = typeof info === 'string' ? Convert.string(info).toUint8Array() : info;
108
+
109
+ // Derive the bytes using the Web Crypto API.
110
+ const derivedKeyBuffer = await webCrypto.deriveBits(
111
+ { name: 'HKDF', hash, salt: saltBytes, info: infoBytes },
112
+ webCryptoKey,
113
+ length
114
+ );
115
+
116
+ // Convert from ArrayBuffer to Uint8Array.
117
+ const derivedKeyBytes = new Uint8Array(derivedKeyBuffer);
118
+
119
+ return derivedKeyBytes;
120
+ }
121
+ }
@@ -1,5 +1,38 @@
1
+ import type { DeriveKeyBytesParams } from '../types/params-direct.js';
2
+
3
+ import { getWebcryptoSubtle } from '@noble/ciphers/webcrypto';
4
+
1
5
  import { crypto } from '@noble/hashes/crypto';
2
6
 
7
+ /**
8
+ * The object that should be passed into `Pbkdf2.deriveKeyBytes()`, when using the PBKDF2 algorithm.
9
+ */
10
+ export interface Pbkdf2Params {
11
+ /**
12
+ * A string representing the digest algorithm to use. This may be one of:
13
+ * - 'SHA-256'
14
+ * - 'SHA-384'
15
+ * - 'SHA-512'
16
+ */
17
+ hash: 'SHA-256' | 'SHA-384' | 'SHA-512';
18
+
19
+ /**
20
+ * The salt value to use in the derivation process, as a Uint8Array. This should be a random or
21
+ * pseudo-random value of at least 16 bytes. Unlike the `password`, `salt` does not need to be
22
+ * kept secret.
23
+ */
24
+ salt: Uint8Array;
25
+
26
+ /**
27
+ * A `Number` representing the number of iterations the hash function will be executed in
28
+ * `deriveKey()`. This impacts the computational cost of the `deriveKey()` operation, making it
29
+ * more resistant to dictionary attacks. The higher the number, the more secure, but also slower,
30
+ * the operation. Choose a value that balances security needs and performance for your
31
+ * application.
32
+ */
33
+ iterations: number;
34
+ }
35
+
3
36
  /**
4
37
  * The object that should be passed into `Pbkdf2.deriveKey()`, when using the PBKDF2 algorithm.
5
38
  */
@@ -119,4 +152,62 @@ export class Pbkdf2 {
119
152
 
120
153
  return derivedKey;
121
154
  }
155
+
156
+ /**
157
+ * Derives cryptographic key bytes from base key material using the PBKDF2 algorithm.
158
+ *
159
+ * @remarks
160
+ * This method is similar to {@link Pbkdf2.deriveKey | `deriveKey()`} but accepts
161
+ * raw key bytes (`baseKeyBytes`) instead of a password. It is intended for use cases
162
+ * where the input key material is already available as a byte array.
163
+ *
164
+ * Notes:
165
+ * - The `baseKeyBytes` that will be the input key material for PBKDF2 is expected to be a
166
+ * low-entropy value, such as a password or passphrase. It should be kept confidential.
167
+ * - In 2023,
168
+ * {@link https://web.archive.org/web/20230123232056/https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
169
+ * | OWASP recommended}
170
+ * a minimum of 600,000 iterations for PBKDF2-HMAC-SHA256 and 210,000 for PBKDF2-HMAC-SHA512.
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * const derivedKeyBytes = await Pbkdf2.deriveKeyBytes({
175
+ * baseKeyBytes: new TextEncoder().encode('password'),
176
+ * hash: 'SHA-256',
177
+ * salt: new Uint8Array([...]),
178
+ * iterations: 600_000,
179
+ * length: 256
180
+ * });
181
+ * ```
182
+ *
183
+ * @param params - The parameters for key derivation.
184
+ * @returns A Promise that resolves to the derived key as a byte array.
185
+ */
186
+ public static async deriveKeyBytes({ baseKeyBytes, hash, salt, iterations, length }:
187
+ DeriveKeyBytesParams & Pbkdf2Params
188
+ ): Promise<Uint8Array> {
189
+ // Get the Web Crypto API interface.
190
+ const webCrypto = getWebcryptoSubtle() as SubtleCrypto;
191
+
192
+ // Import the password as a raw key for use with the Web Crypto API.
193
+ const webCryptoKey = await webCrypto.importKey(
194
+ 'raw', // key format is raw bytes
195
+ baseKeyBytes, // key data to import
196
+ { name: 'PBKDF2' }, // algorithm identifier
197
+ false, // key is not extractable
198
+ ['deriveBits'] // key usages
199
+ );
200
+
201
+ // Derive the bytes using the Web Crypto API.
202
+ const derivedKeyBuffer = await webCrypto.deriveBits(
203
+ { name: 'PBKDF2', hash, salt, iterations },
204
+ webCryptoKey,
205
+ length
206
+ );
207
+
208
+ // Convert from ArrayBuffer to Uint8Array.
209
+ const derivedKeyBytes = new Uint8Array(derivedKeyBuffer);
210
+
211
+ return derivedKeyBytes;
212
+ }
122
213
  }
@@ -1,9 +1,9 @@
1
1
  import type { AffinePoint } from '@noble/curves/abstract/weierstrass';
2
2
 
3
3
  import { Convert } from '@enbox/common';
4
- import { sha256 } from '@noble/hashes/sha256';
5
- import { secp256k1 } from '@noble/curves/secp256k1';
6
4
  import { numberToBytesBE } from '@noble/curves/abstract/utils';
5
+ import { secp256k1 } from '@noble/curves/secp256k1';
6
+ import { sha256 } from '@noble/hashes/sha256';
7
7
 
8
8
  import type { Jwk } from '../jose/jwk.js';
9
9
  import type { ComputePublicKeyParams, GetPublicKeyParams, SignParams, VerifyParams } from '../types/params-direct.js';
@@ -304,7 +304,7 @@ export class Secp256k1 {
304
304
  ComputePublicKeyParams
305
305
  ): Promise<Jwk> {
306
306
  // Convert the provided private key to a byte array.
307
- const privateKeyBytes = await Secp256k1.privateKeyToBytes({ privateKey: key });
307
+ const privateKeyBytes = await Secp256k1.privateKeyToBytes({ privateKey: key });
308
308
 
309
309
  // Get the elliptic curve point (x and y coordinates) for the provided private key.
310
310
  const point = await Secp256k1.getCurvePoint({ keyBytes: privateKeyBytes });
@@ -357,7 +357,7 @@ export class Secp256k1 {
357
357
  // into a single byte array.
358
358
  const compactSignature = signatureObject.toCompactRawBytes();
359
359
 
360
- return compactSignature;
360
+ return compactSignature;
361
361
  }
362
362
 
363
363
  /**
@@ -468,7 +468,7 @@ export class Secp256k1 {
468
468
  }
469
469
 
470
470
  // Remove the private key property ('d') and make a shallow copy of the provided key.
471
- let { d, ...publicKey } = key;
471
+ const { d, ...publicKey } = key;
472
472
 
473
473
  // If the key ID is undefined, set it to the JWK thumbprint.
474
474
  publicKey.kid ??= await computeJwkThumbprint({ jwk: publicKey });
@@ -732,7 +732,7 @@ export class Secp256k1 {
732
732
  // Check if points are on the Short Weierstrass curve.
733
733
  point.assertValidity();
734
734
 
735
- } catch(error: any) {
735
+ } catch {
736
736
  return false;
737
737
  }
738
738
 
@@ -1,9 +1,9 @@
1
1
  import type { AffinePoint } from '@noble/curves/abstract/weierstrass';
2
2
 
3
3
  import { Convert } from '@enbox/common';
4
- import { sha256 } from '@noble/hashes/sha256';
5
- import { secp256r1 } from '@noble/curves/p256';
6
4
  import { numberToBytesBE } from '@noble/curves/abstract/utils';
5
+ import { secp256r1 } from '@noble/curves/p256';
6
+ import { sha256 } from '@noble/hashes/sha256';
7
7
 
8
8
  import type { Jwk } from '../jose/jwk.js';
9
9
  import type { ComputePublicKeyParams, GetPublicKeyParams, SignParams, VerifyParams } from '../types/params-direct.js';
@@ -304,7 +304,7 @@ export class Secp256r1 {
304
304
  ComputePublicKeyParams
305
305
  ): Promise<Jwk> {
306
306
  // Convert the provided private key to a byte array.
307
- const privateKeyBytes = await Secp256r1.privateKeyToBytes({ privateKey: key });
307
+ const privateKeyBytes = await Secp256r1.privateKeyToBytes({ privateKey: key });
308
308
 
309
309
  // Get the elliptic curve point (x and y coordinates) for the provided private key.
310
310
  const point = await Secp256r1.getCurvePoint({ keyBytes: privateKeyBytes });
@@ -357,7 +357,7 @@ export class Secp256r1 {
357
357
  // into a single byte array.
358
358
  const compactSignature = signatureObject.toCompactRawBytes();
359
359
 
360
- return compactSignature;
360
+ return compactSignature;
361
361
  }
362
362
 
363
363
  /**
@@ -468,7 +468,7 @@ export class Secp256r1 {
468
468
  }
469
469
 
470
470
  // Remove the private key property ('d') and make a shallow copy of the provided key.
471
- let { d, ...publicKey } = key;
471
+ const { d, ...publicKey } = key;
472
472
 
473
473
  // If the key ID is undefined, set it to the JWK thumbprint.
474
474
  publicKey.kid ??= await computeJwkThumbprint({ jwk: publicKey });
@@ -732,7 +732,7 @@ export class Secp256r1 {
732
732
  // Check if points are on the Short Weierstrass curve.
733
733
  point.assertValidity();
734
734
 
735
- } catch(error: any) {
735
+ } catch {
736
736
  return false;
737
737
  }
738
738
 
@@ -79,7 +79,7 @@ export class X25519 {
79
79
  privateKeyBytes: Uint8Array;
80
80
  }): Promise<Jwk> {
81
81
  // Derive the public key from the private key.
82
- const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
82
+ const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
83
83
 
84
84
  // Construct the private key in JWK format.
85
85
  const privateKey: Jwk = {
@@ -168,7 +168,7 @@ export class X25519 {
168
168
  ComputePublicKeyParams
169
169
  ): Promise<Jwk> {
170
170
  // Convert the provided private key to a byte array.
171
- const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey: key });
171
+ const privateKeyBytes = await X25519.privateKeyToBytes({ privateKey: key });
172
172
 
173
173
  // Derive the public key from the private key.
174
174
  const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
@@ -260,7 +260,7 @@ export class X25519 {
260
260
  }
261
261
 
262
262
  // Remove the private key property ('d') and make a shallow copy of the provided key.
263
- let { d, ...publicKey } = key;
263
+ const { d, ...publicKey } = key;
264
264
 
265
265
  // If the key ID is undefined, set it to the JWK thumbprint.
266
266
  publicKey.kid ??= await computeJwkThumbprint({ jwk: publicKey });