key-rotation-manager 1.0.10 → 1.1.1
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/README.md +6 -5
- package/dist/index.cjs +201 -234
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -18
- package/dist/index.d.ts +15 -18
- package/dist/index.js +202 -233
- package/dist/index.js.map +1 -1
- package/package.json +1 -5
package/README.md
CHANGED
|
@@ -521,13 +521,13 @@ const cloned = original.clone({
|
|
|
521
521
|
|
|
522
522
|
## 📖 API Reference
|
|
523
523
|
|
|
524
|
-
### `create(options?,
|
|
524
|
+
### `create(options?, singleton?)`
|
|
525
525
|
|
|
526
526
|
Creates a new KeyManager instance.
|
|
527
527
|
|
|
528
528
|
**Parameters:**
|
|
529
529
|
- `options` (optional): Partial `TModuleOptions`
|
|
530
|
-
- `
|
|
530
|
+
- `singleton` (optional): `boolean` - If `true`, returns a shared singleton instance (default: `false`)
|
|
531
531
|
|
|
532
532
|
**Returns:** `KM` instance
|
|
533
533
|
|
|
@@ -535,7 +535,7 @@ Creates a new KeyManager instance.
|
|
|
535
535
|
```typescript
|
|
536
536
|
const km = create({ versionGenerator: () => 'v1' });
|
|
537
537
|
// or
|
|
538
|
-
const km = create({},
|
|
538
|
+
const km = create({}, true); // Singleton instance
|
|
539
539
|
```
|
|
540
540
|
|
|
541
541
|
### `newKey(options, variables?)`
|
|
@@ -615,7 +615,8 @@ Sets a single hook.
|
|
|
615
615
|
|
|
616
616
|
See the full changelog for each version:
|
|
617
617
|
|
|
618
|
-
- **[v1.
|
|
618
|
+
- **[v1.1.1](https://github.com/DucAnh2611/key-rotation-manager/tree/master/changelogs/1.1.1.md)** - Native crypto, zero dependencies, bug fixes
|
|
619
|
+
- **[v1.0.10](https://github.com/DucAnh2611/key-rotation-manager/tree/master/changelogs/1.0.10.md)** - Hooks system, enhanced gitIgnore configuration
|
|
619
620
|
|
|
620
621
|
---
|
|
621
622
|
|
|
@@ -648,7 +649,7 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
648
649
|
|
|
649
650
|
## 📄 License
|
|
650
651
|
|
|
651
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
652
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/DucAnh2611/key-rotation-manager/blob/master/LICENSE) file for details.
|
|
652
653
|
|
|
653
654
|
---
|
|
654
655
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var EventEmitter = require('events');
|
|
4
3
|
var promises = require('fs/promises');
|
|
5
4
|
var path = require('path');
|
|
6
|
-
var
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
var EventEmitter = require('events');
|
|
7
7
|
|
|
8
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
|
|
10
10
|
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
|
|
11
|
-
var CryptoJS__default = /*#__PURE__*/_interopDefault(CryptoJS);
|
|
12
11
|
|
|
13
12
|
// src/constants/default.constant.ts
|
|
14
13
|
var DEFAULT_CRYPTO_OPTIONS = {
|
|
@@ -59,99 +58,6 @@ var EEvent = /* @__PURE__ */ ((EEvent2) => {
|
|
|
59
58
|
return EEvent2;
|
|
60
59
|
})(EEvent || {});
|
|
61
60
|
|
|
62
|
-
// src/types/module.types.ts
|
|
63
|
-
var EError = /* @__PURE__ */ ((EError2) => {
|
|
64
|
-
return EError2;
|
|
65
|
-
})(EError || {});
|
|
66
|
-
|
|
67
|
-
// src/core/base.core.ts
|
|
68
|
-
var Base = class {
|
|
69
|
-
logger;
|
|
70
|
-
bOptions;
|
|
71
|
-
hooks = /* @__PURE__ */ new Map();
|
|
72
|
-
constructor(options) {
|
|
73
|
-
this.bOptions = { ...DEFAULT_BASE_OPTIONS, ...options };
|
|
74
|
-
this.logger = DEFAULT_BASE_LOGGER;
|
|
75
|
-
}
|
|
76
|
-
getLogger() {
|
|
77
|
-
return this.logger;
|
|
78
|
-
}
|
|
79
|
-
setLogger(logger) {
|
|
80
|
-
this.logger = logger;
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
async sysLog(...args) {
|
|
84
|
-
if (!this.bOptions.quiet) await this.logger(...args);
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
|
-
async customLog(logger, ...args) {
|
|
88
|
-
await logger(...args);
|
|
89
|
-
return this;
|
|
90
|
-
}
|
|
91
|
-
setHooks(hooks) {
|
|
92
|
-
Object.entries(hooks).forEach(([name, handler]) => {
|
|
93
|
-
if (this.hooks.has(name)) {
|
|
94
|
-
this.hooks.get("onHookOverriding")?.call(this, name);
|
|
95
|
-
}
|
|
96
|
-
this.hooks.set(name, handler);
|
|
97
|
-
});
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
runHook(name, ...args) {
|
|
101
|
-
if (!this.hooks.has(name)) {
|
|
102
|
-
this.hooks.get("onHookNotFound")?.call(this, name);
|
|
103
|
-
return void 0;
|
|
104
|
-
}
|
|
105
|
-
return this.hooks.get(name).call(this, ...args);
|
|
106
|
-
}
|
|
107
|
-
getHooks() {
|
|
108
|
-
return Object.fromEntries(this.hooks.entries());
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// src/core/events.core.ts
|
|
113
|
-
var Events = class extends Base {
|
|
114
|
-
events;
|
|
115
|
-
eOptions;
|
|
116
|
-
constructor(options) {
|
|
117
|
-
super(options);
|
|
118
|
-
this.eOptions = {
|
|
119
|
-
...DEFAULT_EVENTS_OPTIONS,
|
|
120
|
-
...options
|
|
121
|
-
};
|
|
122
|
-
if (this.eOptions.useEvent) {
|
|
123
|
-
this.events = new EventEmitter__default.default();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
emit(event, args) {
|
|
127
|
-
if (!this.events) return this;
|
|
128
|
-
this.events.emit(event, args);
|
|
129
|
-
return this;
|
|
130
|
-
}
|
|
131
|
-
on(event, listener) {
|
|
132
|
-
if (!this.events) return this;
|
|
133
|
-
this.events.on(event, listener);
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
once(event, listener) {
|
|
137
|
-
if (!this.events) return this;
|
|
138
|
-
this.events.once(event, listener);
|
|
139
|
-
return this;
|
|
140
|
-
}
|
|
141
|
-
off(event, listener) {
|
|
142
|
-
if (!this.events) return this;
|
|
143
|
-
this.events.off(event, listener);
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// src/core/config-events.core.ts
|
|
149
|
-
var ConfigEvents = class extends Events {
|
|
150
|
-
constructor(options) {
|
|
151
|
-
super(options);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
61
|
// src/utils/promise.util.ts
|
|
156
62
|
var executePromisably = (promiseOrFn) => {
|
|
157
63
|
try {
|
|
@@ -161,35 +67,45 @@ var executePromisably = (promiseOrFn) => {
|
|
|
161
67
|
return Promise.reject(error);
|
|
162
68
|
}
|
|
163
69
|
};
|
|
70
|
+
var FileUtilError = class extends Error {
|
|
71
|
+
constructor(message, path, operation, cause) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.path = path;
|
|
74
|
+
this.operation = operation;
|
|
75
|
+
this.cause = cause;
|
|
76
|
+
this.name = "FileUtilError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
164
79
|
var FileUtil = class {
|
|
165
80
|
async getFolder(path) {
|
|
166
81
|
try {
|
|
82
|
+
await promises.access(path);
|
|
83
|
+
return path;
|
|
84
|
+
} catch {
|
|
167
85
|
try {
|
|
168
|
-
await promises.access(path);
|
|
169
|
-
} catch {
|
|
170
86
|
await promises.mkdir(path, { recursive: true });
|
|
87
|
+
return path;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new FileUtilError(`Failed to create folder: ${path}`, path, "mkdir", error);
|
|
171
90
|
}
|
|
172
|
-
return path;
|
|
173
|
-
} catch {
|
|
174
|
-
throw new Error(`Failed to get folder: ${path}`);
|
|
175
91
|
}
|
|
176
92
|
}
|
|
177
|
-
async read(path, fallback
|
|
93
|
+
async read(path, fallback) {
|
|
178
94
|
try {
|
|
179
95
|
const data = await promises.readFile(path, { encoding: "utf8" });
|
|
180
96
|
return data;
|
|
181
97
|
} catch (error) {
|
|
182
|
-
if (fallback) return fallback;
|
|
183
|
-
throw error;
|
|
98
|
+
if (fallback !== void 0) return fallback;
|
|
99
|
+
throw new FileUtilError(`Failed to read file: ${path}`, path, "read", error);
|
|
184
100
|
}
|
|
185
101
|
}
|
|
186
102
|
async write(path$1, data, flag = "w") {
|
|
103
|
+
const dir = path.dirname(path$1);
|
|
104
|
+
await this.getFolder(dir);
|
|
187
105
|
try {
|
|
188
|
-
const dir = path.dirname(path$1);
|
|
189
|
-
await this.getFolder(dir);
|
|
190
106
|
await promises.writeFile(path$1, data, { encoding: "utf8", flag });
|
|
191
107
|
} catch (error) {
|
|
192
|
-
throw new
|
|
108
|
+
throw new FileUtilError(`Failed to write file: ${path$1}`, path$1, "write", error);
|
|
193
109
|
}
|
|
194
110
|
}
|
|
195
111
|
async checkExists(path) {
|
|
@@ -197,14 +113,14 @@ var FileUtil = class {
|
|
|
197
113
|
await promises.access(path);
|
|
198
114
|
return true;
|
|
199
115
|
} catch {
|
|
200
|
-
|
|
116
|
+
return false;
|
|
201
117
|
}
|
|
202
118
|
}
|
|
203
119
|
async delete(path) {
|
|
204
120
|
try {
|
|
205
121
|
await promises.unlink(path);
|
|
206
|
-
} catch {
|
|
207
|
-
throw new
|
|
122
|
+
} catch (error) {
|
|
123
|
+
throw new FileUtilError(`Failed to delete file: ${path}`, path, "delete", error);
|
|
208
124
|
}
|
|
209
125
|
}
|
|
210
126
|
};
|
|
@@ -268,7 +184,7 @@ var isType = (data) => {
|
|
|
268
184
|
return {
|
|
269
185
|
number: typeof data === "number" && !Number.isNaN(Number(data)),
|
|
270
186
|
string: typeof data === "string",
|
|
271
|
-
stringNumber: typeof data === "string"
|
|
187
|
+
stringNumber: typeof data === "string" || typeof data === "number",
|
|
272
188
|
boolean: typeof data === "boolean",
|
|
273
189
|
null: data === null,
|
|
274
190
|
undefined: data === void 0
|
|
@@ -279,97 +195,77 @@ var CryptoService = class {
|
|
|
279
195
|
constructor(options = {}) {
|
|
280
196
|
this.options = { ...DEFAULT_CRYPTO_OPTIONS, ...options };
|
|
281
197
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Generate random bytes and encode them
|
|
200
|
+
*/
|
|
285
201
|
generateRandom(length = 32) {
|
|
286
|
-
const
|
|
287
|
-
return this.
|
|
202
|
+
const buffer = crypto.randomBytes(length);
|
|
203
|
+
return this.encodeBuffer(buffer);
|
|
288
204
|
}
|
|
289
|
-
|
|
205
|
+
/**
|
|
290
206
|
* Generate a random key
|
|
291
|
-
* @param length Length of the key
|
|
207
|
+
* @param length Length of the key in bytes
|
|
292
208
|
* @default cryptoOptions.keyLength
|
|
293
|
-
* @returns { key: string, length: number }
|
|
294
209
|
*/
|
|
295
210
|
generateKey(length = this.options.keyLength) {
|
|
296
211
|
return { key: this.generateRandom(length), length };
|
|
297
212
|
}
|
|
298
|
-
|
|
213
|
+
/**
|
|
299
214
|
* Generate a random salt
|
|
300
|
-
* @param length Length of the salt
|
|
215
|
+
* @param length Length of the salt in bytes
|
|
301
216
|
* @default cryptoOptions.saltLength
|
|
302
|
-
* @returns { salt: string, length: number }
|
|
303
217
|
*/
|
|
304
218
|
generateSalt(length = this.options.saltLength) {
|
|
305
219
|
return { salt: this.generateRandom(length), length };
|
|
306
220
|
}
|
|
307
|
-
|
|
221
|
+
/**
|
|
308
222
|
* Generate a random IV
|
|
309
|
-
* @param length Length of the IV
|
|
223
|
+
* @param length Length of the IV in bytes
|
|
310
224
|
* @default cryptoOptions.ivLength
|
|
311
|
-
* @returns { iv: string, length: number }
|
|
312
225
|
*/
|
|
313
226
|
generateIV(length = this.options.ivLength) {
|
|
314
227
|
return { iv: this.generateRandom(length), length };
|
|
315
228
|
}
|
|
316
|
-
|
|
229
|
+
encodeBuffer(buffer) {
|
|
317
230
|
switch (this.options.encoding) {
|
|
318
231
|
case "hex":
|
|
319
|
-
return
|
|
232
|
+
return buffer.toString("hex");
|
|
320
233
|
case "base64url":
|
|
321
|
-
return
|
|
234
|
+
return buffer.toString("base64url");
|
|
322
235
|
case "base64":
|
|
323
236
|
default:
|
|
324
|
-
return
|
|
237
|
+
return buffer.toString("base64");
|
|
325
238
|
}
|
|
326
239
|
}
|
|
327
|
-
|
|
240
|
+
decodeToBuffer(encoded) {
|
|
328
241
|
switch (this.options.encoding) {
|
|
329
242
|
case "hex":
|
|
330
|
-
return
|
|
243
|
+
return Buffer.from(encoded, "hex");
|
|
331
244
|
case "base64url":
|
|
332
|
-
|
|
333
|
-
const padding = "=".repeat((4 - base64.length % 4) % 4);
|
|
334
|
-
return CryptoJS__default.default.enc.Base64.parse(base64 + padding);
|
|
245
|
+
return Buffer.from(encoded, "base64url");
|
|
335
246
|
case "base64":
|
|
336
247
|
default:
|
|
337
|
-
return
|
|
248
|
+
return Buffer.from(encoded, "base64");
|
|
338
249
|
}
|
|
339
250
|
}
|
|
340
251
|
deriveKey(password, salt, keyLength) {
|
|
341
252
|
if (this.options.kdf === "none") {
|
|
342
|
-
return
|
|
253
|
+
return Buffer.from(password, "hex");
|
|
343
254
|
}
|
|
344
255
|
const actualKeyLength = keyLength ?? this.options.keyLength;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
getHasher() {
|
|
353
|
-
switch (this.options.hashAlgorithm) {
|
|
354
|
-
case "sha512":
|
|
355
|
-
return CryptoJS__default.default.algo.SHA512;
|
|
356
|
-
case "sha384":
|
|
357
|
-
return CryptoJS__default.default.algo.SHA384;
|
|
358
|
-
case "sha256":
|
|
359
|
-
default:
|
|
360
|
-
return CryptoJS__default.default.algo.SHA256;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
getAESMode() {
|
|
364
|
-
return CryptoJS__default.default.mode.CBC;
|
|
365
|
-
}
|
|
366
|
-
getPadding() {
|
|
367
|
-
return CryptoJS__default.default.pad.Pkcs7;
|
|
256
|
+
return crypto.pbkdf2Sync(
|
|
257
|
+
password,
|
|
258
|
+
salt,
|
|
259
|
+
this.options.iterations,
|
|
260
|
+
actualKeyLength,
|
|
261
|
+
this.options.hashAlgorithm
|
|
262
|
+
);
|
|
368
263
|
}
|
|
369
264
|
/**
|
|
370
|
-
* Encrypt data
|
|
265
|
+
* Encrypt data using AES-CBC
|
|
371
266
|
* @param plainText Text to encrypt
|
|
372
|
-
* @param secret Secret key for encryption
|
|
267
|
+
* @param secret Secret key for encryption
|
|
268
|
+
* @param options Optional lengths for key, salt, and IV
|
|
373
269
|
*/
|
|
374
270
|
encrypt(plainText, secret, {
|
|
375
271
|
keyLength,
|
|
@@ -379,61 +275,48 @@ var CryptoService = class {
|
|
|
379
275
|
keyLength = keyLength ?? this.options.keyLength;
|
|
380
276
|
saltLength = saltLength ?? this.options.saltLength;
|
|
381
277
|
ivLength = ivLength ?? this.options.ivLength;
|
|
382
|
-
const salt = this.options.kdf !== "none" ?
|
|
383
|
-
const iv =
|
|
278
|
+
const salt = this.options.kdf !== "none" ? crypto.randomBytes(saltLength) : Buffer.alloc(0);
|
|
279
|
+
const iv = crypto.randomBytes(ivLength);
|
|
384
280
|
const key = this.deriveKey(secret, salt, keyLength);
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
const ivHex = iv.toString(CryptoJS__default.default.enc.Hex);
|
|
396
|
-
const encryptedHex = encrypted.ciphertext.toString(CryptoJS__default.default.enc.Hex);
|
|
397
|
-
const combined = keyLengthHex + saltLengthHex + ivLengthHex + saltHex + ivHex + encryptedHex;
|
|
398
|
-
return this.encode(CryptoJS__default.default.enc.Hex.parse(combined));
|
|
281
|
+
const cipher = crypto.createCipheriv(this.options.algorithm, key, iv);
|
|
282
|
+
const encrypted = Buffer.concat([cipher.update(plainText, "utf8"), cipher.final()]);
|
|
283
|
+
const keyLengthBuf = Buffer.alloc(2);
|
|
284
|
+
keyLengthBuf.writeUInt16BE(keyLength);
|
|
285
|
+
const saltLengthBuf = Buffer.alloc(2);
|
|
286
|
+
saltLengthBuf.writeUInt16BE(salt.length);
|
|
287
|
+
const ivLengthBuf = Buffer.alloc(2);
|
|
288
|
+
ivLengthBuf.writeUInt16BE(ivLength);
|
|
289
|
+
const combined = Buffer.concat([keyLengthBuf, saltLengthBuf, ivLengthBuf, salt, iv, encrypted]);
|
|
290
|
+
return this.encodeBuffer(combined);
|
|
399
291
|
}
|
|
400
292
|
/**
|
|
401
293
|
* Decrypt data
|
|
402
294
|
* @param encryptedData Encrypted data to decrypt
|
|
403
295
|
* @param secret Secret key for decryption
|
|
296
|
+
* @returns Decrypted string, or empty string if decryption fails (wrong password/corrupted data)
|
|
404
297
|
*/
|
|
405
298
|
decrypt(encryptedData, secret) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const encrypted = CryptoJS__default.default.lib.CipherParams.create({
|
|
428
|
-
ciphertext: CryptoJS__default.default.enc.Hex.parse(encryptedHex)
|
|
429
|
-
});
|
|
430
|
-
const key = this.deriveKey(secret, salt, keyLength);
|
|
431
|
-
const decrypted = CryptoJS__default.default.AES.decrypt(encrypted, key, {
|
|
432
|
-
iv,
|
|
433
|
-
mode: this.getAESMode(),
|
|
434
|
-
padding: this.getPadding()
|
|
435
|
-
});
|
|
436
|
-
return decrypted.toString(CryptoJS__default.default.enc.Utf8);
|
|
299
|
+
try {
|
|
300
|
+
const combined = this.decodeToBuffer(encryptedData);
|
|
301
|
+
let offset = 0;
|
|
302
|
+
const keyLength = combined.readUInt16BE(offset);
|
|
303
|
+
offset += 2;
|
|
304
|
+
const saltLength = combined.readUInt16BE(offset);
|
|
305
|
+
offset += 2;
|
|
306
|
+
const ivLength = combined.readUInt16BE(offset);
|
|
307
|
+
offset += 2;
|
|
308
|
+
const salt = combined.subarray(offset, offset + saltLength);
|
|
309
|
+
offset += saltLength;
|
|
310
|
+
const iv = combined.subarray(offset, offset + ivLength);
|
|
311
|
+
offset += ivLength;
|
|
312
|
+
const encrypted = combined.subarray(offset);
|
|
313
|
+
const key = this.deriveKey(secret, salt, keyLength);
|
|
314
|
+
const decipher = crypto.createDecipheriv(this.options.algorithm, key, iv);
|
|
315
|
+
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
316
|
+
return decrypted.toString("utf8");
|
|
317
|
+
} catch {
|
|
318
|
+
return "";
|
|
319
|
+
}
|
|
437
320
|
}
|
|
438
321
|
/**
|
|
439
322
|
* Hash data (one-way)
|
|
@@ -442,19 +325,17 @@ var CryptoService = class {
|
|
|
442
325
|
* @param salt Optional encoded salt string for deterministic hashing
|
|
443
326
|
*/
|
|
444
327
|
hash(data, secret, salt) {
|
|
445
|
-
|
|
446
|
-
const hasher = this.getHasher();
|
|
447
|
-
let saltWordArray;
|
|
328
|
+
let saltBuffer;
|
|
448
329
|
let saltStr;
|
|
449
330
|
if (salt) {
|
|
450
|
-
|
|
331
|
+
saltBuffer = this.decodeToBuffer(salt);
|
|
451
332
|
saltStr = salt;
|
|
452
333
|
} else {
|
|
453
|
-
|
|
454
|
-
saltStr = this.
|
|
334
|
+
saltBuffer = crypto.randomBytes(this.options.saltLength);
|
|
335
|
+
saltStr = this.encodeBuffer(saltBuffer);
|
|
455
336
|
}
|
|
456
|
-
const hash =
|
|
457
|
-
const hashStr = this.
|
|
337
|
+
const hash = crypto.createHash(this.options.hashAlgorithm).update(data).update(secret).update(saltBuffer).digest();
|
|
338
|
+
const hashStr = this.encodeBuffer(hash);
|
|
458
339
|
return `${saltStr}:${hashStr}`;
|
|
459
340
|
}
|
|
460
341
|
/**
|
|
@@ -468,15 +349,102 @@ var CryptoService = class {
|
|
|
468
349
|
if (!saltStr || !expectedHashStr) {
|
|
469
350
|
return false;
|
|
470
351
|
}
|
|
471
|
-
const
|
|
472
|
-
const
|
|
473
|
-
const
|
|
474
|
-
const hash = hasher.create().update(data).update(secretWordArray).update(salt).finalize();
|
|
475
|
-
const hashStr = this.encode(hash);
|
|
352
|
+
const saltBuffer = this.decodeToBuffer(saltStr);
|
|
353
|
+
const hash = crypto.createHash(this.options.hashAlgorithm).update(data).update(secret).update(saltBuffer).digest();
|
|
354
|
+
const hashStr = this.encodeBuffer(hash);
|
|
476
355
|
return hashStr === expectedHashStr;
|
|
477
356
|
}
|
|
478
357
|
};
|
|
479
|
-
|
|
358
|
+
|
|
359
|
+
// src/core/base.core.ts
|
|
360
|
+
var Base = class {
|
|
361
|
+
logger;
|
|
362
|
+
bOptions;
|
|
363
|
+
hooks = /* @__PURE__ */ new Map();
|
|
364
|
+
constructor(options) {
|
|
365
|
+
this.bOptions = { ...DEFAULT_BASE_OPTIONS, ...options };
|
|
366
|
+
this.logger = DEFAULT_BASE_LOGGER;
|
|
367
|
+
}
|
|
368
|
+
getLogger() {
|
|
369
|
+
return this.logger;
|
|
370
|
+
}
|
|
371
|
+
setLogger(logger) {
|
|
372
|
+
this.logger = logger;
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
sysLog(...args) {
|
|
376
|
+
if (!this.bOptions.quiet) {
|
|
377
|
+
const result = this.logger(...args);
|
|
378
|
+
if (result && typeof result === "object" && "catch" in result) {
|
|
379
|
+
result.catch(() => {
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
async customLog(logger, ...args) {
|
|
386
|
+
await logger(...args);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
setHooks(hooks) {
|
|
390
|
+
Object.entries(hooks).forEach(([name, handler]) => {
|
|
391
|
+
if (this.hooks.has(name)) {
|
|
392
|
+
this.hooks.get("onHookOverriding")?.call(this, name);
|
|
393
|
+
}
|
|
394
|
+
this.hooks.set(name, handler);
|
|
395
|
+
});
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
runHook(name, ...args) {
|
|
399
|
+
if (!this.hooks.has(name)) {
|
|
400
|
+
this.hooks.get("onHookNotFound")?.call(this, name);
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
return this.hooks.get(name).call(this, ...args);
|
|
404
|
+
}
|
|
405
|
+
getHooks() {
|
|
406
|
+
return Object.fromEntries(this.hooks.entries());
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/core/events.core.ts
|
|
411
|
+
var Events = class extends Base {
|
|
412
|
+
events;
|
|
413
|
+
eOptions;
|
|
414
|
+
constructor(options) {
|
|
415
|
+
super(options);
|
|
416
|
+
this.eOptions = {
|
|
417
|
+
...DEFAULT_EVENTS_OPTIONS,
|
|
418
|
+
...options
|
|
419
|
+
};
|
|
420
|
+
if (this.eOptions.useEvent) {
|
|
421
|
+
this.events = new EventEmitter__default.default();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
emit(event, args) {
|
|
425
|
+
if (!this.events) return this;
|
|
426
|
+
this.events.emit(event, args);
|
|
427
|
+
return this;
|
|
428
|
+
}
|
|
429
|
+
on(event, listener) {
|
|
430
|
+
if (!this.events) return this;
|
|
431
|
+
this.events.on(event, listener);
|
|
432
|
+
return this;
|
|
433
|
+
}
|
|
434
|
+
once(event, listener) {
|
|
435
|
+
if (!this.events) return this;
|
|
436
|
+
this.events.once(event, listener);
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
off(event, listener) {
|
|
440
|
+
if (!this.events) return this;
|
|
441
|
+
this.events.off(event, listener);
|
|
442
|
+
return this;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/core/store.core.ts
|
|
447
|
+
var Store = class extends Events {
|
|
480
448
|
sOptions;
|
|
481
449
|
fileUtil;
|
|
482
450
|
storePath;
|
|
@@ -663,21 +631,21 @@ var KeyManager = class extends Store {
|
|
|
663
631
|
* console.log('Key was rotated from version:', expired.version);
|
|
664
632
|
* }
|
|
665
633
|
*
|
|
666
|
-
* // Use expired
|
|
634
|
+
* // Use expired?.key ?? ready?.key safely
|
|
667
635
|
* ```
|
|
668
636
|
*/
|
|
669
637
|
async getKey(options) {
|
|
670
638
|
const { path, version } = options;
|
|
671
639
|
const key = await this.getKeyByStore(path, String(version));
|
|
672
640
|
if (!key) {
|
|
673
|
-
|
|
641
|
+
this.runKeyHook("onKeyNotFound", path, version);
|
|
674
642
|
this.sysLog(`Key not found!`, { path, version });
|
|
675
643
|
return { expired: null, ready: null };
|
|
676
644
|
}
|
|
677
645
|
const { ok, message, isExpired, isRenewable, errorOn } = this.validateKey(key);
|
|
678
646
|
if (!ok && isExpired && isRenewable && key) {
|
|
679
647
|
if (!options.onRotate) {
|
|
680
|
-
|
|
648
|
+
this.runKeyHook("onKeyMissingRotateOption", key, options);
|
|
681
649
|
this.sysLog(`Key missing rotate option!`, { path, version });
|
|
682
650
|
return { expired: null, ready: null };
|
|
683
651
|
}
|
|
@@ -686,17 +654,17 @@ var KeyManager = class extends Store {
|
|
|
686
654
|
...options.onRotate
|
|
687
655
|
});
|
|
688
656
|
const resGetKey = { expired: key, ready: renew.key };
|
|
689
|
-
|
|
657
|
+
this.runKeyHook("onKeyRenewed", resGetKey, options);
|
|
690
658
|
this.sysLog(`Key renewed!`, { path, version });
|
|
691
659
|
return resGetKey;
|
|
692
660
|
}
|
|
693
661
|
if (!ok && isExpired && !isRenewable && key) {
|
|
694
|
-
|
|
662
|
+
this.runKeyHook("onKeyExpired", path, key);
|
|
695
663
|
this.sysLog(`Key expired!`, { path, version });
|
|
696
664
|
return { expired: key, ready: null };
|
|
697
665
|
}
|
|
698
666
|
if (!ok) {
|
|
699
|
-
|
|
667
|
+
this.runKeyHook("onKeyInvalid", key, message, errorOn);
|
|
700
668
|
this.sysLog(`Key invalid!`, { path, version });
|
|
701
669
|
return { expired: null, ready: null };
|
|
702
670
|
}
|
|
@@ -743,7 +711,7 @@ var KeyManager = class extends Store {
|
|
|
743
711
|
const { rotate, duration, type, unit, merge, keyLength } = options;
|
|
744
712
|
const { key, length: kLength } = this.cryptoService.generateKey(keyLength);
|
|
745
713
|
const { salt } = this.cryptoService.generateSalt();
|
|
746
|
-
|
|
714
|
+
this.sysLog(`Key generated
|
|
747
715
|
Options:`, options);
|
|
748
716
|
const hashedKey = this.cryptoService.hash(key, salt);
|
|
749
717
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -758,7 +726,7 @@ Options:`, options);
|
|
|
758
726
|
rotate: !!rotate
|
|
759
727
|
};
|
|
760
728
|
const path = await this.saveKeyToStore(keyGenerated, !!merge, variables);
|
|
761
|
-
|
|
729
|
+
this.sysLog(`Key saved!`, {
|
|
762
730
|
path,
|
|
763
731
|
version: keyGenerated.version,
|
|
764
732
|
type: keyGenerated.type
|
|
@@ -788,7 +756,7 @@ Options:`, options);
|
|
|
788
756
|
hashed: "string",
|
|
789
757
|
rotate: "boolean",
|
|
790
758
|
type: "string",
|
|
791
|
-
version: "
|
|
759
|
+
version: "stringNumber",
|
|
792
760
|
hashedBytes: "number"
|
|
793
761
|
};
|
|
794
762
|
for (const [field, type] of Object.entries(typeChecks)) {
|
|
@@ -883,8 +851,8 @@ var KM = class _KM extends KeyManager {
|
|
|
883
851
|
|
|
884
852
|
// src/index.ts
|
|
885
853
|
var instance = null;
|
|
886
|
-
var create = (options = {},
|
|
887
|
-
if (
|
|
854
|
+
var create = (options = {}, singleton = false) => {
|
|
855
|
+
if (!singleton) return new KM(options);
|
|
888
856
|
if (!instance) {
|
|
889
857
|
instance = new KM(options);
|
|
890
858
|
}
|
|
@@ -892,7 +860,6 @@ var create = (options = {}, only = true) => {
|
|
|
892
860
|
};
|
|
893
861
|
var km = create;
|
|
894
862
|
|
|
895
|
-
exports.EError = EError;
|
|
896
863
|
exports.EEvent = EEvent;
|
|
897
864
|
exports.create = create;
|
|
898
865
|
exports.km = km;
|