cipher-kit 2.0.0-beta.2 → 2.0.0-beta.4
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/dist/{chunk-SUGN4VDZ.js → chunk-LHB5NXYW.js} +85 -77
- package/dist/chunk-LHB5NXYW.js.map +1 -0
- package/dist/{chunk-AEQNI5EZ.js → chunk-N5T4PNG7.js} +75 -72
- package/dist/chunk-N5T4PNG7.js.map +1 -0
- package/dist/{chunk-3XGARINH.cjs → chunk-OL2AIXWK.cjs} +156 -152
- package/dist/chunk-OL2AIXWK.cjs.map +1 -0
- package/dist/{chunk-RXXVBCWL.cjs → chunk-UIV6DG54.cjs} +37 -38
- package/dist/chunk-UIV6DG54.cjs.map +1 -0
- package/dist/{chunk-WIZT7AYM.cjs → chunk-XIWV7XVI.cjs} +171 -162
- package/dist/chunk-XIWV7XVI.cjs.map +1 -0
- package/dist/{chunk-LOJKJJX5.js → chunk-YMNOTRET.js} +34 -37
- package/dist/chunk-YMNOTRET.js.map +1 -0
- package/dist/export-D1Vh79Qw.d.ts +417 -0
- package/dist/export-DGrELdz_.d.ts +416 -0
- package/dist/export-DXRl-ncG.d.cts +417 -0
- package/dist/export-Gd8hafl6.d.cts +416 -0
- package/dist/index.cjs +14 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -4
- package/dist/index.d.ts +52 -4
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +35 -35
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/validate-Cb7IOrPo.d.cts +373 -0
- package/dist/validate-Cb7IOrPo.d.ts +373 -0
- package/dist/web-api.cjs +35 -35
- package/dist/web-api.d.cts +2 -2
- package/dist/web-api.d.ts +2 -2
- package/dist/web-api.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-3XGARINH.cjs.map +0 -1
- package/dist/chunk-AEQNI5EZ.js.map +0 -1
- package/dist/chunk-LOJKJJX5.js.map +0 -1
- package/dist/chunk-RXXVBCWL.cjs.map +0 -1
- package/dist/chunk-SUGN4VDZ.js.map +0 -1
- package/dist/chunk-WIZT7AYM.cjs.map +0 -1
- package/dist/export-Bq9tslUU.d.ts +0 -252
- package/dist/export-C0WDJZUy.d.ts +0 -251
- package/dist/export-LPOfeH2z.d.cts +0 -251
- package/dist/export-RD2Af4CJ.d.cts +0 -252
- package/dist/validate-CS4PFmY1.d.cts +0 -159
- package/dist/validate-CS4PFmY1.d.ts +0 -159
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __export, $fmtResultErr, $ok, $err, $fmtError, $isStr, ENCRYPTION_ALGORITHMS, DIGEST_ALGORITHMS,
|
|
1
|
+
import { __export, $isSecretKey, $fmtResultErr, $ok, $err, $fmtError, title, $isStr, ENCRYPTION_ALGORITHMS, DIGEST_ALGORITHMS, CIPHER_ENCODING, matchPattern, $stringifyObj, $parseToObj, ENCODING } from './chunk-YMNOTRET.js';
|
|
2
2
|
|
|
3
3
|
// src/web/kit.ts
|
|
4
4
|
var kit_exports = {};
|
|
@@ -14,6 +14,7 @@ __export(kit_exports, {
|
|
|
14
14
|
generateUuid: () => generateUuid,
|
|
15
15
|
hash: () => hash,
|
|
16
16
|
hashPassword: () => hashPassword,
|
|
17
|
+
isWebSecretKey: () => isWebSecretKey,
|
|
17
18
|
tryConvertBytesToStr: () => tryConvertBytesToStr,
|
|
18
19
|
tryConvertEncoding: () => tryConvertEncoding,
|
|
19
20
|
tryConvertStrToBytes: () => tryConvertStrToBytes,
|
|
@@ -38,7 +39,7 @@ function $convertStrToBytes(data, inputEncoding = "utf8") {
|
|
|
38
39
|
desc: "Data must be a non-empty string"
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
|
-
if (!
|
|
42
|
+
if (!ENCODING.includes(inputEncoding)) {
|
|
42
43
|
return $err({
|
|
43
44
|
msg: `Crypto Web API - String to Bytes: Unsupported encoding: ${inputEncoding}`,
|
|
44
45
|
desc: "Use base64, base64url, hex, utf8, or latin1"
|
|
@@ -58,7 +59,7 @@ function $convertBytesToStr(data, outputEncoding = "utf8") {
|
|
|
58
59
|
desc: "Data must be an ArrayBuffer or Uint8Array"
|
|
59
60
|
});
|
|
60
61
|
}
|
|
61
|
-
if (!
|
|
62
|
+
if (!ENCODING.includes(outputEncoding)) {
|
|
62
63
|
return $err({
|
|
63
64
|
msg: `Crypto Web API - Bytes to String: Unsupported encoding: ${outputEncoding}`,
|
|
64
65
|
desc: "Use base64, base64url, hex, utf8, or latin1"
|
|
@@ -79,7 +80,7 @@ function $convertEncoding(data, from, to) {
|
|
|
79
80
|
desc: "Data must be a non-empty string"
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
|
-
if (!
|
|
83
|
+
if (!ENCODING.includes(from) || !ENCODING.includes(to)) {
|
|
83
84
|
return $err({
|
|
84
85
|
msg: `Crypto Web API - Convert Format: Unsupported encoding: from ${from} to ${to}`,
|
|
85
86
|
desc: "Use base64, base64url, hex, utf8, or latin1"
|
|
@@ -157,38 +158,38 @@ function $generateUuid() {
|
|
|
157
158
|
try {
|
|
158
159
|
return $ok(crypto.randomUUID());
|
|
159
160
|
} catch (error) {
|
|
160
|
-
return $err({ msg: "
|
|
161
|
+
return $err({ msg: `${title("web", "UUID Generation")}: Failed to generate UUID`, desc: $fmtError(error) });
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
async function $createSecretKey(secret, options = {}) {
|
|
164
165
|
if (!$isStr(secret)) {
|
|
165
|
-
return $err({ msg: "
|
|
166
|
+
return $err({ msg: `${title("web", "Key Generation")}: Empty Secret`, desc: "Secret must be a non-empty string" });
|
|
166
167
|
}
|
|
167
168
|
const algorithm = options.algorithm ?? "aes256gcm";
|
|
168
169
|
if (!(algorithm in ENCRYPTION_ALGORITHMS)) {
|
|
169
170
|
return $err({
|
|
170
|
-
msg:
|
|
171
|
+
msg: `${title("web", "Key Generation")}: Unsupported algorithm: ${algorithm}`,
|
|
171
172
|
desc: `Supported algorithms are: ${Object.keys(ENCRYPTION_ALGORITHMS).join(", ")}`
|
|
172
173
|
});
|
|
173
174
|
}
|
|
174
175
|
const digest = options.digest ?? "sha256";
|
|
175
176
|
if (!(digest in DIGEST_ALGORITHMS)) {
|
|
176
177
|
return $err({
|
|
177
|
-
msg:
|
|
178
|
+
msg: `${title("web", "Key Generation")}: Unsupported digest: ${digest}`,
|
|
178
179
|
desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(", ")}`
|
|
179
180
|
});
|
|
180
181
|
}
|
|
181
182
|
const salt = options.salt ?? "cipher-kit-salt";
|
|
182
183
|
if (!$isStr(salt, 8)) {
|
|
183
184
|
return $err({
|
|
184
|
-
msg: "
|
|
185
|
+
msg: `${title("web", "Key Generation")}: Weak salt`,
|
|
185
186
|
desc: "Salt must be a non-empty string with at least 8 characters"
|
|
186
187
|
});
|
|
187
188
|
}
|
|
188
189
|
const info = options.info ?? "cipher-kit";
|
|
189
190
|
if (!$isStr(info)) {
|
|
190
191
|
return $err({
|
|
191
|
-
msg: "
|
|
192
|
+
msg: `${title("web", "Key Generation")}: Invalid info`,
|
|
192
193
|
desc: "Info must be a non-empty string"
|
|
193
194
|
});
|
|
194
195
|
}
|
|
@@ -213,13 +214,13 @@ async function $createSecretKey(secret, options = {}) {
|
|
|
213
214
|
const secretKey = Object.freeze({
|
|
214
215
|
platform: "web",
|
|
215
216
|
digest,
|
|
216
|
-
|
|
217
|
+
algorithm,
|
|
217
218
|
key
|
|
218
219
|
});
|
|
219
220
|
return $ok({ result: secretKey });
|
|
220
221
|
} catch (error) {
|
|
221
222
|
return $err({
|
|
222
|
-
msg: "
|
|
223
|
+
msg: `${title("web", "Key Generation")}: Failed to create secret key`,
|
|
223
224
|
desc: $fmtError(error)
|
|
224
225
|
});
|
|
225
226
|
}
|
|
@@ -227,87 +228,91 @@ async function $createSecretKey(secret, options = {}) {
|
|
|
227
228
|
async function $encrypt(data, secretKey, options = {}) {
|
|
228
229
|
if (!$isStr(data)) {
|
|
229
230
|
return $err({
|
|
230
|
-
msg: "
|
|
231
|
+
msg: `${title("web", "Encryption")}: Empty data for encryption`,
|
|
231
232
|
desc: "Data must be a non-empty string"
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
if (!ENCODINGS.includes(inputEncoding) || !ENCODINGS.includes(outputEncoding)) {
|
|
235
|
+
const encoding = options.encoding ?? "base64url";
|
|
236
|
+
if (!CIPHER_ENCODING.includes(encoding)) {
|
|
237
237
|
return $err({
|
|
238
|
-
msg:
|
|
239
|
-
desc: "Use base64, base64url,
|
|
238
|
+
msg: `${title("web", "Encryption")}: Unsupported output encoding: ${encoding}`,
|
|
239
|
+
desc: "Use base64, base64url, or hex"
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
|
-
|
|
242
|
+
const injectedKey = $isSecretKey(secretKey, "web");
|
|
243
|
+
if (!injectedKey) {
|
|
243
244
|
return $err({
|
|
244
|
-
msg: "
|
|
245
|
+
msg: `${title("web", "Encryption")}: Invalid Secret Key`,
|
|
245
246
|
desc: "Expected a Web SecretKey"
|
|
246
247
|
});
|
|
247
248
|
}
|
|
248
|
-
const { result, error } = $convertStrToBytes(data,
|
|
249
|
+
const { result, error } = $convertStrToBytes(data, "utf8");
|
|
249
250
|
if (error) return $err(error);
|
|
250
251
|
try {
|
|
251
|
-
const iv = crypto.getRandomValues(new Uint8Array(
|
|
252
|
-
const cipherWithTag = await crypto.subtle.encrypt(
|
|
253
|
-
|
|
254
|
-
|
|
252
|
+
const iv = crypto.getRandomValues(new Uint8Array(injectedKey.injected.ivLength));
|
|
253
|
+
const cipherWithTag = await crypto.subtle.encrypt(
|
|
254
|
+
{ name: injectedKey.injected.web, iv },
|
|
255
|
+
injectedKey.key,
|
|
256
|
+
result
|
|
257
|
+
);
|
|
258
|
+
const ivStr = $convertBytesToStr(iv, encoding);
|
|
259
|
+
const cipherStr = $convertBytesToStr(cipherWithTag, encoding);
|
|
255
260
|
if (ivStr.error || cipherStr.error) {
|
|
256
261
|
return $err({
|
|
257
|
-
msg: "
|
|
258
|
-
desc: `Conversion error: ${ivStr.error || cipherStr.error}`
|
|
262
|
+
msg: `${title("web", "Encryption")}: Failed to convert IV or encrypted data`,
|
|
263
|
+
desc: `Conversion error: ${$fmtResultErr(ivStr.error || cipherStr.error)}`
|
|
259
264
|
});
|
|
260
265
|
}
|
|
261
266
|
return $ok(`${ivStr.result}.${cipherStr.result}.`);
|
|
262
267
|
} catch (error2) {
|
|
263
|
-
return $err({ msg: "
|
|
268
|
+
return $err({ msg: `${title("web", "Encryption")}: Failed to encrypt data`, desc: $fmtError(error2) });
|
|
264
269
|
}
|
|
265
270
|
}
|
|
266
271
|
async function $decrypt(encrypted, secretKey, options = {}) {
|
|
267
272
|
if (matchPattern(encrypted, "web") === false) {
|
|
268
273
|
return $err({
|
|
269
|
-
msg: "
|
|
274
|
+
msg: `${title("web", "Decryption")}: Invalid encrypted data format`,
|
|
270
275
|
desc: 'Encrypted data must be in the format "iv.cipherWithTag."'
|
|
271
276
|
});
|
|
272
277
|
}
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
if (!ENCODINGS.includes(inputEncoding) || !ENCODINGS.includes(outputEncoding)) {
|
|
278
|
+
const encoding = options.encoding ?? "base64url";
|
|
279
|
+
if (!CIPHER_ENCODING.includes(encoding)) {
|
|
276
280
|
return $err({
|
|
277
|
-
msg:
|
|
278
|
-
desc: "Use base64, base64url,
|
|
281
|
+
msg: `${title("web", "Decryption")}: Unsupported input encoding: ${encoding}`,
|
|
282
|
+
desc: "Use base64, base64url, or hex"
|
|
279
283
|
});
|
|
280
284
|
}
|
|
281
285
|
const [iv, encryptedWithTag] = encrypted.split(".", 3);
|
|
282
286
|
if (!$isStr(iv) || !$isStr(encryptedWithTag)) {
|
|
283
287
|
return $err({
|
|
284
|
-
msg: "
|
|
288
|
+
msg: `${title("web", "Decryption")}: Invalid encrypted data`,
|
|
285
289
|
desc: "Encrypted data must contain valid IV, encrypted and tag components"
|
|
286
290
|
});
|
|
287
291
|
}
|
|
288
|
-
|
|
292
|
+
const injectedKey = $isSecretKey(secretKey, "web");
|
|
293
|
+
if (!injectedKey) {
|
|
289
294
|
return $err({
|
|
290
|
-
msg: "
|
|
295
|
+
msg: `${title("web", "Decryption")}: Invalid Secret Key`,
|
|
291
296
|
desc: "Expected a Web SecretKey"
|
|
292
297
|
});
|
|
293
298
|
}
|
|
294
|
-
const ivBytes = $convertStrToBytes(iv,
|
|
295
|
-
const cipherWithTagBytes = $convertStrToBytes(encryptedWithTag,
|
|
299
|
+
const ivBytes = $convertStrToBytes(iv, encoding);
|
|
300
|
+
const cipherWithTagBytes = $convertStrToBytes(encryptedWithTag, encoding);
|
|
296
301
|
if (ivBytes.error || cipherWithTagBytes.error) {
|
|
297
302
|
return $err({
|
|
298
|
-
msg: "
|
|
299
|
-
desc: `Conversion error: ${ivBytes.error || cipherWithTagBytes.error}`
|
|
303
|
+
msg: `${title("web", "Decryption")}: Failed to convert IV or encrypted data`,
|
|
304
|
+
desc: `Conversion error: ${$fmtResultErr(ivBytes.error || cipherWithTagBytes.error)}`
|
|
300
305
|
});
|
|
301
306
|
}
|
|
302
307
|
try {
|
|
303
308
|
const decrypted = await crypto.subtle.decrypt(
|
|
304
|
-
{ name:
|
|
305
|
-
|
|
309
|
+
{ name: injectedKey.injected.web, iv: ivBytes.result },
|
|
310
|
+
injectedKey.key,
|
|
306
311
|
cipherWithTagBytes.result
|
|
307
312
|
);
|
|
308
313
|
return $convertBytesToStr(decrypted, "utf8");
|
|
309
314
|
} catch (error) {
|
|
310
|
-
return $err({ msg: "
|
|
315
|
+
return $err({ msg: `${title("web", "Decryption")}: Failed to decrypt data`, desc: $fmtError(error) });
|
|
311
316
|
}
|
|
312
317
|
}
|
|
313
318
|
async function $encryptObj(data, secretKey, options = {}) {
|
|
@@ -322,73 +327,72 @@ async function $decryptObj(encrypted, secretKey, options = {}) {
|
|
|
322
327
|
}
|
|
323
328
|
async function $hash(data, options = {}) {
|
|
324
329
|
if (!$isStr(data)) {
|
|
325
|
-
return $err({ msg: "
|
|
330
|
+
return $err({ msg: `${title("web", "Hashing")}: Empty data for hashing`, desc: "Data must be a non-empty string" });
|
|
326
331
|
}
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
if (!ENCODINGS.includes(inputEncoding) || !ENCODINGS.includes(outputEncoding)) {
|
|
332
|
+
const encoding = options.encoding ?? "base64url";
|
|
333
|
+
if (!CIPHER_ENCODING.includes(encoding)) {
|
|
330
334
|
return $err({
|
|
331
|
-
msg:
|
|
332
|
-
desc: "Use base64, base64url,
|
|
335
|
+
msg: `${title("web", "Hashing")}: Unsupported output encoding: ${encoding}`,
|
|
336
|
+
desc: "Use base64, base64url, or hex"
|
|
333
337
|
});
|
|
334
338
|
}
|
|
335
339
|
const digest = options.digest ?? "sha256";
|
|
336
340
|
if (!(digest in DIGEST_ALGORITHMS)) {
|
|
337
341
|
return $err({
|
|
338
|
-
msg:
|
|
342
|
+
msg: `${title("web", "Hashing")}: Unsupported digest: ${digest}`,
|
|
339
343
|
desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(", ")}`
|
|
340
344
|
});
|
|
341
345
|
}
|
|
342
346
|
const digestAlgo = DIGEST_ALGORITHMS[digest];
|
|
343
|
-
const { result, error } = $convertStrToBytes(data,
|
|
347
|
+
const { result, error } = $convertStrToBytes(data, "utf8");
|
|
344
348
|
if (error) return $err(error);
|
|
345
349
|
try {
|
|
346
350
|
const hashed = await crypto.subtle.digest(digestAlgo.web, result);
|
|
347
|
-
return $convertBytesToStr(hashed,
|
|
351
|
+
return $convertBytesToStr(hashed, encoding);
|
|
348
352
|
} catch (error2) {
|
|
349
|
-
return $err({ msg: "
|
|
353
|
+
return $err({ msg: `${title("web", "Hashing")}: Failed to hash data`, desc: $fmtError(error2) });
|
|
350
354
|
}
|
|
351
355
|
}
|
|
352
356
|
async function $hashPassword(password, options = {}) {
|
|
353
357
|
if (!$isStr(password)) {
|
|
354
358
|
return $err({
|
|
355
|
-
msg: "
|
|
359
|
+
msg: `${title("web", "Password Hashing")}: Empty password`,
|
|
356
360
|
desc: "Password must be a non-empty string"
|
|
357
361
|
});
|
|
358
362
|
}
|
|
359
363
|
const digest = options.digest ?? "sha512";
|
|
360
364
|
if (!(digest in DIGEST_ALGORITHMS)) {
|
|
361
365
|
return $err({
|
|
362
|
-
msg:
|
|
366
|
+
msg: `${title("web", "Password Hashing")}: Unsupported digest: ${digest}`,
|
|
363
367
|
desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(", ")}`
|
|
364
368
|
});
|
|
365
369
|
}
|
|
366
370
|
const digestAlgo = DIGEST_ALGORITHMS[digest];
|
|
367
|
-
const
|
|
368
|
-
if (!
|
|
371
|
+
const encoding = options.encoding ?? "base64url";
|
|
372
|
+
if (!CIPHER_ENCODING.includes(encoding)) {
|
|
369
373
|
return $err({
|
|
370
|
-
msg:
|
|
371
|
-
desc: "Use base64, base64url,
|
|
374
|
+
msg: `${title("web", "Password Hashing")}: Unsupported output encoding: ${encoding}`,
|
|
375
|
+
desc: "Use base64, base64url, or hex"
|
|
372
376
|
});
|
|
373
377
|
}
|
|
374
378
|
const saltLength = options.saltLength ?? 16;
|
|
375
379
|
if (typeof saltLength !== "number" || saltLength < 8) {
|
|
376
380
|
return $err({
|
|
377
|
-
msg: "
|
|
381
|
+
msg: `${title("web", "Password Hashing")}: Weak salt length`,
|
|
378
382
|
desc: "Salt length must be a number and at least 8 bytes (recommended 16)"
|
|
379
383
|
});
|
|
380
384
|
}
|
|
381
385
|
const iterations = options.iterations ?? 32e4;
|
|
382
386
|
if (typeof iterations !== "number" || iterations < 1e3) {
|
|
383
387
|
return $err({
|
|
384
|
-
msg: "
|
|
388
|
+
msg: `${title("web", "Password Hashing")}: Weak iteration count`,
|
|
385
389
|
desc: "Iterations must be a number and at least 1000 (recommended 320,000 or more)"
|
|
386
390
|
});
|
|
387
391
|
}
|
|
388
392
|
const keyLength = options.keyLength ?? 64;
|
|
389
393
|
if (typeof keyLength !== "number" || keyLength < 16) {
|
|
390
394
|
return $err({
|
|
391
|
-
msg: "
|
|
395
|
+
msg: `${title("web", "Password Hashing")}: Weak key length`,
|
|
392
396
|
desc: "Key length must be a number and at least 16 bytes (recommended 64)"
|
|
393
397
|
});
|
|
394
398
|
}
|
|
@@ -406,13 +410,13 @@ async function $hashPassword(password, options = {}) {
|
|
|
406
410
|
baseKey,
|
|
407
411
|
keyLength * 8
|
|
408
412
|
);
|
|
409
|
-
const saltStr = $convertBytesToStr(salt,
|
|
413
|
+
const saltStr = $convertBytesToStr(salt, encoding);
|
|
410
414
|
if (saltStr.error) return $err(saltStr.error);
|
|
411
|
-
const hashedPasswordStr = $convertBytesToStr(bits,
|
|
415
|
+
const hashedPasswordStr = $convertBytesToStr(bits, encoding);
|
|
412
416
|
if (hashedPasswordStr.error) return $err(hashedPasswordStr.error);
|
|
413
417
|
return $ok({ hash: hashedPasswordStr.result, salt: saltStr.result });
|
|
414
418
|
} catch (error) {
|
|
415
|
-
return $err({ msg: "
|
|
419
|
+
return $err({ msg: `${title("web", "Password Hashing")}: Failed to hash password`, desc: $fmtError(error) });
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
async function $verifyPassword(password, hashedPassword, salt, options = {}) {
|
|
@@ -420,15 +424,15 @@ async function $verifyPassword(password, hashedPassword, salt, options = {}) {
|
|
|
420
424
|
const digest = options.digest ?? "sha512";
|
|
421
425
|
if (!(digest in DIGEST_ALGORITHMS)) return false;
|
|
422
426
|
const digestAlgo = DIGEST_ALGORITHMS[digest];
|
|
423
|
-
const
|
|
424
|
-
if (!
|
|
427
|
+
const encoding = options.encoding ?? "base64url";
|
|
428
|
+
if (!CIPHER_ENCODING.includes(encoding)) return false;
|
|
425
429
|
const iterations = options.iterations ?? 32e4;
|
|
426
430
|
if (typeof iterations !== "number" || iterations < 1e3) return false;
|
|
427
431
|
const keyLength = options.keyLength ?? 64;
|
|
428
432
|
if (typeof keyLength !== "number" || keyLength < 16) return false;
|
|
429
|
-
const saltBytes = $convertStrToBytes(salt,
|
|
433
|
+
const saltBytes = $convertStrToBytes(salt, encoding);
|
|
430
434
|
if (saltBytes.error) return false;
|
|
431
|
-
const hashedPasswordBytes = $convertStrToBytes(hashedPassword,
|
|
435
|
+
const hashedPasswordBytes = $convertStrToBytes(hashedPassword, encoding);
|
|
432
436
|
if (hashedPasswordBytes.error) return false;
|
|
433
437
|
try {
|
|
434
438
|
const baseKey = await crypto.subtle.importKey(
|
|
@@ -450,18 +454,22 @@ async function $verifyPassword(password, hashedPassword, salt, options = {}) {
|
|
|
450
454
|
keyLength * 8
|
|
451
455
|
)
|
|
452
456
|
);
|
|
457
|
+
if (bits === void 0 || hashedPasswordBytes.result === void 0) return false;
|
|
453
458
|
if (bits.length !== hashedPasswordBytes.result.length) return false;
|
|
454
|
-
let
|
|
459
|
+
let diff = 0;
|
|
455
460
|
for (let i = 0; i < bits.length; i++) {
|
|
456
|
-
|
|
461
|
+
diff |= bits[i] ^ hashedPasswordBytes.result[i];
|
|
457
462
|
}
|
|
458
|
-
return
|
|
463
|
+
return diff === 0;
|
|
459
464
|
} catch {
|
|
460
465
|
return false;
|
|
461
466
|
}
|
|
462
467
|
}
|
|
463
468
|
|
|
464
469
|
// src/web/kit.ts
|
|
470
|
+
function isWebSecretKey(x) {
|
|
471
|
+
return $isSecretKey(x, "web") !== null;
|
|
472
|
+
}
|
|
465
473
|
function tryGenerateUuid() {
|
|
466
474
|
return $generateUuid();
|
|
467
475
|
}
|
|
@@ -482,7 +490,7 @@ async function tryEncrypt(data, secretKey, options = {}) {
|
|
|
482
490
|
return await $encrypt(data, secretKey, options);
|
|
483
491
|
}
|
|
484
492
|
async function encrypt(data, secretKey, options = {}) {
|
|
485
|
-
const { result, error } = await $encrypt(data, secretKey);
|
|
493
|
+
const { result, error } = await $encrypt(data, secretKey, options);
|
|
486
494
|
if (error) throw new Error($fmtResultErr(error));
|
|
487
495
|
return result;
|
|
488
496
|
}
|
|
@@ -554,6 +562,6 @@ function convertEncoding(data, from, to) {
|
|
|
554
562
|
return result;
|
|
555
563
|
}
|
|
556
564
|
|
|
557
|
-
export { convertBytesToStr, convertEncoding, convertStrToBytes, createSecretKey, decrypt, decryptObj, encrypt, encryptObj, generateUuid, hash, hashPassword, kit_exports, tryConvertBytesToStr, tryConvertEncoding, tryConvertStrToBytes, tryCreateSecretKey, tryDecrypt, tryDecryptObj, tryEncrypt, tryEncryptObj, tryGenerateUuid, tryHash, tryHashPassword, verifyPassword };
|
|
558
|
-
//# sourceMappingURL=chunk-
|
|
559
|
-
//# sourceMappingURL=chunk-
|
|
565
|
+
export { convertBytesToStr, convertEncoding, convertStrToBytes, createSecretKey, decrypt, decryptObj, encrypt, encryptObj, generateUuid, hash, hashPassword, isWebSecretKey, kit_exports, tryConvertBytesToStr, tryConvertEncoding, tryConvertStrToBytes, tryCreateSecretKey, tryDecrypt, tryDecryptObj, tryEncrypt, tryEncryptObj, tryGenerateUuid, tryHash, tryHashPassword, verifyPassword };
|
|
566
|
+
//# sourceMappingURL=chunk-LHB5NXYW.js.map
|
|
567
|
+
//# sourceMappingURL=chunk-LHB5NXYW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web/kit.ts","../src/web/web-encode.ts","../src/web/web-encrypt.ts"],"names":["error","hash"],"mappings":"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","file":"chunk-LHB5NXYW.js","sourcesContent":["import { $fmtResultErr, type Result } from '~/helpers/error';\r\nimport type {\r\n CreateSecretKeyOptions,\r\n DecryptOptions,\r\n Encoding,\r\n EncryptOptions,\r\n HashOptions,\r\n HashPasswordOptions,\r\n SecretKey,\r\n VerifyPasswordOptions,\r\n} from '~/helpers/types';\r\nimport { $isSecretKey } from '~/helpers/validate';\r\nimport { $convertBytesToStr, $convertEncoding, $convertStrToBytes } from './web-encode';\r\nimport {\r\n $createSecretKey,\r\n $decrypt,\r\n $decryptObj,\r\n $encrypt,\r\n $encryptObj,\r\n $generateUuid,\r\n $hash,\r\n $hashPassword,\r\n $verifyPassword,\r\n} from './web-encrypt';\r\n\r\n/**\r\n * Type guard to check if the provided value is a SecretKey object for Web (Deno, Bun, Cloudflare included) environment.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Checking if the key in your hand is the right kind of key for the lock.\r\n *\r\n * @param x - The value to check.\r\n * @returns True if the value is a SecretKey object for Web, false otherwise.\r\n */\r\nexport function isWebSecretKey(x: unknown): x is SecretKey<'web'> {\r\n return $isSecretKey(x, 'web') !== null;\r\n}\r\n\r\n/**\r\n * Safely generates a UUID (v4) (non-throwing).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * It's like giving your pet a name tag with a super random name made of numbers and letters.\r\n * The chance of two pets getting the same name tag is practically zero, and it's very hard to guess!\r\n *\r\n * @returns A `Result` containing the UUID string or an error.\r\n */\r\nexport function tryGenerateUuid(): Result<string> {\r\n return $generateUuid();\r\n}\r\n\r\n/**\r\n * Generates a UUID (v4) (throwing).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * It's like giving your pet a name tag with a super random name made of numbers and letters.\r\n * The chance of two pets getting the same name tag is practically zero, and it's very hard to guess!\r\n *\r\n * @returns A UUID string.\r\n * @throws {Error} If UUID generation fails.\r\n */\r\nexport function generateUuid(): string {\r\n const { result, error } = $generateUuid();\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely derives a `SecretKey` from the provided string for encryption/decryption (non-throwing).\r\n *\r\n * Uses HKDF to derive a key from the input string.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Imagine you want to create a special key for future use to lock your treasure box (data).\r\n * So, you stir in some secret ingredients (like salt and info) to make sure your key is one-of-a-kind.\r\n *\r\n * @param secret - The input string to derive the `SecretKey` from.\r\n * @param options.algorithm - The encryption algorithm to use (default: `'aes256gcm'`).\r\n * @param options.digest - The hash algorithm for HKDF (default: `'sha256'`).\r\n * @param options.salt - A salt string (default: `'cipher-kit-salt'`, must be ≥ 8 chars).\r\n * @param options.info - An info string (default: `'cipher-kit'`).\r\n * @returns A `Result` promise containing the derived `SecretKey` or an error.\r\n */\r\nexport async function tryCreateSecretKey(\r\n secret: string,\r\n options: CreateSecretKeyOptions = {},\r\n): Promise<Result<{ result: SecretKey<'web'> }>> {\r\n return await $createSecretKey(secret, options);\r\n}\r\n\r\n/**\r\n * Derives a `SecretKey` from the provided string for encryption/decryption (throwing).\r\n *\r\n * Uses HKDF to derive a key from the input string.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Imagine you want to create a special key for future use to lock your treasure box (data).\r\n * So, you stir in some secret ingredients (like salt and info) to make sure your key is one-of-a-kind.\r\n *\r\n * @param secret - The input string to derive the `SecretKey` from.\r\n * @param options.algorithm - The encryption algorithm to use (default: `'aes256gcm'`).\r\n * @param options.digest - The hash algorithm for HKDF (default: `'sha256'`).\r\n * @param options.salt - A salt string (default: `'cipher-kit-salt'`, must be ≥ 8 chars).\r\n * @param options.info - An info string (default: `'cipher-kit'`).\r\n * @returns A promise of the derived `SecretKey`.\r\n * @throws {Error} If key derivation fails.\r\n */\r\nexport async function createSecretKey(secret: string, options: CreateSecretKeyOptions = {}): Promise<SecretKey<'web'>> {\r\n const { result, error } = await $createSecretKey(secret, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely encrypts a UTF-8 string using the provided `SecretKey` (non-throwing).\r\n *\r\n * Output format: \"iv.cipherWithTag.\"\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You scramble a secret message with your special key,\r\n * creating a jumbled code that only someone with the right key can read.\r\n *\r\n * @param data - A UTF-8 string to encrypt.\r\n * @param secretKey - The `SecretKey` object used for encryption.\r\n * @param options.encoding - The encoding format for the output ciphertext (default: `'base64url'`).\r\n * @returns A `Result` promise containing the encrypted string in the specified format or an error.\r\n */\r\nexport async function tryEncrypt(\r\n data: string,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<Result<string>> {\r\n return await $encrypt(data, secretKey, options);\r\n}\r\n\r\n/**\r\n * Encrypts a UTF-8 string using the provided `SecretKey` (throwing).\r\n *\r\n * Output format: \"iv.cipherWithTag.\"\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You scramble a secret message with your special key,\r\n * creating a jumbled code that only someone with the right key can read.\r\n *\r\n * @param data - A UTF-8 string to encrypt.\r\n * @param secretKey - The `SecretKey` object used for encryption.\r\n * @param options.encoding - The encoding format for the output ciphertext (default: `'base64url'`).\r\n * @returns A promise with the encrypted string in the specified format.\r\n * @throws {Error} If the input data or key is invalid, or if encryption fails.\r\n */\r\nexport async function encrypt(\r\n data: string,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<string> {\r\n const { result, error } = await $encrypt(data, secretKey, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely decrypts the input string using the provided `SecretKey` (non-throwing).\r\n *\r\n * Expects input in the format \"iv.cipherWithTag.\" and returns the decrypted UTF-8 string.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You take a scrambled secret message and use your special key to unscramble it,\r\n * revealing the original message inside.\r\n *\r\n * @param encrypted - The input string to decrypt, in the format \"iv.cipherWithTag.\".\r\n * @param secretKey - The `SecretKey` object used for decryption.\r\n * @param options.encoding - The encoding format for the input ciphertext (default: `'base64url'`).\r\n * @returns A `Result` promise containing the decrypted UTF-8 string or an error.\r\n */\r\nexport async function tryDecrypt(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<Result<string>> {\r\n return await $decrypt(encrypted, secretKey, options);\r\n}\r\n\r\n/**\r\n * Decrypts the input string using the provided `SecretKey` (throwing).\r\n *\r\n * Expects input in the format \"iv.cipherWithTag.\" and returns the decrypted UTF-8 string.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You take a scrambled secret message and use your special key to unscramble it,\r\n * revealing the original message inside.\r\n *\r\n * @param encrypted - The input string to decrypt, in the format \"iv.cipherWithTag.\".\r\n * @param secretKey - The `SecretKey` object used for decryption.\r\n * @param options.encoding - The encoding format for the input ciphertext (default: `'base64url'`).\r\n * @returns A promise with the decrypted UTF-8 string.\r\n * @throws {Error} If the input data or key is invalid, or if decryption fails.\r\n */\r\nexport async function decrypt(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<string> {\r\n const { result, error } = await $decrypt(encrypted, secretKey, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely encrypts a plain object using the provided `SecretKey` (non-throwing).\r\n *\r\n * Only plain objects (POJOs) are accepted. Class instances, Maps, Sets, etc. are rejected.\r\n *\r\n * Output format: \"iv.cipherWithTag.\"\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Imagine you have a toy box (an object) that you want to keep secret.\r\n * So, you take a picture of your toy box (convert it to JSON), and scramble that with\r\n * your special key, creating a jumbled code that only someone with the right key can read.\r\n *\r\n * @param data - A plain object to encrypt.\r\n * @param secretKey - The `SecretKey` object used for encryption.\r\n * @param options.encoding - The encoding format for the output ciphertext (default: `'base64url'`).\r\n * @returns A `Result` promise containing the encrypted string in the specified format or an error.\r\n */\r\nexport async function tryEncryptObj<T extends object = Record<string, unknown>>(\r\n data: T,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<Result<string>> {\r\n return await $encryptObj(data, secretKey, options);\r\n}\r\n\r\n/**\r\n * Encrypts a plain object using the provided `SecretKey` (throwing).\r\n *\r\n * Only plain objects (POJOs) are accepted. Class instances, Maps, Sets, etc. are rejected.\r\n *\r\n * Output format: \"iv.cipherWithTag.\"\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Imagine you have a toy box (an object) that you want to keep secret.\r\n * So, you take a picture of your toy box (convert it to JSON), and scramble that with\r\n * your special key, creating a jumbled code that only someone with the right key can read.\r\n *\r\n * @param data - A plain object to encrypt.\r\n * @param secretKey - The `SecretKey` object used for encryption.\r\n * @param options.encoding - The encoding format for the output ciphertext (default: `'base64url'`).\r\n * @returns A promise with the encrypted string in the specified format.\r\n * @throws {Error} If the input data or key is invalid, or if encryption fails.\r\n */\r\nexport async function encryptObj<T extends object = Record<string, unknown>>(\r\n data: T,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<string> {\r\n const { result, error } = await $encryptObj(data, secretKey, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely decrypts an encrypted JSON string into an object (non-throwing).\r\n *\r\n * Expects input in the format `\"iv.cipherWithTag.\"` and returns a plain object.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You rebuild your toy box (an object) by unscrambling the jumbled code (encrypted text),\r\n * using your special key to open it.\r\n *\r\n * @template T - The expected shape of the decrypted object.\r\n * @param encrypted - The encrypted string (format: `\"iv.cipherWithTag.\"`).\r\n * @param secretKey - The `SecretKey` used for decryption.\r\n * @param options.encoding - Input ciphertext encoding (default: `'base64url'`).\r\n * @returns A `Result` promise with the decrypted object on success, or an error.\r\n */\r\nexport async function tryDecryptObj<T extends object = Record<string, unknown>>(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<Result<{ result: T }>> {\r\n return await $decryptObj<T>(encrypted, secretKey, options);\r\n}\r\n\r\n/**\r\n * Decrypts an encrypted JSON string into an object (throwing).\r\n *\r\n * Expects input in the format `\"iv.cipherWithTag.\"` and returns a plain object.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You rebuild your toy box (an object) by unscrambling the jumbled code (encrypted text),\r\n * using your special key to open it.\r\n *\r\n * @template T - The expected shape of the decrypted object.\r\n * @param encrypted - The encrypted string (format: `\"iv.cipherWithTag.\"`).\r\n * @param secretKey - The `SecretKey` used for decryption.\r\n * @param options.encoding - Input ciphertext encoding (default: `'base64url'`).\r\n * @returns A promise with the decrypted object.\r\n * @throws {Error} If decryption or JSON parsing fails.\r\n */\r\nexport async function decryptObj<T extends object = Record<string, unknown>>(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<T> {\r\n const { result, error } = await $decryptObj<T>(encrypted, secretKey, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely hashes a UTF-8 string (non-throwing).\r\n *\r\n * Uses the selected digest (default: `'sha256'`) and returns the hash\r\n * in the chosen encoding (default: `'base64url'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Like putting something in a blender and getting a smoothie, you can’t get the original ingredients back,\r\n * but the smoothie is always the same if you use the same ingredients.\r\n *\r\n * @param data - The input string to hash.\r\n * @param options.digest - Hash algorithm (`'sha256' | 'sha384' | 'sha512'`, default: `'sha256'`).\r\n * @param options.encoding - Output encoding (`'base64' | 'base64url' | 'hex'`, default: `'base64url'`).\r\n * @returns A `Result` promise with the hash string or an error.\r\n */\r\nexport async function tryHash(data: string, options: HashOptions = {}): Promise<Result<string>> {\r\n return await $hash(data, options);\r\n}\r\n\r\n/**\r\n * Hashes a UTF-8 string (throwing).\r\n *\r\n * Uses the selected digest (default: `'sha256'`) and returns the hash\r\n * in the chosen encoding (default: `'base64url'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * Like putting something in a blender and getting a smoothie, you can’t get the original ingredients back,\r\n * but the smoothie is always the same if you use the same ingredients.\r\n *\r\n * @param data - The input string to hash.\r\n * @param options.digest - Hash algorithm (`'sha256' | 'sha384' | 'sha512'`, default: `'sha256'`).\r\n * @param options.encoding - Output encoding (`'base64' | 'base64url' | 'hex'`, default: `'base64url'`).\r\n * @returns A promise of the hashed string.\r\n * @throws {Error} If input is invalid or hashing fails.\r\n */\r\nexport async function hash(data: string, options: HashOptions = {}): Promise<string> {\r\n const { result, error } = await $hash(data, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely hashes a password using PBKDF2 (non-throwing).\r\n *\r\n * Uses strong defaults (`sha512`, 320k iterations, 64-byte key, 16-byte salt) and\r\n * returns `{ hash, salt }` encoded (default: `'base64url'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * We take your password, mix in some random salt, and stir many times.\r\n * The result is a super-secret soup that’s hard to copy.\r\n *\r\n * @param password - The password to hash.\r\n * @param options.digest - Hash algorithm (`'sha256' | 'sha384' | 'sha512'`; default: `'sha512'`).\r\n * @param options.encoding - Output encoding (`'base64' | 'base64url' | 'hex'`; default: `'base64url'`).\r\n * @param options.saltLength - Length of the random salt in bytes (default: `16` bytes, min: `8` bytes).\r\n * @param options.iterations - Number of iterations (default: `320000`, min: `1000`).\r\n * @param options.keyLength - Length of the derived key in bytes (default: `64` bytes, min: `16` bytes).\r\n * @returns A `Result` promise with `{ hash, salt }` or an error.\r\n */\r\nexport async function tryHashPassword(\r\n password: string,\r\n options: HashPasswordOptions = {},\r\n): Promise<Result<{ hash: string; salt: string }>> {\r\n return await $hashPassword(password, options);\r\n}\r\n\r\n/**\r\n * Hashes a password using PBKDF2 (throwing).\r\n *\r\n * Uses strong defaults (`sha512`, 320k iterations, 64-byte key, 16-byte salt) and\r\n * returns `{ hash, salt }` encoded (default: `'base64url'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * We take your password, mix in some random salt, and stir many times.\r\n * The result is a super-secret soup that’s hard to copy.\r\n *\r\n * @param password - The password to hash.\r\n * @param options.digest - Hash algorithm (`'sha256' | 'sha384' | 'sha512'`; default: `'sha512'`).\r\n * @param options.encoding - Output encoding (`'base64' | 'base64url' | 'hex'`; default: `'base64url'`).\r\n * @param options.saltLength - Length of the random salt in bytes (default: `16` bytes, min: `8` bytes).\r\n * @param options.iterations - Number of iterations (default: `320000`, min: `1000`).\r\n * @param options.keyLength - Length of the derived key in bytes (default: `64` bytes, min: `16` bytes).\r\n * @returns A promise with `{ hash, salt }` for storage.\r\n * @throws {Error} If inputs are invalid or hashing fails.\r\n */\r\nexport async function hashPassword(\r\n password: string,\r\n options: HashPasswordOptions = {},\r\n): Promise<{ hash: string; salt: string }> {\r\n const { hash, salt, error } = await $hashPassword(password, options);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return { hash, salt };\r\n}\r\n\r\n/**\r\n * Verifies a password against a stored PBKDF2 hash (non-throwing).\r\n *\r\n * Re-derives the key using the same parameters and compares in constant time, to prevent timing attacks.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * We follow the same recipe as when we made the secret.\r\n * If the new soup tastes exactly the same, the password is correct.\r\n *\r\n * @param password - The plain password to verify.\r\n * @param hashedPassword - The stored hash (encoded).\r\n * @param salt - The stored salt (encoded).\r\n * @param options.digest - Hash algorithm used during hashing (`'sha256' | 'sha384' | 'sha512'`; default: `'sha512'`).\r\n * @param options.encoding - Encoding of the stored hash and salt (`'base64' | 'base64url' | 'hex'`; default: `'base64url'`).\r\n * @param options.iterations - Number of iterations used during hashing (default: `320000`).\r\n * @param options.keyLength - Length of the derived key in bytes used during hashing (default: `64` bytes).\r\n * @returns A promise of boolean, `true` if the password matches, otherwise `false`.\r\n */\r\nexport async function verifyPassword(\r\n password: string,\r\n hashedPassword: string,\r\n salt: string,\r\n options: VerifyPasswordOptions = {},\r\n): Promise<boolean> {\r\n return await $verifyPassword(password, hashedPassword, salt, options);\r\n}\r\n\r\n/**\r\n * Safely converts a string to a `Uint8Array` using the specified encoding (non-throwing).\r\n *\r\n * Supported encodings: `'base64' | 'base64url' | 'hex' | 'utf8' | 'latin1'` (default: `'utf8'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * This turns your words into tiny computer building blocks (bytes) so computers can work with them.\r\n *\r\n * @param data - The input string to convert.\r\n * @param inputEncoding - The encoding of the input string (default: `'utf8'`).\r\n * @returns A `Result` with `{ result: Uint8Array }` or an error.\r\n */\r\nexport function tryConvertStrToBytes(\r\n data: string,\r\n inputEncoding: Encoding = 'utf8',\r\n): Result<{ result: Uint8Array<ArrayBuffer> }> {\r\n return $convertStrToBytes(data, inputEncoding);\r\n}\r\n\r\n/**\r\n * Converts a string to a `Uint8Array` using the specified encoding (throwing).\r\n *\r\n * Supported encodings: `'base64' | 'base64url' | 'hex' | 'utf8' | 'latin1'` (default: `'utf8'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * This turns your words into tiny computer building blocks (bytes) so computers can work with them.\r\n *\r\n * @param data - The input string to convert.\r\n * @param inputEncoding - The encoding of the input string (default: `'utf8'`).\r\n * @returns A `Uint8Array` containing the bytes.\r\n * @throws {Error} If input is invalid or conversion fails.\r\n */\r\nexport function convertStrToBytes(data: string, inputEncoding: Encoding = 'utf8'): Uint8Array<ArrayBuffer> {\r\n const { result, error } = $convertStrToBytes(data, inputEncoding);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely converts a `Uint8Array` or `ArrayBuffer` to a string using the specified encoding (non-throwing).\r\n *\r\n * Supported encodings: `'base64' | 'base64url' | 'hex' | 'utf8' | 'latin1'` (default: `'utf8'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * This turns the tiny computer building blocks (bytes) back into a readable sentence.\r\n *\r\n * @param data - The `Uint8Array` or `ArrayBuffer` to convert.\r\n * @param outputEncoding - The output encoding (default: `'utf8'`).\r\n * @returns A `Result` with the string or an error.\r\n */\r\nexport function tryConvertBytesToStr(\r\n data: Uint8Array | ArrayBuffer,\r\n outputEncoding: Encoding = 'utf8',\r\n): Result<string> {\r\n return $convertBytesToStr(data, outputEncoding);\r\n}\r\n\r\n/**\r\n * Converts a `Uint8Array` or `ArrayBuffer` to a string using the specified encoding (throwing).\r\n *\r\n * Supported encodings: `'base64' | 'base64url' | 'hex' | 'utf8' | 'latin1'` (default: `'utf8'`).\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * This turns the tiny computer building blocks (bytes) back into a readable sentence.\r\n *\r\n * @param data - The `Uint8Array` or `ArrayBuffer` to convert.\r\n * @param outputEncoding - The output encoding (default: `'utf8'`).\r\n * @returns The encoded string.\r\n * @throws {Error} If input is invalid or conversion fails.\r\n */\r\nexport function convertBytesToStr(data: Uint8Array | ArrayBuffer, outputEncoding: Encoding = 'utf8'): string {\r\n const { result, error } = $convertBytesToStr(data, outputEncoding);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely converts text between encodings (non-throwing).\r\n *\r\n * Example: convert `'utf8'` text to `'base64url'`, or `'hex'` to `'utf8'`.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * It’s like translating your sentence from one alphabet to another.\r\n *\r\n * @param data - The input string to convert.\r\n * @param from - The current encoding of `data`.\r\n * @param to - The target encoding for `data`.\r\n * @returns A `Result` with a string or an error.\r\n */\r\nexport function tryConvertEncoding(data: string, from: Encoding, to: Encoding): Result<string> {\r\n return $convertEncoding(data, from, to);\r\n}\r\n\r\n/**\r\n * Converts text between encodings (throwing).\r\n *\r\n * Example: convert `'utf8'` text to `'base64url'`, or `'hex'` to `'utf8'`.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * It’s like translating your sentence from one alphabet to another.\r\n *\r\n * @param data - The input string to convert.\r\n * @param from - The current encoding of `data`.\r\n * @param to - The target encoding for `data`.\r\n * @returns The converted string.\r\n * @throws {Error} If encodings are invalid or conversion fails.\r\n */\r\nexport function convertEncoding(data: string, from: Encoding, to: Encoding): string {\r\n const { result, error } = $convertEncoding(data, from, to);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n","import { ENCODING } from '~/helpers/consts';\r\nimport { $err, $fmtError, $ok, type Result } from '~/helpers/error';\r\nimport type { Encoding } from '~/helpers/types';\r\nimport { $isStr } from '~/helpers/validate';\r\n\r\nexport const textEncoder = new TextEncoder();\r\nexport const textDecoder = new TextDecoder();\r\n\r\nexport function $convertStrToBytes(\r\n data: string,\r\n inputEncoding: Encoding = 'utf8',\r\n): Result<{ result: Uint8Array<ArrayBuffer> }> {\r\n if (!$isStr(data)) {\r\n return $err({\r\n msg: 'Crypto Web API - String to Bytes: Empty data',\r\n desc: 'Data must be a non-empty string',\r\n });\r\n }\r\n if (!ENCODING.includes(inputEncoding)) {\r\n return $err({\r\n msg: `Crypto Web API - String to Bytes: Unsupported encoding: ${inputEncoding}`,\r\n desc: 'Use base64, base64url, hex, utf8, or latin1',\r\n });\r\n }\r\n\r\n try {\r\n const bytes = strToBytes[inputEncoding](data);\r\n return $ok({ result: bytes });\r\n } catch (error) {\r\n return $err({ msg: 'Crypto Web API - String to Bytes: Failed to convert data', desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport function $convertBytesToStr(data: Uint8Array | ArrayBuffer, outputEncoding: Encoding = 'utf8'): Result<string> {\r\n if (!(data instanceof ArrayBuffer || data instanceof Uint8Array)) {\r\n return $err({\r\n msg: 'Crypto Web API - Bytes to String: Invalid data type',\r\n desc: 'Data must be an ArrayBuffer or Uint8Array',\r\n });\r\n }\r\n if (!ENCODING.includes(outputEncoding)) {\r\n return $err({\r\n msg: `Crypto Web API - Bytes to String: Unsupported encoding: ${outputEncoding}`,\r\n desc: 'Use base64, base64url, hex, utf8, or latin1',\r\n });\r\n }\r\n try {\r\n const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\r\n const str = bytesToStr[outputEncoding](bytes);\r\n return $ok(str);\r\n } catch (error) {\r\n return $err({ msg: 'Crypto Web API - Bytes to String: Failed to convert data', desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport function $convertEncoding(data: string, from: Encoding, to: Encoding): Result<string> {\r\n if (!$isStr(data)) {\r\n return $err({\r\n msg: 'Crypto Web API - Convert Format: Empty data',\r\n desc: 'Data must be a non-empty string',\r\n });\r\n }\r\n if (!ENCODING.includes(from) || !ENCODING.includes(to)) {\r\n return $err({\r\n msg: `Crypto Web API - Convert Format: Unsupported encoding: from ${from} to ${to}`,\r\n desc: 'Use base64, base64url, hex, utf8, or latin1',\r\n });\r\n }\r\n\r\n const bytes = $convertStrToBytes(data, from);\r\n if (bytes.error) return $err({ msg: bytes.error.message, desc: bytes.error.description });\r\n\r\n const str = $convertBytesToStr(bytes.result, to);\r\n if (str.error) return $err({ msg: str.error.message, desc: str.error.description });\r\n\r\n return $ok({ result: str.result });\r\n}\r\n\r\nconst strToBytes = {\r\n base64: $fromBase64,\r\n base64url: $fromBase64Url,\r\n hex: $fromHex,\r\n latin1: $fromLatin1,\r\n utf8: (data: string) => textEncoder.encode(data),\r\n} as const satisfies Record<Encoding, (data: string) => Uint8Array<ArrayBuffer>>;\r\n\r\nconst bytesToStr = {\r\n base64: $toBase64,\r\n base64url: $toBase64Url,\r\n hex: $toHex,\r\n latin1: $toLatin1,\r\n utf8: (data: Uint8Array) => textDecoder.decode(data),\r\n} as const satisfies Record<Encoding, (data: Uint8Array) => string>;\r\n\r\nfunction $toLatin1(bytes: Uint8Array): string {\r\n let out = '';\r\n const chunk = 1 << 15; // 32KiB per chunk\r\n for (let i = 0; i < bytes.length; i += chunk) {\r\n out += String.fromCharCode(...bytes.subarray(i, i + chunk));\r\n }\r\n return out;\r\n}\r\n\r\nfunction $fromLatin1(data: string): Uint8Array<ArrayBuffer> {\r\n const out = new Uint8Array(data.length);\r\n for (let i = 0; i < data.length; i++) {\r\n const charCode = data.charCodeAt(i);\r\n if (charCode > 255) throw new Error('Invalid latin1 string');\r\n out[i] = charCode;\r\n }\r\n return out;\r\n}\r\n\r\nfunction $toBase64(bytes: Uint8Array): string {\r\n return btoa($toLatin1(bytes));\r\n}\r\n\r\nfunction $fromBase64(data: string): Uint8Array<ArrayBuffer> {\r\n return $fromLatin1(atob(data));\r\n}\r\n\r\nfunction $toBase64Url(bytes: Uint8Array): string {\r\n return $toBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n}\r\n\r\nfunction $fromBase64Url(data: string): Uint8Array<ArrayBuffer> {\r\n let base64 = data.replace(/-/g, '+').replace(/_/g, '/');\r\n const padLen = (4 - (base64.length % 4)) % 4;\r\n base64 += '='.repeat(padLen);\r\n return $fromBase64(base64);\r\n}\r\n\r\nfunction $toHex(bytes: Uint8Array): string {\r\n return Array.from(bytes)\r\n .map((b) => b.toString(16).padStart(2, '0'))\r\n .join('');\r\n}\r\n\r\nfunction $fromHex(data: string): Uint8Array<ArrayBuffer> {\r\n const clean = data.startsWith('0x') ? data.slice(2) : data;\r\n if (clean.length % 2 !== 0) throw new Error('Invalid hex string');\r\n const out = new Uint8Array(clean.length / 2);\r\n for (let i = 0; i < out.length; i++) {\r\n const byte = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\r\n if (Number.isNaN(byte)) throw new Error('Invalid hex string');\r\n out[i] = byte;\r\n }\r\n return out;\r\n}\r\n","import { CIPHER_ENCODING, DIGEST_ALGORITHMS, ENCRYPTION_ALGORITHMS } from '~/helpers/consts';\r\nimport { $err, $fmtError, $fmtResultErr, $ok, type Result, title } from '~/helpers/error';\r\nimport { $parseToObj, $stringifyObj } from '~/helpers/object';\r\nimport type {\r\n CreateSecretKeyOptions,\r\n DecryptOptions,\r\n EncryptOptions,\r\n HashOptions,\r\n HashPasswordOptions,\r\n SecretKey,\r\n VerifyPasswordOptions,\r\n} from '~/helpers/types';\r\nimport { $isSecretKey, $isStr, matchPattern } from '~/helpers/validate';\r\nimport { $convertBytesToStr, $convertStrToBytes, textEncoder } from './web-encode';\r\n\r\nexport function $generateUuid(): Result<string> {\r\n try {\r\n return $ok(crypto.randomUUID());\r\n } catch (error) {\r\n return $err({ msg: `${title('web', 'UUID Generation')}: Failed to generate UUID`, desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport async function $createSecretKey(\r\n secret: string,\r\n options: CreateSecretKeyOptions = {},\r\n): Promise<Result<{ result: SecretKey<'web'> }>> {\r\n if (!$isStr(secret)) {\r\n return $err({ msg: `${title('web', 'Key Generation')}: Empty Secret`, desc: 'Secret must be a non-empty string' });\r\n }\r\n\r\n const algorithm = options.algorithm ?? 'aes256gcm';\r\n if (!(algorithm in ENCRYPTION_ALGORITHMS)) {\r\n return $err({\r\n msg: `${title('web', 'Key Generation')}: Unsupported algorithm: ${algorithm}`,\r\n desc: `Supported algorithms are: ${Object.keys(ENCRYPTION_ALGORITHMS).join(', ')}`,\r\n });\r\n }\r\n\r\n const digest = options.digest ?? 'sha256';\r\n if (!(digest in DIGEST_ALGORITHMS)) {\r\n return $err({\r\n msg: `${title('web', 'Key Generation')}: Unsupported digest: ${digest}`,\r\n desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(', ')}`,\r\n });\r\n }\r\n\r\n const salt = options.salt ?? 'cipher-kit-salt';\r\n if (!$isStr(salt, 8)) {\r\n return $err({\r\n msg: `${title('web', 'Key Generation')}: Weak salt`,\r\n desc: 'Salt must be a non-empty string with at least 8 characters',\r\n });\r\n }\r\n\r\n const info = options.info ?? 'cipher-kit';\r\n if (!$isStr(info)) {\r\n return $err({\r\n msg: `${title('web', 'Key Generation')}: Invalid info`,\r\n desc: 'Info must be a non-empty string',\r\n });\r\n }\r\n\r\n const encryptAlgo = ENCRYPTION_ALGORITHMS[algorithm];\r\n const digestAlgo = DIGEST_ALGORITHMS[digest];\r\n\r\n try {\r\n const ikm = await crypto.subtle.importKey('raw', textEncoder.encode(secret.normalize('NFKC')), 'HKDF', false, [\r\n 'deriveKey',\r\n ]);\r\n const key = await crypto.subtle.deriveKey(\r\n {\r\n name: 'HKDF',\r\n hash: digestAlgo.web,\r\n salt: textEncoder.encode(salt.normalize('NFKC')),\r\n info: textEncoder.encode(info.normalize('NFKC')),\r\n },\r\n ikm,\r\n { name: encryptAlgo.web, length: encryptAlgo.keyBytes * 8 },\r\n true,\r\n ['encrypt', 'decrypt'],\r\n );\r\n const secretKey = Object.freeze({\r\n platform: 'web',\r\n digest: digest,\r\n algorithm: algorithm,\r\n key: key,\r\n }) as SecretKey<'web'>;\r\n\r\n return $ok({ result: secretKey });\r\n } catch (error) {\r\n return $err({\r\n msg: `${title('web', 'Key Generation')}: Failed to create secret key`,\r\n desc: $fmtError(error),\r\n });\r\n }\r\n}\r\n\r\nexport async function $encrypt(\r\n data: string,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<Result<string>> {\r\n if (!$isStr(data)) {\r\n return $err({\r\n msg: `${title('web', 'Encryption')}: Empty data for encryption`,\r\n desc: 'Data must be a non-empty string',\r\n });\r\n }\r\n\r\n const encoding = options.encoding ?? 'base64url';\r\n if (!CIPHER_ENCODING.includes(encoding)) {\r\n return $err({\r\n msg: `${title('web', 'Encryption')}: Unsupported output encoding: ${encoding}`,\r\n desc: 'Use base64, base64url, or hex',\r\n });\r\n }\r\n\r\n const injectedKey = $isSecretKey(secretKey, 'web');\r\n if (!injectedKey) {\r\n return $err({\r\n msg: `${title('web', 'Encryption')}: Invalid Secret Key`,\r\n desc: 'Expected a Web SecretKey',\r\n });\r\n }\r\n\r\n const { result, error } = $convertStrToBytes(data, 'utf8');\r\n if (error) return $err(error);\r\n\r\n try {\r\n const iv = crypto.getRandomValues(new Uint8Array(injectedKey.injected.ivLength));\r\n const cipherWithTag = await crypto.subtle.encrypt(\r\n { name: injectedKey.injected.web, iv: iv },\r\n injectedKey.key,\r\n result,\r\n );\r\n\r\n const ivStr = $convertBytesToStr(iv, encoding);\r\n const cipherStr = $convertBytesToStr(cipherWithTag, encoding);\r\n\r\n if (ivStr.error || cipherStr.error) {\r\n return $err({\r\n msg: `${title('web', 'Encryption')}: Failed to convert IV or encrypted data`,\r\n desc: `Conversion error: ${$fmtResultErr(ivStr.error || cipherStr.error)}`,\r\n });\r\n }\r\n\r\n return $ok(`${ivStr.result}.${cipherStr.result}.`);\r\n } catch (error) {\r\n return $err({ msg: `${title('web', 'Encryption')}: Failed to encrypt data`, desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport async function $decrypt(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<Result<string>> {\r\n if (matchPattern(encrypted, 'web') === false) {\r\n return $err({\r\n msg: `${title('web', 'Decryption')}: Invalid encrypted data format`,\r\n desc: 'Encrypted data must be in the format \"iv.cipherWithTag.\"',\r\n });\r\n }\r\n\r\n const encoding = options.encoding ?? 'base64url';\r\n if (!CIPHER_ENCODING.includes(encoding)) {\r\n return $err({\r\n msg: `${title('web', 'Decryption')}: Unsupported input encoding: ${encoding}`,\r\n desc: 'Use base64, base64url, or hex',\r\n });\r\n }\r\n\r\n const [iv, encryptedWithTag] = encrypted.split('.', 3);\r\n if (!$isStr(iv) || !$isStr(encryptedWithTag)) {\r\n return $err({\r\n msg: `${title('web', 'Decryption')}: Invalid encrypted data`,\r\n desc: 'Encrypted data must contain valid IV, encrypted and tag components',\r\n });\r\n }\r\n\r\n const injectedKey = $isSecretKey(secretKey, 'web');\r\n if (!injectedKey) {\r\n return $err({\r\n msg: `${title('web', 'Decryption')}: Invalid Secret Key`,\r\n desc: 'Expected a Web SecretKey',\r\n });\r\n }\r\n\r\n const ivBytes = $convertStrToBytes(iv, encoding);\r\n const cipherWithTagBytes = $convertStrToBytes(encryptedWithTag, encoding);\r\n\r\n if (ivBytes.error || cipherWithTagBytes.error) {\r\n return $err({\r\n msg: `${title('web', 'Decryption')}: Failed to convert IV or encrypted data`,\r\n desc: `Conversion error: ${$fmtResultErr(ivBytes.error || cipherWithTagBytes.error)}`,\r\n });\r\n }\r\n\r\n try {\r\n const decrypted = await crypto.subtle.decrypt(\r\n { name: injectedKey.injected.web, iv: ivBytes.result },\r\n injectedKey.key,\r\n cipherWithTagBytes.result,\r\n );\r\n\r\n return $convertBytesToStr(decrypted, 'utf8');\r\n } catch (error) {\r\n return $err({ msg: `${title('web', 'Decryption')}: Failed to decrypt data`, desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport async function $encryptObj<T extends object = Record<string, unknown>>(\r\n data: T,\r\n secretKey: SecretKey<'web'>,\r\n options: EncryptOptions = {},\r\n): Promise<Result<string>> {\r\n const { result, error } = $stringifyObj(data);\r\n if (error) return $err(error);\r\n return await $encrypt(result, secretKey, options);\r\n}\r\n\r\nexport async function $decryptObj<T extends object = Record<string, unknown>>(\r\n encrypted: string,\r\n secretKey: SecretKey<'web'>,\r\n options: DecryptOptions = {},\r\n): Promise<Result<{ result: T }>> {\r\n const { result, error } = await $decrypt(encrypted, secretKey, options);\r\n if (error) return $err(error);\r\n return $parseToObj<T>(result);\r\n}\r\n\r\nexport async function $hash(data: string, options: HashOptions = {}): Promise<Result<string>> {\r\n if (!$isStr(data)) {\r\n return $err({ msg: `${title('web', 'Hashing')}: Empty data for hashing`, desc: 'Data must be a non-empty string' });\r\n }\r\n\r\n const encoding = options.encoding ?? 'base64url';\r\n if (!CIPHER_ENCODING.includes(encoding)) {\r\n return $err({\r\n msg: `${title('web', 'Hashing')}: Unsupported output encoding: ${encoding}`,\r\n desc: 'Use base64, base64url, or hex',\r\n });\r\n }\r\n\r\n const digest = options.digest ?? 'sha256';\r\n if (!(digest in DIGEST_ALGORITHMS)) {\r\n return $err({\r\n msg: `${title('web', 'Hashing')}: Unsupported digest: ${digest}`,\r\n desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(', ')}`,\r\n });\r\n }\r\n const digestAlgo = DIGEST_ALGORITHMS[digest];\r\n\r\n const { result, error } = $convertStrToBytes(data, 'utf8');\r\n if (error) return $err(error);\r\n\r\n try {\r\n const hashed = await crypto.subtle.digest(digestAlgo.web, result);\r\n return $convertBytesToStr(hashed, encoding);\r\n } catch (error) {\r\n return $err({ msg: `${title('web', 'Hashing')}: Failed to hash data`, desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport async function $hashPassword(\r\n password: string,\r\n options: HashPasswordOptions = {},\r\n): Promise<Result<{ hash: string; salt: string }>> {\r\n if (!$isStr(password)) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Empty password`,\r\n desc: 'Password must be a non-empty string',\r\n });\r\n }\r\n\r\n const digest = options.digest ?? 'sha512';\r\n if (!(digest in DIGEST_ALGORITHMS)) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Unsupported digest: ${digest}`,\r\n desc: `Supported digests are: ${Object.keys(DIGEST_ALGORITHMS).join(', ')}`,\r\n });\r\n }\r\n const digestAlgo = DIGEST_ALGORITHMS[digest];\r\n\r\n const encoding = options.encoding ?? 'base64url';\r\n if (!CIPHER_ENCODING.includes(encoding)) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Unsupported output encoding: ${encoding}`,\r\n desc: 'Use base64, base64url, or hex',\r\n });\r\n }\r\n\r\n const saltLength = options.saltLength ?? 16;\r\n if (typeof saltLength !== 'number' || saltLength < 8) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Weak salt length`,\r\n desc: 'Salt length must be a number and at least 8 bytes (recommended 16)',\r\n });\r\n }\r\n\r\n const iterations = options.iterations ?? 320_000;\r\n if (typeof iterations !== 'number' || iterations < 1000) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Weak iteration count`,\r\n desc: 'Iterations must be a number and at least 1000 (recommended 320,000 or more)',\r\n });\r\n }\r\n\r\n const keyLength = options.keyLength ?? 64;\r\n if (typeof keyLength !== 'number' || keyLength < 16) {\r\n return $err({\r\n msg: `${title('web', 'Password Hashing')}: Weak key length`,\r\n desc: 'Key length must be a number and at least 16 bytes (recommended 64)',\r\n });\r\n }\r\n\r\n try {\r\n const salt = crypto.getRandomValues(new Uint8Array(saltLength));\r\n const baseKey = await crypto.subtle.importKey(\r\n 'raw',\r\n textEncoder.encode(password.normalize('NFKC')),\r\n 'PBKDF2',\r\n false,\r\n ['deriveBits'],\r\n );\r\n const bits = await crypto.subtle.deriveBits(\r\n { name: 'PBKDF2', salt, iterations: iterations, hash: digestAlgo.web },\r\n baseKey,\r\n keyLength * 8,\r\n );\r\n\r\n const saltStr = $convertBytesToStr(salt, encoding);\r\n if (saltStr.error) return $err(saltStr.error);\r\n\r\n const hashedPasswordStr = $convertBytesToStr(bits, encoding);\r\n if (hashedPasswordStr.error) return $err(hashedPasswordStr.error);\r\n\r\n return $ok({ hash: hashedPasswordStr.result, salt: saltStr.result });\r\n } catch (error) {\r\n return $err({ msg: `${title('web', 'Password Hashing')}: Failed to hash password`, desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport async function $verifyPassword(\r\n password: string,\r\n hashedPassword: string,\r\n salt: string,\r\n options: VerifyPasswordOptions = {},\r\n): Promise<boolean> {\r\n if (!$isStr(password) || !$isStr(hashedPassword) || !$isStr(salt)) return false;\r\n\r\n const digest = options.digest ?? 'sha512';\r\n if (!(digest in DIGEST_ALGORITHMS)) return false;\r\n const digestAlgo = DIGEST_ALGORITHMS[digest];\r\n\r\n const encoding = options.encoding ?? 'base64url';\r\n if (!CIPHER_ENCODING.includes(encoding)) return false;\r\n\r\n const iterations = options.iterations ?? 320_000;\r\n if (typeof iterations !== 'number' || iterations < 1000) return false;\r\n\r\n const keyLength = options.keyLength ?? 64;\r\n if (typeof keyLength !== 'number' || keyLength < 16) return false;\r\n\r\n const saltBytes = $convertStrToBytes(salt, encoding);\r\n if (saltBytes.error) return false;\r\n\r\n const hashedPasswordBytes = $convertStrToBytes(hashedPassword, encoding);\r\n if (hashedPasswordBytes.error) return false;\r\n\r\n try {\r\n const baseKey = await crypto.subtle.importKey(\r\n 'raw',\r\n textEncoder.encode(password.normalize('NFKC')),\r\n 'PBKDF2',\r\n false,\r\n ['deriveBits'],\r\n );\r\n\r\n const bits = new Uint8Array(\r\n await crypto.subtle.deriveBits(\r\n {\r\n name: 'PBKDF2',\r\n salt: saltBytes.result,\r\n iterations: iterations,\r\n hash: digestAlgo.web,\r\n },\r\n baseKey,\r\n keyLength * 8,\r\n ),\r\n );\r\n\r\n if (bits === undefined || hashedPasswordBytes.result === undefined) return false;\r\n if (bits.length !== hashedPasswordBytes.result.length) return false;\r\n\r\n let diff = 0;\r\n for (let i = 0; i < bits.length; i++) {\r\n diff |= (bits[i] as number) ^ (hashedPasswordBytes.result[i] as number);\r\n }\r\n return diff === 0;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n"]}
|