typesecure 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,720 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConfigSchema: () => ConfigSchema,
34
+ EncryptionOptionsSchema: () => EncryptionOptionsSchema,
35
+ HashOptionsSchema: () => HashOptionsSchema,
36
+ PasswordHashOptionsSchema: () => PasswordHashOptionsSchema,
37
+ PasswordStrengthSchema: () => PasswordStrengthSchema,
38
+ SecurityLevel: () => SecurityLevel,
39
+ TimingSafeOptionsSchema: () => TimingSafeOptionsSchema,
40
+ analyzePasswordPatterns: () => analyzePasswordPatterns,
41
+ calculatePasswordEntropy: () => calculatePasswordEntropy,
42
+ checkPasswordStrength: () => checkPasswordStrength,
43
+ decrypt: () => decrypt,
44
+ encrypt: () => encrypt,
45
+ generateKey: () => generateKey,
46
+ generateRandomBytes: () => generateRandomBytes,
47
+ generateSecurePassword: () => generateSecurePassword,
48
+ getSecurityLevel: () => getSecurityLevel,
49
+ hash: () => hash,
50
+ hashPassword: () => hashPassword,
51
+ hmac: () => hmac,
52
+ timingSafeEqual: () => timingSafeEqual,
53
+ verifyHash: () => verifyHash,
54
+ verifyPassword: () => verifyPassword
55
+ });
56
+ module.exports = __toCommonJS(index_exports);
57
+
58
+ // src/utils/hash.ts
59
+ var import_crypto_js = __toESM(require("crypto-js"));
60
+
61
+ // src/types/index.ts
62
+ var import_zod = require("zod");
63
+ var PasswordStrengthSchema = import_zod.z.object({
64
+ score: import_zod.z.number().min(0).max(4),
65
+ feedback: import_zod.z.object({
66
+ warning: import_zod.z.string().optional(),
67
+ suggestions: import_zod.z.array(import_zod.z.string()).optional()
68
+ }),
69
+ isStrong: import_zod.z.boolean()
70
+ });
71
+ var HashOptionsSchema = import_zod.z.object({
72
+ algorithm: import_zod.z.enum(["md5", "sha1", "sha256", "sha512", "sha3"]).default("sha256"),
73
+ encoding: import_zod.z.enum(["hex", "base64"]).default("hex")
74
+ });
75
+ var PasswordHashOptionsSchema = import_zod.z.object({
76
+ algorithm: import_zod.z.enum(["pbkdf2", "argon2", "bcrypt"]).default("pbkdf2"),
77
+ iterations: import_zod.z.number().min(1e3).default(1e4),
78
+ saltLength: import_zod.z.number().min(16).default(32),
79
+ keyLength: import_zod.z.number().min(16).default(64)
80
+ });
81
+ var TimingSafeOptionsSchema = import_zod.z.object({
82
+ encoding: import_zod.z.enum(["utf8", "hex", "base64"]).default("utf8")
83
+ });
84
+ var EncryptionOptionsSchema = import_zod.z.object({
85
+ mode: import_zod.z.enum(["aes-cbc", "aes-ctr", "aes-ecb", "aes-gcm"]).default("aes-cbc"),
86
+ padding: import_zod.z.enum(["Pkcs7", "NoPadding", "ZeroPadding"]).default("Pkcs7"),
87
+ iv: import_zod.z.string().optional(),
88
+ authTag: import_zod.z.string().optional(),
89
+ aad: import_zod.z.string().optional()
90
+ // Additional authenticated data for GCM mode
91
+ });
92
+ var ConfigSchema = import_zod.z.object({
93
+ minimumPasswordLength: import_zod.z.number().min(8).default(12),
94
+ requireNumbers: import_zod.z.boolean().default(true),
95
+ requireSymbols: import_zod.z.boolean().default(true),
96
+ requireUppercase: import_zod.z.boolean().default(true),
97
+ requireLowercase: import_zod.z.boolean().default(true)
98
+ });
99
+
100
+ // src/utils/hash.ts
101
+ function hash(input, options) {
102
+ const validatedOptions = HashOptionsSchema.parse({
103
+ algorithm: options?.algorithm || "sha256",
104
+ encoding: options?.encoding || "hex"
105
+ });
106
+ let hashedValue;
107
+ switch (validatedOptions.algorithm) {
108
+ case "md5":
109
+ hashedValue = import_crypto_js.default.MD5(input);
110
+ break;
111
+ case "sha1":
112
+ hashedValue = import_crypto_js.default.SHA1(input);
113
+ break;
114
+ case "sha256":
115
+ hashedValue = import_crypto_js.default.SHA256(input);
116
+ break;
117
+ case "sha512":
118
+ hashedValue = import_crypto_js.default.SHA512(input);
119
+ break;
120
+ case "sha3":
121
+ hashedValue = import_crypto_js.default.SHA3(input);
122
+ break;
123
+ default:
124
+ hashedValue = import_crypto_js.default.SHA256(input);
125
+ }
126
+ return validatedOptions.encoding === "base64" ? hashedValue.toString(import_crypto_js.default.enc.Base64) : hashedValue.toString(import_crypto_js.default.enc.Hex);
127
+ }
128
+ function verifyHash(input, hashedValue, options) {
129
+ const newHash = hash(input, options);
130
+ return newHash === hashedValue;
131
+ }
132
+ function hmac(input, key, options) {
133
+ const validatedOptions = HashOptionsSchema.parse({
134
+ algorithm: options?.algorithm || "sha256",
135
+ encoding: options?.encoding || "hex"
136
+ });
137
+ let hmacValue;
138
+ switch (validatedOptions.algorithm) {
139
+ case "md5":
140
+ hmacValue = import_crypto_js.default.HmacMD5(input, key);
141
+ break;
142
+ case "sha1":
143
+ hmacValue = import_crypto_js.default.HmacSHA1(input, key);
144
+ break;
145
+ case "sha256":
146
+ hmacValue = import_crypto_js.default.HmacSHA256(input, key);
147
+ break;
148
+ case "sha512":
149
+ hmacValue = import_crypto_js.default.HmacSHA512(input, key);
150
+ break;
151
+ case "sha3":
152
+ hmacValue = import_crypto_js.default.HmacSHA3(input, key);
153
+ break;
154
+ default:
155
+ hmacValue = import_crypto_js.default.HmacSHA256(input, key);
156
+ }
157
+ return validatedOptions.encoding === "base64" ? hmacValue.toString(import_crypto_js.default.enc.Base64) : hmacValue.toString(import_crypto_js.default.enc.Hex);
158
+ }
159
+ function hashPassword(password, options) {
160
+ const validatedOptions = PasswordHashOptionsSchema.parse({
161
+ algorithm: options?.algorithm || "pbkdf2",
162
+ iterations: options?.iterations || 1e4,
163
+ saltLength: options?.saltLength || 32,
164
+ keyLength: options?.keyLength || 64
165
+ });
166
+ const salt = import_crypto_js.default.lib.WordArray.random(validatedOptions.saltLength / 2).toString(
167
+ import_crypto_js.default.enc.Hex
168
+ );
169
+ let hashedPassword;
170
+ switch (validatedOptions.algorithm) {
171
+ case "pbkdf2":
172
+ hashedPassword = import_crypto_js.default.PBKDF2(password, salt, {
173
+ keySize: validatedOptions.keyLength / 4,
174
+ // keySize is in 32-bit words
175
+ iterations: validatedOptions.iterations
176
+ }).toString(import_crypto_js.default.enc.Hex);
177
+ break;
178
+ case "argon2":
179
+ hashedPassword = import_crypto_js.default.PBKDF2(password, salt, {
180
+ keySize: validatedOptions.keyLength / 4,
181
+ iterations: validatedOptions.iterations * 2
182
+ // Higher iterations to simulate Argon2 costs
183
+ }).toString(import_crypto_js.default.enc.Hex);
184
+ break;
185
+ case "bcrypt":
186
+ hashedPassword = import_crypto_js.default.PBKDF2(password, salt, {
187
+ keySize: validatedOptions.keyLength / 4,
188
+ iterations: 1024 + validatedOptions.iterations % 1024
189
+ // bcrypt simulation
190
+ }).toString(import_crypto_js.default.enc.Hex);
191
+ break;
192
+ default:
193
+ hashedPassword = import_crypto_js.default.PBKDF2(password, salt, {
194
+ keySize: validatedOptions.keyLength / 4,
195
+ iterations: validatedOptions.iterations
196
+ }).toString(import_crypto_js.default.enc.Hex);
197
+ }
198
+ return {
199
+ hash: hashedPassword,
200
+ salt,
201
+ params: validatedOptions
202
+ };
203
+ }
204
+ function verifyPassword(password, hash2, salt, options) {
205
+ const validatedOptions = PasswordHashOptionsSchema.parse({
206
+ algorithm: options?.algorithm || "pbkdf2",
207
+ iterations: options?.iterations || 1e4,
208
+ saltLength: options?.saltLength || 32,
209
+ keyLength: options?.keyLength || 64
210
+ });
211
+ const { hash: generatedHash } = hashPassword(password, validatedOptions);
212
+ return timingSafeEqual(generatedHash, hash2);
213
+ }
214
+ function timingSafeEqual(a, b, options) {
215
+ const validatedOptions = TimingSafeOptionsSchema.parse({
216
+ encoding: options?.encoding || "utf8"
217
+ });
218
+ if (a.length !== b.length) {
219
+ return false;
220
+ }
221
+ let bufferA = a;
222
+ let bufferB = b;
223
+ if (validatedOptions.encoding === "hex") {
224
+ try {
225
+ bufferA = import_crypto_js.default.enc.Hex.parse(a).toString(import_crypto_js.default.enc.Utf8);
226
+ bufferB = import_crypto_js.default.enc.Hex.parse(b).toString(import_crypto_js.default.enc.Utf8);
227
+ } catch {
228
+ return false;
229
+ }
230
+ } else if (validatedOptions.encoding === "base64") {
231
+ try {
232
+ bufferA = import_crypto_js.default.enc.Base64.parse(a).toString(import_crypto_js.default.enc.Utf8);
233
+ bufferB = import_crypto_js.default.enc.Base64.parse(b).toString(import_crypto_js.default.enc.Utf8);
234
+ } catch {
235
+ return false;
236
+ }
237
+ }
238
+ let result = 0;
239
+ for (let i = 0; i < bufferA.length; i++) {
240
+ result |= bufferA.charCodeAt(i) ^ bufferB.charCodeAt(i);
241
+ }
242
+ return result === 0;
243
+ }
244
+ function generateRandomBytes(length = 32, encoding = "hex") {
245
+ const randomBytes = import_crypto_js.default.lib.WordArray.random(length);
246
+ return encoding === "base64" ? randomBytes.toString(import_crypto_js.default.enc.Base64) : randomBytes.toString(import_crypto_js.default.enc.Hex);
247
+ }
248
+
249
+ // src/utils/encryption.ts
250
+ var import_crypto_js2 = __toESM(require("crypto-js"));
251
+ var SecurityLevel = /* @__PURE__ */ ((SecurityLevel2) => {
252
+ SecurityLevel2["HIGH"] = "HIGH";
253
+ SecurityLevel2["MEDIUM"] = "MEDIUM";
254
+ SecurityLevel2["LOW"] = "LOW";
255
+ SecurityLevel2["INSECURE"] = "INSECURE";
256
+ return SecurityLevel2;
257
+ })(SecurityLevel || {});
258
+ function getSecurityLevel(options) {
259
+ if (options.mode === "aes-gcm") {
260
+ return "HIGH" /* HIGH */;
261
+ }
262
+ if (options.mode === "aes-cbc" || options.mode === "aes-ctr") {
263
+ return "HIGH" /* HIGH */;
264
+ }
265
+ if (options.mode === "aes-ecb") {
266
+ return "INSECURE" /* INSECURE */;
267
+ }
268
+ return "LOW" /* LOW */;
269
+ }
270
+ function logSecurityWarning(options) {
271
+ const securityLevel = getSecurityLevel(options);
272
+ switch (securityLevel) {
273
+ case "INSECURE" /* INSECURE */:
274
+ console.warn(
275
+ `\u26A0\uFE0F SECURITY WARNING: ${options.mode} mode is insecure and should not be used in production!`
276
+ );
277
+ break;
278
+ case "LOW" /* LOW */:
279
+ console.warn(
280
+ `\u26A0\uFE0F Security Warning: ${options.mode} with ${options.padding} padding provides low security.`
281
+ );
282
+ break;
283
+ case "MEDIUM" /* MEDIUM */:
284
+ console.info(
285
+ `\u2139\uFE0F Security Note: ${options.mode} with ${options.padding} padding provides adequate security for most use cases.`
286
+ );
287
+ break;
288
+ case "HIGH" /* HIGH */:
289
+ break;
290
+ }
291
+ }
292
+ function encrypt(text, key, options) {
293
+ const validatedOptions = EncryptionOptionsSchema.parse({
294
+ mode: options?.mode || "aes-cbc",
295
+ padding: options?.padding || "Pkcs7",
296
+ iv: options?.iv,
297
+ authTag: options?.authTag,
298
+ aad: options?.aad
299
+ });
300
+ logSecurityWarning(validatedOptions);
301
+ const keyWordArray = import_crypto_js2.default.enc.Utf8.parse(key);
302
+ let encrypted;
303
+ const paddingOption = import_crypto_js2.default.pad[validatedOptions.padding];
304
+ switch (validatedOptions.mode) {
305
+ case "aes-gcm": {
306
+ const iv = validatedOptions.iv ? import_crypto_js2.default.enc.Utf8.parse(validatedOptions.iv) : import_crypto_js2.default.lib.WordArray.random(12);
307
+ const aad = validatedOptions.aad ? import_crypto_js2.default.enc.Utf8.parse(validatedOptions.aad) : void 0;
308
+ encrypted = import_crypto_js2.default.AES.encrypt(text, keyWordArray, {
309
+ iv,
310
+ mode: import_crypto_js2.default.mode.CTR,
311
+ // Use CTR as a base for GCM simulation
312
+ padding: paddingOption
313
+ });
314
+ const authData = aad ? aad.concat(encrypted.ciphertext) : encrypted.ciphertext;
315
+ const authTag = import_crypto_js2.default.HmacSHA256(authData, keyWordArray).toString().substring(0, 32);
316
+ const ivHex = iv.toString(import_crypto_js2.default.enc.Hex);
317
+ return `${ivHex}:${authTag}:${encrypted.toString()}`;
318
+ }
319
+ case "aes-cbc": {
320
+ const iv = validatedOptions.iv ? import_crypto_js2.default.enc.Utf8.parse(validatedOptions.iv) : import_crypto_js2.default.lib.WordArray.random(16);
321
+ encrypted = import_crypto_js2.default.AES.encrypt(text, keyWordArray, {
322
+ iv,
323
+ padding: paddingOption,
324
+ mode: import_crypto_js2.default.mode.CBC
325
+ });
326
+ if (!validatedOptions.iv) {
327
+ const ivHex = iv.toString(import_crypto_js2.default.enc.Hex);
328
+ return `${ivHex}:${encrypted.toString()}`;
329
+ }
330
+ break;
331
+ }
332
+ case "aes-ctr": {
333
+ const iv = validatedOptions.iv ? import_crypto_js2.default.enc.Utf8.parse(validatedOptions.iv) : import_crypto_js2.default.lib.WordArray.random(16);
334
+ encrypted = import_crypto_js2.default.AES.encrypt(text, keyWordArray, {
335
+ iv,
336
+ padding: paddingOption,
337
+ mode: import_crypto_js2.default.mode.CTR
338
+ });
339
+ if (!validatedOptions.iv) {
340
+ const ivHex = iv.toString(import_crypto_js2.default.enc.Hex);
341
+ return `${ivHex}:${encrypted.toString()}`;
342
+ }
343
+ break;
344
+ }
345
+ case "aes-ecb":
346
+ encrypted = import_crypto_js2.default.AES.encrypt(text, keyWordArray, {
347
+ padding: paddingOption,
348
+ mode: import_crypto_js2.default.mode.ECB
349
+ });
350
+ break;
351
+ default:
352
+ throw new Error(`Unsupported encryption mode: ${validatedOptions.mode}`);
353
+ }
354
+ return encrypted.toString();
355
+ }
356
+ function decrypt(encryptedText, key, options) {
357
+ const validatedOptions = EncryptionOptionsSchema.parse({
358
+ mode: options?.mode || "aes-cbc",
359
+ padding: options?.padding || "Pkcs7",
360
+ iv: options?.iv,
361
+ authTag: options?.authTag,
362
+ aad: options?.aad
363
+ });
364
+ const keyWordArray = import_crypto_js2.default.enc.Utf8.parse(key);
365
+ let cipherText = encryptedText;
366
+ let iv;
367
+ let authTag;
368
+ if (validatedOptions.mode === "aes-gcm" && encryptedText.includes(":")) {
369
+ const parts = encryptedText.split(":");
370
+ if (parts.length >= 3) {
371
+ const ivHex = parts[0];
372
+ authTag = parts[1];
373
+ cipherText = parts[2];
374
+ iv = import_crypto_js2.default.enc.Hex.parse(ivHex);
375
+ }
376
+ } else if (encryptedText.includes(":") && !validatedOptions.iv) {
377
+ const parts = encryptedText.split(":");
378
+ const ivHex = parts[0];
379
+ cipherText = parts[1];
380
+ iv = import_crypto_js2.default.enc.Hex.parse(ivHex);
381
+ } else if (validatedOptions.iv) {
382
+ iv = import_crypto_js2.default.enc.Utf8.parse(validatedOptions.iv);
383
+ }
384
+ const paddingOption = import_crypto_js2.default.pad[validatedOptions.padding];
385
+ let decrypted;
386
+ switch (validatedOptions.mode) {
387
+ case "aes-gcm": {
388
+ if (!iv) {
389
+ throw new Error("IV is required for GCM mode decryption");
390
+ }
391
+ if (!authTag && !validatedOptions.authTag) {
392
+ throw new Error("Authentication tag is required for GCM mode decryption");
393
+ }
394
+ const actualAuthTag = authTag || validatedOptions.authTag;
395
+ const aad = validatedOptions.aad ? import_crypto_js2.default.enc.Utf8.parse(validatedOptions.aad) : void 0;
396
+ decrypted = import_crypto_js2.default.AES.decrypt(cipherText, keyWordArray, {
397
+ iv,
398
+ mode: import_crypto_js2.default.mode.CTR,
399
+ padding: paddingOption
400
+ });
401
+ const cipherTextObj = import_crypto_js2.default.enc.Base64.parse(cipherText);
402
+ const authData = aad ? aad.concat(cipherTextObj) : cipherTextObj;
403
+ const calculatedAuthTag = import_crypto_js2.default.HmacSHA256(authData, keyWordArray).toString().substring(0, 32);
404
+ if (calculatedAuthTag !== actualAuthTag) {
405
+ throw new Error("Authentication failed: Invalid authentication tag");
406
+ }
407
+ break;
408
+ }
409
+ case "aes-cbc":
410
+ if (!iv && validatedOptions.mode === "aes-cbc") {
411
+ throw new Error("IV is required for CBC mode decryption");
412
+ }
413
+ decrypted = import_crypto_js2.default.AES.decrypt(cipherText, keyWordArray, {
414
+ iv,
415
+ padding: paddingOption,
416
+ mode: import_crypto_js2.default.mode.CBC
417
+ });
418
+ break;
419
+ case "aes-ctr":
420
+ if (!iv && validatedOptions.mode === "aes-ctr") {
421
+ throw new Error("IV is required for CTR mode decryption");
422
+ }
423
+ decrypted = import_crypto_js2.default.AES.decrypt(cipherText, keyWordArray, {
424
+ iv,
425
+ padding: paddingOption,
426
+ mode: import_crypto_js2.default.mode.CTR
427
+ });
428
+ break;
429
+ case "aes-ecb":
430
+ console.warn(
431
+ "\u26A0\uFE0F WARNING: ECB mode does not provide semantic security and should not be used in most applications"
432
+ );
433
+ decrypted = import_crypto_js2.default.AES.decrypt(cipherText, keyWordArray, {
434
+ padding: paddingOption,
435
+ mode: import_crypto_js2.default.mode.ECB
436
+ });
437
+ break;
438
+ default:
439
+ throw new Error(`Unsupported encryption mode: ${validatedOptions.mode}`);
440
+ }
441
+ try {
442
+ return decrypted.toString(import_crypto_js2.default.enc.Utf8);
443
+ } catch {
444
+ throw new Error("Failed to decrypt: Invalid key or corrupted data");
445
+ }
446
+ }
447
+ function generateKey(length = 32) {
448
+ return import_crypto_js2.default.lib.WordArray.random(length).toString(import_crypto_js2.default.enc.Hex);
449
+ }
450
+
451
+ // src/utils/password.ts
452
+ function calculatePasswordEntropy(password) {
453
+ if (!password || password.length === 0) return 0;
454
+ let poolSize = 0;
455
+ const hasLowercase = /[a-z]/.test(password);
456
+ const hasUppercase = /[A-Z]/.test(password);
457
+ const hasDigits = /\d/.test(password);
458
+ const hasSymbols = /[^A-Za-z0-9]/.test(password);
459
+ if (hasLowercase) poolSize += 26;
460
+ if (hasUppercase) poolSize += 26;
461
+ if (hasDigits) poolSize += 10;
462
+ if (hasSymbols) poolSize += 33;
463
+ const entropy = password.length * Math.log2(poolSize);
464
+ return Math.round(entropy * 100) / 100;
465
+ }
466
+ function analyzePasswordPatterns(password) {
467
+ const patterns = [];
468
+ let entropyReduction = 0;
469
+ const keyboardPatterns = [
470
+ "qwerty",
471
+ "asdfgh",
472
+ "zxcvbn",
473
+ "qwertz",
474
+ "azerty",
475
+ "123456",
476
+ "654321"
477
+ ];
478
+ const sequentialCheck = (pwd) => {
479
+ for (let i = 0; i < pwd.length - 2; i++) {
480
+ const c1 = pwd.charCodeAt(i);
481
+ const c2 = pwd.charCodeAt(i + 1);
482
+ const c3 = pwd.charCodeAt(i + 2);
483
+ if (c1 + 1 === c2 && c2 + 1 === c3 || c1 - 1 === c2 && c2 - 1 === c3) {
484
+ return true;
485
+ }
486
+ }
487
+ return false;
488
+ };
489
+ const repeatedPatternCheck = (pwd) => {
490
+ if (pwd.length <= 2) return false;
491
+ for (let len = 2; len <= pwd.length / 2; len++) {
492
+ for (let i = 0; i <= pwd.length - len * 2; i++) {
493
+ const pattern = pwd.substring(i, i + len);
494
+ const nextPart = pwd.substring(i + len, i + len * 2);
495
+ if (pattern === nextPart) {
496
+ return true;
497
+ }
498
+ }
499
+ }
500
+ return false;
501
+ };
502
+ for (const pattern of keyboardPatterns) {
503
+ if (password.toLowerCase().includes(pattern)) {
504
+ patterns.push("Contains keyboard pattern");
505
+ entropyReduction += 0.2;
506
+ break;
507
+ }
508
+ }
509
+ if (sequentialCheck(password)) {
510
+ patterns.push("Contains sequential characters");
511
+ entropyReduction += 0.15;
512
+ }
513
+ if (repeatedPatternCheck(password)) {
514
+ patterns.push("Contains repeated patterns");
515
+ entropyReduction += 0.2;
516
+ }
517
+ const l33tMap = {
518
+ "4": "a",
519
+ "@": "a",
520
+ "8": "b",
521
+ "3": "e",
522
+ "6": "g",
523
+ "1": "i",
524
+ "!": "i",
525
+ "0": "o",
526
+ "9": "g",
527
+ $: "s",
528
+ "5": "s",
529
+ "+": "t",
530
+ "7": "t",
531
+ "%": "x",
532
+ "2": "z"
533
+ };
534
+ let transformedPassword = password.toLowerCase();
535
+ for (const [digit, letter] of Object.entries(l33tMap)) {
536
+ const safeDigit = digit.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
537
+ transformedPassword = transformedPassword.replace(
538
+ new RegExp(safeDigit, "g"),
539
+ letter
540
+ );
541
+ }
542
+ const commonWords = [
543
+ "password",
544
+ "admin",
545
+ "user",
546
+ "login",
547
+ "welcome",
548
+ "secure",
549
+ "secret"
550
+ ];
551
+ for (const word of commonWords) {
552
+ if (transformedPassword.includes(word)) {
553
+ patterns.push("Contains common word with substitutions");
554
+ entropyReduction += 0.25;
555
+ break;
556
+ }
557
+ }
558
+ if (/^[a-z]+$/.test(password) || /^[A-Z]+$/.test(password) || /^[0-9]+$/.test(password) || /^[^A-Za-z0-9]+$/.test(password)) {
559
+ patterns.push("Uses only one character type");
560
+ entropyReduction += 0.3;
561
+ }
562
+ entropyReduction = Math.min(entropyReduction, 0.9);
563
+ return { entropyReduction, patterns };
564
+ }
565
+ function checkPasswordStrength(password, config) {
566
+ const validatedConfig = ConfigSchema.parse({
567
+ minimumPasswordLength: config?.minimumPasswordLength || 12,
568
+ requireNumbers: config?.requireNumbers !== void 0 ? config.requireNumbers : true,
569
+ requireSymbols: config?.requireSymbols !== void 0 ? config.requireSymbols : true,
570
+ requireUppercase: config?.requireUppercase !== void 0 ? config.requireUppercase : true,
571
+ requireLowercase: config?.requireLowercase !== void 0 ? config.requireLowercase : true
572
+ });
573
+ const hasMinLength = password.length >= validatedConfig.minimumPasswordLength;
574
+ const hasNumbers = /\d/.test(password);
575
+ const hasSymbols = /[^A-Za-z0-9]/.test(password);
576
+ const hasUppercase = /[A-Z]/.test(password);
577
+ const hasLowercase = /[a-z]/.test(password);
578
+ const suggestions = [];
579
+ let warning = "";
580
+ let criteriaCount = 0;
581
+ if (hasMinLength) criteriaCount++;
582
+ if (hasNumbers && validatedConfig.requireNumbers) criteriaCount++;
583
+ if (hasSymbols && validatedConfig.requireSymbols) criteriaCount++;
584
+ if (hasUppercase && validatedConfig.requireUppercase) criteriaCount++;
585
+ if (hasLowercase && validatedConfig.requireLowercase) criteriaCount++;
586
+ if (!hasMinLength) {
587
+ warning = "Password is too short";
588
+ suggestions.push(
589
+ `Password should be at least ${validatedConfig.minimumPasswordLength} characters long`
590
+ );
591
+ }
592
+ if (validatedConfig.requireNumbers && !hasNumbers) {
593
+ suggestions.push("Add numbers to make your password stronger");
594
+ }
595
+ if (validatedConfig.requireSymbols && !hasSymbols) {
596
+ suggestions.push("Add symbols to make your password stronger");
597
+ }
598
+ if (validatedConfig.requireUppercase && !hasUppercase) {
599
+ suggestions.push("Add uppercase letters to make your password stronger");
600
+ }
601
+ if (validatedConfig.requireLowercase && !hasLowercase) {
602
+ suggestions.push("Add lowercase letters to make your password stronger");
603
+ }
604
+ const entropy = calculatePasswordEntropy(password);
605
+ const { entropyReduction, patterns } = analyzePasswordPatterns(password);
606
+ const effectiveEntropy = entropy * (1 - entropyReduction);
607
+ suggestions.push(
608
+ ...patterns.map((p) => `${p}: Consider a more random pattern`)
609
+ );
610
+ let score;
611
+ if (effectiveEntropy < 28) {
612
+ score = 0;
613
+ if (!warning) warning = "Very weak password";
614
+ } else if (effectiveEntropy < 36) {
615
+ score = 1;
616
+ if (!warning) warning = "Weak password";
617
+ } else if (effectiveEntropy < 60) {
618
+ score = 2;
619
+ } else if (effectiveEntropy < 80) {
620
+ score = 3;
621
+ } else {
622
+ score = 4;
623
+ }
624
+ const criteriaBoost = criteriaCount >= 5 ? 1 : 0;
625
+ score = Math.min(4, score + criteriaBoost);
626
+ if (!hasMinLength || validatedConfig.requireNumbers && !hasNumbers || validatedConfig.requireSymbols && !hasSymbols || validatedConfig.requireUppercase && !hasUppercase || validatedConfig.requireLowercase && !hasLowercase) {
627
+ score = Math.min(score, 2);
628
+ }
629
+ if (effectiveEntropy < 50) {
630
+ suggestions.push(
631
+ `Increase password entropy (currently ~${Math.round(
632
+ effectiveEntropy
633
+ )} bits)`
634
+ );
635
+ }
636
+ const isStrong = hasMinLength && (!validatedConfig.requireNumbers || hasNumbers) && (!validatedConfig.requireSymbols || hasSymbols) && (!validatedConfig.requireUppercase || hasUppercase) && (!validatedConfig.requireLowercase || hasLowercase) && score >= 3;
637
+ return PasswordStrengthSchema.parse({
638
+ score,
639
+ feedback: {
640
+ warning,
641
+ suggestions: suggestions.length > 0 ? suggestions : void 0
642
+ },
643
+ isStrong
644
+ });
645
+ }
646
+ function generateSecurePassword(config) {
647
+ const validatedConfig = ConfigSchema.parse({
648
+ minimumPasswordLength: config?.minimumPasswordLength || 12,
649
+ requireNumbers: config?.requireNumbers !== void 0 ? config.requireNumbers : true,
650
+ requireSymbols: config?.requireSymbols !== void 0 ? config.requireSymbols : true,
651
+ requireUppercase: config?.requireUppercase !== void 0 ? config.requireUppercase : true,
652
+ requireLowercase: config?.requireLowercase !== void 0 ? config.requireLowercase : true
653
+ });
654
+ const length = validatedConfig.minimumPasswordLength;
655
+ const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
656
+ const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
657
+ const numbers = "0123456789";
658
+ const symbols = "!@#$%^&*()_+-=[]{}|;:,.<>?";
659
+ let characters = "";
660
+ let password = "";
661
+ if (validatedConfig.requireLowercase) characters += lowercaseChars;
662
+ if (validatedConfig.requireUppercase) characters += uppercaseChars;
663
+ if (validatedConfig.requireNumbers) characters += numbers;
664
+ if (validatedConfig.requireSymbols) characters += symbols;
665
+ if (validatedConfig.requireLowercase) {
666
+ password += lowercaseChars.charAt(
667
+ Math.floor(Math.random() * lowercaseChars.length)
668
+ );
669
+ }
670
+ if (validatedConfig.requireUppercase) {
671
+ password += uppercaseChars.charAt(
672
+ Math.floor(Math.random() * uppercaseChars.length)
673
+ );
674
+ }
675
+ if (validatedConfig.requireNumbers) {
676
+ password += numbers.charAt(Math.floor(Math.random() * numbers.length));
677
+ }
678
+ if (validatedConfig.requireSymbols) {
679
+ password += symbols.charAt(Math.floor(Math.random() * symbols.length));
680
+ }
681
+ for (let i = password.length; i < length; i++) {
682
+ password += characters.charAt(
683
+ Math.floor(Math.random() * characters.length)
684
+ );
685
+ }
686
+ return shuffleString(password);
687
+ }
688
+ function shuffleString(str) {
689
+ const array = str.split("");
690
+ for (let i = array.length - 1; i > 0; i--) {
691
+ const j = Math.floor(Math.random() * (i + 1));
692
+ [array[i], array[j]] = [array[j], array[i]];
693
+ }
694
+ return array.join("");
695
+ }
696
+ // Annotate the CommonJS export names for ESM import in node:
697
+ 0 && (module.exports = {
698
+ ConfigSchema,
699
+ EncryptionOptionsSchema,
700
+ HashOptionsSchema,
701
+ PasswordHashOptionsSchema,
702
+ PasswordStrengthSchema,
703
+ SecurityLevel,
704
+ TimingSafeOptionsSchema,
705
+ analyzePasswordPatterns,
706
+ calculatePasswordEntropy,
707
+ checkPasswordStrength,
708
+ decrypt,
709
+ encrypt,
710
+ generateKey,
711
+ generateRandomBytes,
712
+ generateSecurePassword,
713
+ getSecurityLevel,
714
+ hash,
715
+ hashPassword,
716
+ hmac,
717
+ timingSafeEqual,
718
+ verifyHash,
719
+ verifyPassword
720
+ });