tiny-essentials 1.0.0 → 1.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/README.md +21 -13
- package/dist/TinyEssentials.min.js +1 -1
- package/dist/v1/basics/objFilter.cjs +212 -14
- package/dist/v1/basics/objFilter.d.mts +60 -0
- package/dist/v1/basics/objFilter.mjs +180 -15
- package/dist/v1/index.cjs +5 -0
- package/dist/v1/index.d.mts +5 -1
- package/dist/v1/index.mjs +3 -2
- package/dist/v1/libs/TinyCrypto.cjs +596 -0
- package/dist/v1/libs/TinyCrypto.d.mts +267 -0
- package/dist/v1/libs/TinyCrypto.mjs +572 -0
- package/docs/README.md +39 -0
- package/docs/basics/array.md +46 -0
- package/docs/basics/clock.md +77 -0
- package/docs/basics/objFilter.md +120 -0
- package/docs/basics/simpleMath.md +65 -0
- package/docs/basics/text.md +38 -0
- package/docs/libs/TinyCrypto.md +177 -0
- package/package.json +16 -2
package/dist/v1/index.mjs
CHANGED
|
@@ -2,7 +2,8 @@ import asyncReplace from '../legacy/libs/replaceAsync.mjs';
|
|
|
2
2
|
import TinyLevelUp from '../legacy/libs/userLevel.mjs';
|
|
3
3
|
import { shuffleArray } from './basics/array.mjs';
|
|
4
4
|
import { formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, } from './basics/clock.mjs';
|
|
5
|
-
import { countObj, objType } from './basics/objFilter.mjs';
|
|
5
|
+
import { countObj, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, objType, } from './basics/objFilter.mjs';
|
|
6
6
|
import { getAge, getSimplePerc, ruleOfThree } from './basics/simpleMath.mjs';
|
|
7
7
|
import { toTitleCase, toTitleCaseLowerFirst } from './basics/text.mjs';
|
|
8
|
-
|
|
8
|
+
import TinyCrypto from './libs/TinyCrypto.mjs';
|
|
9
|
+
export { TinyCrypto, TinyLevelUp, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst, };
|
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var objFilter = require('../basics/objFilter.cjs');
|
|
6
|
+
|
|
7
|
+
// Detecta se estamos no browser
|
|
8
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* TinyCrypto is a utility class that provides methods for secure key generation,
|
|
12
|
+
* encryption, and decryption of data. It also allows for serialization
|
|
13
|
+
* and deserialization of complex data types, and offers methods to save and load encryption
|
|
14
|
+
* configurations and keys from files.
|
|
15
|
+
*
|
|
16
|
+
* @class
|
|
17
|
+
*/
|
|
18
|
+
class TinyCrypto {
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new instance of the CryptoManager class with configurable options.
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} [options={}] - Configuration options for encryption and decryption.
|
|
23
|
+
* @param {string} [options.algorithm='aes-256-gcm'] - The encryption algorithm to use. Recommended: 'aes-256-gcm' for authenticated encryption.
|
|
24
|
+
* @param {string} [options.outputEncoding='hex'] - The encoding used when returning encrypted data (e.g., 'hex', 'base64').
|
|
25
|
+
* @param {string} [options.inputEncoding='utf8'] - The encoding used for plaintext inputs (e.g., 'utf8').
|
|
26
|
+
* @param {number} [options.authTagLength=16] - The length of the authentication tag used in GCM mode. Usually 16 for AES-256-GCM.
|
|
27
|
+
* @param {Buffer} [options.key] - Optional 32-byte cryptographic key. If not provided, a random key is generated.
|
|
28
|
+
*
|
|
29
|
+
* @throws {Error} Throws if the provided key is not 32 bytes long.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const crypto = new CryptoManager({
|
|
33
|
+
* algorithm: 'aes-256-gcm',
|
|
34
|
+
* outputEncoding: 'base64',
|
|
35
|
+
* key: crypto.randomBytes(32),
|
|
36
|
+
* });
|
|
37
|
+
*/
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
this.algorithm = options.algorithm || 'aes-256-gcm';
|
|
40
|
+
this.outputEncoding = options.outputEncoding || 'hex';
|
|
41
|
+
this.inputEncoding = options.inputEncoding || 'utf8';
|
|
42
|
+
this.authTagLength = options.authTagLength || 16;
|
|
43
|
+
this.key = options.key || this.generateKey();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generates a secure random cryptographic key.
|
|
48
|
+
*
|
|
49
|
+
* @param {number} [value=32] - The number of bytes to generate. Default is 32 bytes (256 bits), suitable for AES-256.
|
|
50
|
+
* @returns {Buffer} A securely generated random key as a Buffer.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const key = cryptoManager.generateKey(); // Generates a 32-byte key
|
|
54
|
+
* const customKey = cryptoManager.generateKey(16); // Generates a 16-byte key (e.g. for AES-128)
|
|
55
|
+
*/
|
|
56
|
+
generateKey(value = 32) {
|
|
57
|
+
return crypto.randomBytes(value); // 256-bit
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generates a secure random Initialization Vector (IV).
|
|
62
|
+
*
|
|
63
|
+
* @param {number} [value=12] - The number of bytes to generate. Default is 12 bytes (96 bits), the recommended size for AES-GCM.
|
|
64
|
+
* @returns {Buffer} A securely generated IV as a Buffer.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* const iv = cryptoManager.generateIV(); // Generates a 12-byte IV
|
|
68
|
+
* const customIV = cryptoManager.generateIV(16); // Generates a 16-byte IV if needed for other algorithms
|
|
69
|
+
*/
|
|
70
|
+
generateIV(value = 12) {
|
|
71
|
+
return crypto.randomBytes(value); // 96-bit padrão para GCM
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Encrypts a given value (string, number, object, etc.)
|
|
76
|
+
*
|
|
77
|
+
* The value is first serialized de forma segura (preservando o tipo) antes da criptografia.
|
|
78
|
+
*
|
|
79
|
+
* @param {*} data - The data to encrypt. Can be of any supported type (string, number, boolean, Date, JSON, etc.).
|
|
80
|
+
* @param {Buffer} [iv=this.generateIV()] - Optional Initialization Vector (IV). If not provided, a secure random IV is generated.
|
|
81
|
+
* @returns {Object} An object containing:
|
|
82
|
+
* - `iv` {string} - The IV used, encoded with the output encoding.
|
|
83
|
+
* - `encrypted` {string} - The encrypted payload.
|
|
84
|
+
* - `authTag` {string} - The authentication tag used to verify the integrity of the ciphertext.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* const result = cryptoManager.encrypt('Hello, world!');
|
|
88
|
+
* // {
|
|
89
|
+
* // iv: 'b32a...',
|
|
90
|
+
* // encrypted: 'c1d5...',
|
|
91
|
+
* // authTag: 'aa93...'
|
|
92
|
+
* // }
|
|
93
|
+
*/
|
|
94
|
+
encrypt(data, iv = this.generateIV()) {
|
|
95
|
+
const plainText = this.#serialize(data);
|
|
96
|
+
|
|
97
|
+
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv, {
|
|
98
|
+
authTagLength: this.authTagLength,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
let encrypted = cipher.update(plainText, this.inputEncoding);
|
|
102
|
+
encrypted = Buffer.concat([encrypted, cipher.final()]);
|
|
103
|
+
const authTag = cipher.getAuthTag();
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
iv: iv.toString(this.outputEncoding),
|
|
107
|
+
encrypted: encrypted.toString(this.outputEncoding),
|
|
108
|
+
authTag: authTag.toString(this.outputEncoding),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Decrypts a previously encrypted value.
|
|
114
|
+
*
|
|
115
|
+
* The method checks the integrity of the data using the authentication tag (`authTag`) and ensures the data is properly decrypted.
|
|
116
|
+
* After decryption, it automatically deserializes the data back to its original type.
|
|
117
|
+
*
|
|
118
|
+
* @param {Object} params - An object containing the encrypted data:
|
|
119
|
+
* - `iv` {string} - The Initialization Vector (IV) used in encryption, encoded with the output encoding.
|
|
120
|
+
* - `encrypted` {string} - The encrypted data to decrypt, encoded with the output encoding.
|
|
121
|
+
* - `authTag` {string} - The authentication tag used to verify the integrity of the encrypted data.
|
|
122
|
+
* @param {string|null} [expectedType=null] - Optionally specify the expected type of the decrypted data. If provided, the method will validate the type of the deserialized value.
|
|
123
|
+
* @returns {*} The decrypted value, which will be the original type of the data before encryption.
|
|
124
|
+
* @throws {Error} Throws if the authentication tag doesn't match or the data has been tampered with.
|
|
125
|
+
* @throws {Error} Throws if the deserialized value doesn't match the `expectedType`.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const encryptedData = {
|
|
129
|
+
* iv: 'b32a...',
|
|
130
|
+
* encrypted: 'c1d5...',
|
|
131
|
+
* authTag: 'aa93...'
|
|
132
|
+
* };
|
|
133
|
+
* const decrypted = cryptoManager.decrypt(encryptedData, 'string');
|
|
134
|
+
* console.log(decrypted); // Outputs: 'Hello, world!'
|
|
135
|
+
*/
|
|
136
|
+
decrypt({ iv, encrypted, authTag }, expectedType = null) {
|
|
137
|
+
const ivBuffer = Buffer.from(iv, this.outputEncoding);
|
|
138
|
+
const encryptedBuffer = Buffer.from(encrypted, this.outputEncoding);
|
|
139
|
+
const authTagBuffer = Buffer.from(authTag, this.outputEncoding);
|
|
140
|
+
|
|
141
|
+
const decipher = crypto.createDecipheriv(this.algorithm, this.key, ivBuffer, {
|
|
142
|
+
authTagLength: this.authTagLength,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
decipher.setAuthTag(authTagBuffer);
|
|
146
|
+
|
|
147
|
+
let decrypted = decipher.update(encryptedBuffer, null, this.inputEncoding);
|
|
148
|
+
decrypted += decipher.final(this.inputEncoding);
|
|
149
|
+
const { value, type } = this.#deserialize(decrypted);
|
|
150
|
+
|
|
151
|
+
if (expectedType) this.#validateDeserializedType(expectedType, type);
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Retrieves the type of the original data from an encrypted object.
|
|
157
|
+
*
|
|
158
|
+
* This method decrypts the encrypted data and extracts its type information without fully deserializing the value.
|
|
159
|
+
* It is useful when you need to verify the type of the encrypted data before fully decrypting it.
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} params - An object containing the encrypted data:
|
|
162
|
+
* - `iv` {string} - The Initialization Vector (IV) used in encryption, encoded with the output encoding.
|
|
163
|
+
* - `encrypted` {string} - The encrypted data to decrypt, encoded with the output encoding.
|
|
164
|
+
* - `authTag` {string} - The authentication tag used to verify the integrity of the encrypted data.
|
|
165
|
+
* @returns {string} The type of the original data (e.g., 'string', 'number', 'date', etc.).
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* const encryptedData = {
|
|
169
|
+
* iv: 'b32a...',
|
|
170
|
+
* encrypted: 'c1d5...',
|
|
171
|
+
* authTag: 'aa93...'
|
|
172
|
+
* };
|
|
173
|
+
* const dataType = cryptoManager.getTypeFromEncrypted(encryptedData);
|
|
174
|
+
* console.log(dataType); // Outputs: 'string'
|
|
175
|
+
*/
|
|
176
|
+
getTypeFromEncrypted({ iv, encrypted, authTag }) {
|
|
177
|
+
const ivBuffer = Buffer.from(iv, this.outputEncoding);
|
|
178
|
+
const encryptedBuffer = Buffer.from(encrypted, this.outputEncoding);
|
|
179
|
+
const authTagBuffer = Buffer.from(authTag, this.outputEncoding);
|
|
180
|
+
|
|
181
|
+
const decipher = crypto.createDecipheriv(this.algorithm, this.key, ivBuffer, {
|
|
182
|
+
authTagLength: this.authTagLength,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
decipher.setAuthTag(authTagBuffer);
|
|
186
|
+
|
|
187
|
+
let decrypted = decipher.update(encryptedBuffer, null, this.inputEncoding);
|
|
188
|
+
decrypted += decipher.final(this.inputEncoding);
|
|
189
|
+
|
|
190
|
+
const { type } = this.#deserialize(decrypted);
|
|
191
|
+
return typeof type === 'string' ? type.toLowerCase() : 'unknown';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Saves the cryptographic key to a file.
|
|
196
|
+
*
|
|
197
|
+
* If running in a browser, the method generates a download link for the key as a text file.
|
|
198
|
+
* If running in Node.js, the method saves the key to the specified file path.
|
|
199
|
+
*
|
|
200
|
+
* @param {string} [filename='secret.key'] - The name of the file to save the key. Defaults to 'secret.key'.
|
|
201
|
+
* @throws {Error} Throws an error if the file cannot be written in Node.js.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* // In a browser, triggers a download of the key
|
|
205
|
+
* cryptoManager.saveKeyToFile('myKey.key');
|
|
206
|
+
*
|
|
207
|
+
* // In Node.js, saves the key to 'myKey.key'
|
|
208
|
+
* cryptoManager.saveKeyToFile('myKey.key');
|
|
209
|
+
*/
|
|
210
|
+
saveKeyToFile(filename = 'secret.key') {
|
|
211
|
+
const data = this.key.toString('hex');
|
|
212
|
+
if (isBrowser) {
|
|
213
|
+
const blob = new Blob([data], { type: 'text/plain' });
|
|
214
|
+
const url = URL.createObjectURL(blob);
|
|
215
|
+
const a = document.createElement('a');
|
|
216
|
+
a.href = url;
|
|
217
|
+
a.download = filename;
|
|
218
|
+
a.click();
|
|
219
|
+
URL.revokeObjectURL(url);
|
|
220
|
+
} else fs.writeFileSync(filename, data);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Saves the current cryptographic configuration to a JSON file.
|
|
225
|
+
*
|
|
226
|
+
* If running in a browser, the method generates a download link for the configuration as a JSON file.
|
|
227
|
+
* If running in Node.js, the method saves the configuration to the specified file path.
|
|
228
|
+
*
|
|
229
|
+
* @param {string} [filename='crypto-config.json'] - The name of the file to save the configuration. Defaults to 'crypto-config.json'.
|
|
230
|
+
* @throws {Error} Throws an error if the file cannot be written in Node.js.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* // In a browser, triggers a download of the configuration
|
|
234
|
+
* cryptoManager.saveConfigToFile('myConfig.json');
|
|
235
|
+
*
|
|
236
|
+
* // In Node.js, saves the configuration to 'myConfig.json'
|
|
237
|
+
* cryptoManager.saveConfigToFile('myConfig.json');
|
|
238
|
+
*/
|
|
239
|
+
saveConfigToFile(filename = 'crypto-config.json') {
|
|
240
|
+
const configData = JSON.stringify(this.exportConfig(), null, 2);
|
|
241
|
+
if (isBrowser) {
|
|
242
|
+
const blob = new Blob([configData], { type: 'application/json' });
|
|
243
|
+
const url = URL.createObjectURL(blob);
|
|
244
|
+
const a = document.createElement('a');
|
|
245
|
+
a.href = url;
|
|
246
|
+
a.download = filename;
|
|
247
|
+
a.click();
|
|
248
|
+
URL.revokeObjectURL(url);
|
|
249
|
+
} else fs.writeFileSync(filename, configData);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Loads and imports cryptographic configuration from a JSON file.
|
|
254
|
+
*
|
|
255
|
+
* If running in a browser, the method allows the user to select a file, reads the file as text,
|
|
256
|
+
* parses the JSON, and imports the configuration.
|
|
257
|
+
* If running in Node.js, the method reads the file synchronously and imports the configuration.
|
|
258
|
+
*
|
|
259
|
+
* @param {File|string} file - The file to load the configuration from. In the browser, this is a `File` object, and in Node.js, it's a file path.
|
|
260
|
+
* @returns {Promise<void>} A promise that resolves when the configuration is successfully loaded and imported.
|
|
261
|
+
* @throws {Error} Throws an error if the JSON file is invalid or the file cannot be read.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* // In a browser, prompt user to select a file and load the configuration
|
|
265
|
+
* cryptoManager.loadConfigFromFile(file)
|
|
266
|
+
* .then(() => console.log('Config loaded successfully'))
|
|
267
|
+
* .catch(err => console.error('Error loading config:', err));
|
|
268
|
+
*
|
|
269
|
+
* // In Node.js, load the configuration from a file path
|
|
270
|
+
* cryptoManager.loadConfigFromFile('myConfig.json')
|
|
271
|
+
* .then(() => console.log('Config loaded successfully'))
|
|
272
|
+
* .catch(err => console.error('Error loading config:', err));
|
|
273
|
+
*/
|
|
274
|
+
async loadConfigFromFile(file) {
|
|
275
|
+
if (isBrowser) {
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
const reader = new FileReader();
|
|
278
|
+
reader.onload = () => {
|
|
279
|
+
try {
|
|
280
|
+
const config = JSON.parse(reader.result);
|
|
281
|
+
resolve(this.importConfig(config));
|
|
282
|
+
} catch (err) {
|
|
283
|
+
reject(new Error('Invalid config JSON file'));
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
reader.onerror = () => reject(reader.error);
|
|
287
|
+
reader.readAsText(file);
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
291
|
+
const config = JSON.parse(raw);
|
|
292
|
+
return this.importConfig(config);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Loads a cryptographic key from a file and sets it for encryption/decryption.
|
|
298
|
+
*
|
|
299
|
+
* If running in a browser, the method allows the user to select a file, reads the file as text,
|
|
300
|
+
* and loads the key (in hexadecimal format) into the current instance.
|
|
301
|
+
* If running in Node.js, the method reads the file synchronously, parses the hexadecimal key,
|
|
302
|
+
* and loads it into the current instance.
|
|
303
|
+
*
|
|
304
|
+
* @param {File|string} file - The file to load the key from. In the browser, this is a `File` object, and in Node.js, it's a file path.
|
|
305
|
+
* @returns {Promise<Buffer>} A promise that resolves with the key as a `Buffer` when the file is successfully loaded.
|
|
306
|
+
* @throws {Error} Throws an error if the file cannot be read or if the key is invalid.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* // In a browser, prompt user to select a file and load the key
|
|
310
|
+
* cryptoManager.loadKeyFromFile(file)
|
|
311
|
+
* .then(key => console.log('Key loaded successfully:', key))
|
|
312
|
+
* .catch(err => console.error('Error loading key:', err));
|
|
313
|
+
*
|
|
314
|
+
* // In Node.js, load the key from a file path
|
|
315
|
+
* cryptoManager.loadKeyFromFile('myKey.key')
|
|
316
|
+
* .then(key => console.log('Key loaded successfully:', key))
|
|
317
|
+
* .catch(err => console.error('Error loading key:', err));
|
|
318
|
+
*/
|
|
319
|
+
async loadKeyFromFile(file) {
|
|
320
|
+
if (isBrowser) {
|
|
321
|
+
return new Promise((resolve, reject) => {
|
|
322
|
+
const reader = new FileReader();
|
|
323
|
+
reader.onload = () => {
|
|
324
|
+
const hexKey = reader.result.trim();
|
|
325
|
+
const keyBuffer = Buffer.from(hexKey, 'hex');
|
|
326
|
+
this.key = keyBuffer;
|
|
327
|
+
resolve(keyBuffer);
|
|
328
|
+
};
|
|
329
|
+
reader.onerror = () => reject(reader.error);
|
|
330
|
+
reader.readAsText(file);
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
const hexKey = fs.readFileSync(file, 'utf8');
|
|
334
|
+
const keyBuffer = Buffer.from(hexKey, 'hex');
|
|
335
|
+
this.key = keyBuffer;
|
|
336
|
+
return keyBuffer;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Exports the current cryptographic configuration as a JSON object.
|
|
342
|
+
*
|
|
343
|
+
* The exported configuration includes the encryption algorithm, output encoding format,
|
|
344
|
+
* input encoding format, the cryptographic key (in hexadecimal format), and the authentication tag length.
|
|
345
|
+
* This method does not include any sensitive data like the raw key, only its hexadecimal representation.
|
|
346
|
+
*
|
|
347
|
+
* @returns {Object} The exported configuration as a plain JavaScript object.
|
|
348
|
+
* @example
|
|
349
|
+
* const config = cryptoManager.exportConfig();
|
|
350
|
+
* console.log(config);
|
|
351
|
+
* // Example output:
|
|
352
|
+
* // {
|
|
353
|
+
* // algorithm: 'aes-256-gcm',
|
|
354
|
+
* // outputEncoding: 'hex',
|
|
355
|
+
* // inputEncoding: 'utf8',
|
|
356
|
+
* // key: 'abcdef1234567890...',
|
|
357
|
+
* // authTagLength: 16
|
|
358
|
+
* // }
|
|
359
|
+
*/
|
|
360
|
+
exportConfig() {
|
|
361
|
+
return {
|
|
362
|
+
algorithm: this.algorithm,
|
|
363
|
+
outputEncoding: this.outputEncoding,
|
|
364
|
+
inputEncoding: this.inputEncoding,
|
|
365
|
+
key: this.key.toString('hex'),
|
|
366
|
+
authTagLength: this.authTagLength,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Imports a cryptographic configuration from a JSON object.
|
|
372
|
+
*
|
|
373
|
+
* This method sets the configuration for the encryption process, including the algorithm, encoding formats,
|
|
374
|
+
* authentication tag length, and the cryptographic key (in hexadecimal string format).
|
|
375
|
+
* If any of the expected properties are missing or invalid, an error will be thrown.
|
|
376
|
+
*
|
|
377
|
+
* @param {Object} config - The configuration object to import.
|
|
378
|
+
* @param {string} config.algorithm - The encryption algorithm (e.g., 'aes-256-gcm').
|
|
379
|
+
* @param {string} config.outputEncoding - The output encoding format (e.g., 'hex').
|
|
380
|
+
* @param {string} config.inputEncoding - The input encoding format (e.g., 'utf8').
|
|
381
|
+
* @param {number} config.authTagLength - The authentication tag length (e.g., 16).
|
|
382
|
+
* @param {string} config.key - The cryptographic key in hexadecimal string format.
|
|
383
|
+
*
|
|
384
|
+
* @throws {Error} If any required property is missing or has an invalid type.
|
|
385
|
+
* @example
|
|
386
|
+
* const config = {
|
|
387
|
+
* algorithm: 'aes-256-gcm',
|
|
388
|
+
* outputEncoding: 'hex',
|
|
389
|
+
* inputEncoding: 'utf8',
|
|
390
|
+
* authTagLength: 16,
|
|
391
|
+
* key: 'abcdef1234567890abcdef1234567890',
|
|
392
|
+
* };
|
|
393
|
+
* cryptoManager.importConfig(config);
|
|
394
|
+
*/
|
|
395
|
+
importConfig(config) {
|
|
396
|
+
if (typeof config.algorithm === 'string') this.algorithm = config.algorithm;
|
|
397
|
+
else if (typeof config.algorithm !== 'undefined')
|
|
398
|
+
throw new Error('Invalid or missing "algorithm" property. Expected a string.');
|
|
399
|
+
|
|
400
|
+
if (typeof config.outputEncoding === 'string') this.outputEncoding = config.outputEncoding;
|
|
401
|
+
else if (typeof config.outputEncoding !== 'undefined')
|
|
402
|
+
throw new Error('Invalid or missing "outputEncoding" property. Expected a string.');
|
|
403
|
+
|
|
404
|
+
if (typeof config.inputEncoding === 'string') this.inputEncoding = config.inputEncoding;
|
|
405
|
+
else if (typeof config.inputEncoding !== 'undefined')
|
|
406
|
+
throw new Error('Invalid or missing "inputEncoding" property. Expected a string.');
|
|
407
|
+
|
|
408
|
+
if (typeof config.authTagLength === 'number') this.authTagLength = config.authTagLength;
|
|
409
|
+
else if (typeof config.authTagLength !== 'undefined')
|
|
410
|
+
throw new Error('Invalid or missing "authTagLength" property. Expected a number.');
|
|
411
|
+
|
|
412
|
+
if (typeof config.key === 'string') this.key = Buffer.from(config.key, 'hex');
|
|
413
|
+
else if (typeof config.key !== 'undefined')
|
|
414
|
+
throw new Error('Invalid or missing "key" property. Expected a hexadecimal string.');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* A mapping of data types to their serialization functions.
|
|
419
|
+
*
|
|
420
|
+
* This object defines how various JavaScript types should be serialized to a JSON-compatible format.
|
|
421
|
+
* It includes handling for primitive types, complex objects, and non-serializable types such as functions
|
|
422
|
+
* and promises, which throw an error when attempted to be serialized.
|
|
423
|
+
*
|
|
424
|
+
* Each key corresponds to a specific data type (e.g., 'number', 'date', 'buffer', etc.),
|
|
425
|
+
* and the value is a function that serializes the data to a specific format.
|
|
426
|
+
*
|
|
427
|
+
* @type {Object}
|
|
428
|
+
* @property {Function} weakmap - Throws an error as WeakMap cannot be serialized.
|
|
429
|
+
* @property {Function} weakset - Throws an error as WeakSet cannot be serialized.
|
|
430
|
+
* @property {Function} promise - Throws an error as Promise cannot be serialized.
|
|
431
|
+
* @property {Function} function - Throws an error as Function cannot be serialized.
|
|
432
|
+
* @property {Function} regexp - Serializes RegExp objects to a JSON object with their string representation.
|
|
433
|
+
* @property {Function} htmlElement - Serializes HTML elements to their outerHTML string.
|
|
434
|
+
* @property {Function} date - Serializes Date objects to an ISO string format.
|
|
435
|
+
* @property {Function} bigint - Serializes BigInt objects to their string representation.
|
|
436
|
+
* @property {Function} number - Serializes numbers to a JSON-compatible format.
|
|
437
|
+
* @property {Function} boolean - Serializes booleans to a JSON-compatible format.
|
|
438
|
+
* @property {Function} string - Serializes strings to a JSON-compatible format.
|
|
439
|
+
* @property {Function} null - Serializes null to a special 'Null' type in JSON.
|
|
440
|
+
* @property {Function} undefined - Serializes undefined to a special 'Undefined' type in JSON.
|
|
441
|
+
* @property {Function} map - Serializes Map objects to an array of entries in JSON format.
|
|
442
|
+
* @property {Function} set - Serializes Set objects to an array of values in JSON format.
|
|
443
|
+
* @property {Function} symbol - Serializes Symbol objects to a JSON object with the symbol's description.
|
|
444
|
+
* @property {Function} array - Serializes arrays to a JSON-compatible format.
|
|
445
|
+
* @property {Function} object - Serializes general objects to a JSON-compatible format.
|
|
446
|
+
* @property {Function} buffer - Serializes Buffer objects to a base64-encoded string.
|
|
447
|
+
*/
|
|
448
|
+
#valueConvertTypes = {
|
|
449
|
+
weakmap: () => {
|
|
450
|
+
throw new Error('WeakMap cannot be serialized');
|
|
451
|
+
},
|
|
452
|
+
weakset: () => {
|
|
453
|
+
throw new Error('WeakSet cannot be serialized');
|
|
454
|
+
},
|
|
455
|
+
promise: () => {
|
|
456
|
+
throw new Error('Promise cannot be serialized');
|
|
457
|
+
},
|
|
458
|
+
function: () => {
|
|
459
|
+
throw new Error('Function cannot be serialized');
|
|
460
|
+
},
|
|
461
|
+
regexp: (data) => JSON.stringify({ __type: 'RegExp', value: data.toString() }),
|
|
462
|
+
htmlElement: (data) => JSON.stringify({ __type: 'HTMLElement', value: data.outerHTML }),
|
|
463
|
+
date: (data) => JSON.stringify({ __type: 'Date', value: data.toISOString() }),
|
|
464
|
+
bigint: (data) => JSON.stringify({ __type: 'BigInt', value: data.toString() }),
|
|
465
|
+
number: (data) => JSON.stringify({ __type: 'Number', value: data }),
|
|
466
|
+
boolean: (data) => JSON.stringify({ __type: 'Boolean', value: data }),
|
|
467
|
+
string: (data) => JSON.stringify({ __type: 'String', value: data }),
|
|
468
|
+
null: (data) => JSON.stringify({ __type: 'Null' }),
|
|
469
|
+
undefined: (data) => JSON.stringify({ __type: 'Undefined' }),
|
|
470
|
+
map: (data) =>
|
|
471
|
+
JSON.stringify({
|
|
472
|
+
__type: 'Map',
|
|
473
|
+
value: Array.from(data.entries()),
|
|
474
|
+
}),
|
|
475
|
+
set: (data) =>
|
|
476
|
+
JSON.stringify({
|
|
477
|
+
__type: 'Set',
|
|
478
|
+
value: Array.from(data.values()),
|
|
479
|
+
}),
|
|
480
|
+
symbol: (data) => JSON.stringify({ __type: 'Symbol', value: data.description }),
|
|
481
|
+
array: (data) => JSON.stringify({ __type: 'Array', value: data }),
|
|
482
|
+
object: (data) => JSON.stringify({ __type: 'JSON', value: data }),
|
|
483
|
+
buffer: (data) => JSON.stringify({ __type: 'Buffer', value: data.toString('base64') }),
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* A mapping of data types to their deserialization functions.
|
|
488
|
+
*
|
|
489
|
+
* This object defines how various serialized types should be deserialized back to their original JavaScript objects.
|
|
490
|
+
* It includes support for primitive types, complex objects, and browser-specific types like `HTMLElement`.
|
|
491
|
+
* Each key corresponds to a specific data type (e.g., 'Date', 'BigInt', 'Buffer', etc.),
|
|
492
|
+
* and the value is a function that deserializes the value to its original format.
|
|
493
|
+
*
|
|
494
|
+
* @type {Object}
|
|
495
|
+
* @property {Function} RegExp - Deserializes a regular expression from its string representation (e.g., `/pattern/flags`).
|
|
496
|
+
* @property {Function} HTMLElement - Deserializes an HTML element from its serialized outerHTML string (only works in browser environments).
|
|
497
|
+
* @property {Function} Date - Deserializes a date from its ISO string representation.
|
|
498
|
+
* @property {Function} BigInt - Deserializes a BigInt from its string representation.
|
|
499
|
+
* @property {Function} Number - Deserializes a number from its string or numeric representation.
|
|
500
|
+
* @property {Function} Boolean - Deserializes a boolean value from its string representation.
|
|
501
|
+
* @property {Function} Null - Deserializes the `null` value.
|
|
502
|
+
* @property {Function} Undefined - Deserializes the `undefined` value.
|
|
503
|
+
* @property {Function} Map - Deserializes a Map from an array of key-value pairs.
|
|
504
|
+
* @property {Function} Set - Deserializes a Set from an array of values.
|
|
505
|
+
* @property {Function} Symbol - Deserializes a Symbol from its string description.
|
|
506
|
+
* @property {Function} Array - Deserializes an array from its serialized representation.
|
|
507
|
+
* @property {Function} JSON - Deserializes a plain JSON object from its serialized representation.
|
|
508
|
+
* @property {Function} String - Deserializes a string from its serialized representation.
|
|
509
|
+
* @property {Function} Buffer - Deserializes a Buffer from its base64-encoded string representation.
|
|
510
|
+
*/
|
|
511
|
+
#valueTypes = {
|
|
512
|
+
RegExp: (value) => {
|
|
513
|
+
const match = value.match(/^\/(.*)\/([gimsuy]*)$/);
|
|
514
|
+
return match ? new RegExp(match[1], match[2]) : new RegExp(value);
|
|
515
|
+
},
|
|
516
|
+
HTMLElement: (value) => {
|
|
517
|
+
if (typeof document === 'undefined')
|
|
518
|
+
throw new Error('HTMLElement deserialization is only supported in browsers');
|
|
519
|
+
const div = document.createElement('div');
|
|
520
|
+
div.innerHTML = value;
|
|
521
|
+
return div.firstElementChild;
|
|
522
|
+
},
|
|
523
|
+
Date: (value) => new Date(value),
|
|
524
|
+
BigInt: (value) => BigInt(value),
|
|
525
|
+
Number: (value) => Number(value),
|
|
526
|
+
Boolean: (value) => Boolean(value),
|
|
527
|
+
Null: (value) => null,
|
|
528
|
+
Undefined: (value) => undefined,
|
|
529
|
+
Map: (value) => new Map(value),
|
|
530
|
+
Set: (value) => new Set(value),
|
|
531
|
+
Symbol: (value) => Symbol(value),
|
|
532
|
+
Array: (value) => value,
|
|
533
|
+
JSON: (value) => value,
|
|
534
|
+
String: (value) => String(value),
|
|
535
|
+
Buffer: (value) => Buffer.from(value, 'base64'),
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Serializes a given data value into a JSON-compatible format based on its type.
|
|
540
|
+
* This method converts various JavaScript data types into their serialized representation
|
|
541
|
+
* that can be encrypted or stored. If the data type is unsupported, an error is thrown.
|
|
542
|
+
*
|
|
543
|
+
* @param {any} data - The data to be serialized.
|
|
544
|
+
* @returns {string} The serialized data in JSON format.
|
|
545
|
+
* @throws {Error} If the data type is unsupported for serialization.
|
|
546
|
+
*/
|
|
547
|
+
#serialize(data) {
|
|
548
|
+
const type = objFilter.objType(data) || 'undefined';
|
|
549
|
+
if (this.#valueConvertTypes[type]) return this.#valueConvertTypes[type](data);
|
|
550
|
+
throw new Error(`Unsupported data type for encryption: ${type}`);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Deserializes a string back into its original JavaScript object based on the serialized type information.
|
|
555
|
+
* This method checks the serialized type and converts the string back to its original JavaScript object
|
|
556
|
+
* (such as a `Date`, `Buffer`, `RegExp`, etc.). If the type is unknown or unsupported, it returns the raw value.
|
|
557
|
+
*
|
|
558
|
+
* @param {string} text - The serialized data to be deserialized.
|
|
559
|
+
* @returns {{value: any, type: string}} An object containing the deserialized value and its type.
|
|
560
|
+
* @throws {Error} If deserialization fails due to an invalid or unknown type.
|
|
561
|
+
*/
|
|
562
|
+
#deserialize(text) {
|
|
563
|
+
try {
|
|
564
|
+
const parsed = JSON.parse(text);
|
|
565
|
+
const type = parsed.__type;
|
|
566
|
+
|
|
567
|
+
if (typeof type !== 'string') return { value: text, type: 'String' };
|
|
568
|
+
if (typeof this.#valueTypes[type] === 'function')
|
|
569
|
+
return {
|
|
570
|
+
value: this.#valueTypes[type](parsed.value),
|
|
571
|
+
type,
|
|
572
|
+
};
|
|
573
|
+
else return { value: text, type: 'Unknown' };
|
|
574
|
+
} catch {
|
|
575
|
+
return { value: text, type: 'Unknown' };
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Validates that the actual type of a deserialized value matches the expected type.
|
|
581
|
+
* This method ensures that the type of the deserialized data matches what is expected,
|
|
582
|
+
* throwing an error if there's a mismatch.
|
|
583
|
+
*
|
|
584
|
+
* @param {string} expected - The expected type of the deserialized data.
|
|
585
|
+
* @param {string} actual - The actual type of the deserialized data.
|
|
586
|
+
* @throws {Error} If the types do not match.
|
|
587
|
+
*/
|
|
588
|
+
#validateDeserializedType(expected, actual) {
|
|
589
|
+
if (expected.toLowerCase() !== actual.toLowerCase())
|
|
590
|
+
throw new Error(
|
|
591
|
+
`Type mismatch: expected ${expected.toLowerCase()}, but got ${actual.toLowerCase()}`,
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
module.exports = TinyCrypto;
|