@lumjs/encode 1.0.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.
@@ -0,0 +1,344 @@
1
+ const {F,S,isObj} = require('@lumjs/core/types');
2
+
3
+ /**
4
+ * Crypto-JS Module Loader
5
+ *
6
+ * Uses some dark magic to normalize any weirdness, bugs,
7
+ * or gotchyas in the upsteam library.
8
+ *
9
+ * @module @lumjs/encode/crypto/load
10
+ */
11
+
12
+ const cc = require('crypto-js/core');
13
+
14
+ /**
15
+ * Load and return a specific `crypto-js` module.
16
+ *
17
+ * This is a wrapper for `require()` that prefixes
18
+ * the `crypto-js/` package namespace.
19
+ *
20
+ * Generally you should never have to call this manually.
21
+ * Instead use one of the more specialized methods.
22
+ *
23
+ * Note that **this** is the actual *default export* from
24
+ * the `load` module, and all the other functions are
25
+ * child properties of it.
26
+ *
27
+ * @param {string} name - The name of the module.
28
+ *
29
+ * @param {boolean} [toLower=true] Enforce lowercase names?
30
+ *
31
+ * If `true` (the default value), the `name` will be
32
+ * normalized to *lowercase* as is expected by the library.
33
+ *
34
+ * @returns {(object|function)} The module content.
35
+ *
36
+ * Some `crypto-js` modules return a child `object`, some return
37
+ * a child `function`, and some return the `crypto-js/core` object.
38
+ * The wide difference in return values is part of why this
39
+ * method should not be used directly.
40
+ *
41
+ * @alias module:@lumjs/encode/crypto/load.load
42
+ */
43
+ function load(name, toLower=true)
44
+ {
45
+ if (toLower)
46
+ name = name.toLowerCase();
47
+ return require('crypto-js/'+name);
48
+ }
49
+
50
+ // Export the load function, and make all other functions properties of it.
51
+ module.exports = exports = load;
52
+ load.load = load; // Fuckery.
53
+
54
+ /**
55
+ * Get a Hashing function or progressive instance.
56
+ *
57
+ * @param {string} name - The name of the algorithm.
58
+ *
59
+ * Hashing algorithms supported by `crypto-js`:
60
+ *
61
+ * - MD5
62
+ * - SHA-1
63
+ * - SHA-256
64
+ * - SHA-512
65
+ * - SHA-224
66
+ * - SHA-384
67
+ * - SHA-3
68
+ * - RIPEMD-160
69
+ *
70
+ * You can omit the `-` characters, and the parameter is
71
+ * case-insensitive.
72
+ *
73
+ * @param {(boolean|object)} [instance=false] Return an instance?
74
+ *
75
+ * If this is `true` we'll return an object instance for
76
+ * progressive hashing.
77
+ *
78
+ * If this is an `object`, we'll return an object instance, and
79
+ * this will be passed as the options to the `algo.create()` method.
80
+ *
81
+ * If this is `false` (default), we'll return a hashing function.
82
+ *
83
+ * @returns {(function|object)} Output depends on parameters.
84
+ */
85
+ exports.hash = function(name, instance=false)
86
+ {
87
+ if (instance)
88
+ { // Return a progressive instance.
89
+ const algo = load.algo(name);
90
+ const opts = isObj(instance) ? instance : {};
91
+ return algo.create(opts);
92
+ }
93
+ else
94
+ { // Return a hashing function.
95
+ return load(name.replaceAll('-', ''));
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Get a `HMAC` Hashing function or progressive instance.
101
+ *
102
+ * @param {string} libName - Name of the hashing algorithm to use.
103
+ *
104
+ * See {@link module:@lumjs/encode/crypto/load.hash} for a list.
105
+ *
106
+ * @param {string} [passphrase] - A secret passphrase for a hashing instance.
107
+ *
108
+ * If this is specified, we'll return an object instance for
109
+ * progressive hashing.
110
+ *
111
+ * If this is **not** specified, we'll return a hashing function.
112
+ *
113
+ * @returns {(object|function)} Output depends on parameters.
114
+ */
115
+ exports.hmac = function(libName, passphrase)
116
+ {
117
+ if (typeof passphrase === S)
118
+ { // Return a progressive instance.
119
+ const algo = load.algo(libName);
120
+ const hmac = load.algo('HMAC');
121
+ return hmac.create(algo, passphrase);
122
+ }
123
+ else
124
+ { // Return a hashing function.
125
+ return load('hmac-'+libName.replaceAll('-', ''));
126
+ }
127
+ }
128
+
129
+ const Ciphers =
130
+ {
131
+ rc4drop()
132
+ {
133
+ load('rc4', false);
134
+ return cc.RC4Drop;
135
+ },
136
+ des()
137
+ {
138
+ load('tripledes', false);
139
+ return cc.DES;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Get a cipher transcoder.
145
+ *
146
+ * @param {string} name - The name of the algorithm.
147
+ *
148
+ * Cipher algorithms supported by `crypto-js`:
149
+ *
150
+ * - AES
151
+ * - DES
152
+ * - TripleDES
153
+ * - Rabbit
154
+ * - RabbitLegacy
155
+ * - RC4
156
+ * - RC4Drop
157
+ *
158
+ * The parameter is case-insensitive.
159
+ *
160
+ * @returns {function} The hashing function.
161
+ */
162
+ exports.cipher = function(name)
163
+ {
164
+ name = name.replaceAll('-', '').toLowerCase();
165
+ if (typeof Ciphers[name] === F)
166
+ { // A custom loader function.
167
+ return Ciphers[name]();
168
+ }
169
+ return load(name, false);
170
+ }
171
+
172
+ const Algos =
173
+ {
174
+ RC4DROP()
175
+ {
176
+ load('rc4', false);
177
+ return cc.algo.RC4Drop;
178
+ },
179
+ des()
180
+ {
181
+ load('tripledes', false);
182
+ return cc.algo.DES;
183
+ },
184
+ TRIPLEDES: 'TripleDES',
185
+ RABBIT: 'Rabbit',
186
+ RABBITLEGACY()
187
+ {
188
+ load('rabbit-legacy', false);
189
+ return cc.algo.RabbitLegacy;
190
+ },
191
+ }
192
+
193
+ /**
194
+ * Get an algorithm plugin.
195
+ *
196
+ * @param {string} name - The name of the algorithm
197
+ *
198
+ * It can be any algorithm supported by either
199
+ * the [hash()]{@link module:@lumjs/encode/crypto.hash}
200
+ * or [cipher()]{@link module:@lumjs/encode/crypto.cipher}
201
+ * as well as standalone algorithms like `PBKDF2`, `HMAC`, `EvpKDF`, etc.
202
+ *
203
+ * @returns {object} The algorithm plugin.
204
+ *
205
+ * You'll need to use the `create()` method on the plugin to get
206
+ * an instance before you can start hashing.
207
+ *
208
+ */
209
+ exports.algo = function(name)
210
+ {
211
+ name = name.replaceAll('-','').toUpperCase();
212
+
213
+ if (typeof Algos[name] === F)
214
+ { // A custom loader.
215
+ return Algos[name]();
216
+ }
217
+ else if (typeof Algos[name] === S)
218
+ { // A specific case-sensitive spelling.
219
+ name = Algos[name];
220
+ }
221
+
222
+ // We make sure the library is loaded.
223
+ load(name);
224
+
225
+ // Now we'll get the object out of the core.
226
+ return cc.algo[name];
227
+ }
228
+
229
+ const Encoders =
230
+ {
231
+ utf16be()
232
+ {
233
+ load('enc-utf16', false);
234
+ return cc.enc.Utf16BE;
235
+ },
236
+ utf16le()
237
+ {
238
+ load('enc-utf16', false);
239
+ return cc.enc.Utf16LE;
240
+ },
241
+ }
242
+
243
+ /**
244
+ * Get an encoder plugin.
245
+ *
246
+ * @param {*} name - The name of the encoder.
247
+ *
248
+ * Encoders supported:
249
+ *
250
+ * - Base64
251
+ * - Base64-URL
252
+ * - Latin1
253
+ * - Hex
254
+ * - UTF-8
255
+ * - UTF-16
256
+ * - UTF-16LE
257
+ *
258
+ * You can omit the `-` characters, and the parameter is
259
+ * case-insensitive.
260
+ *
261
+ * @returns {object} The encoder plugin.
262
+ */
263
+ exports.enc = function(name)
264
+ {
265
+ name = name.replaceAll('-','').toLowerCase();
266
+
267
+ if (typeof Encoders[name] === F)
268
+ { // A custom loader.
269
+ return Encoders[name]();
270
+ }
271
+
272
+ // We make sure the library is loaded.
273
+ return load('enc-'+name, false);
274
+ }
275
+
276
+ const Paddings =
277
+ {
278
+ ansix923()
279
+ { // This module has a typo in it, correcting that here.
280
+ load('pad-ansix923', false);
281
+ return cc.pad.AnsiX923;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Get a padding plugin.
287
+ *
288
+ * @param {string} name - Name of the plugin.
289
+ * @returns {object}
290
+ */
291
+ exports.pad = function(name)
292
+ {
293
+ name = name.replaceAll('-','').toLowerCase();
294
+
295
+ if (typeof Paddings[name] === F)
296
+ { // A custom loader.
297
+ return Paddings[name]();
298
+ }
299
+
300
+ return load('pad-'+name, false);
301
+ }
302
+
303
+ const Modes =
304
+ {
305
+ CBC()
306
+ { // Built-in mode, no load required.
307
+ return cc.mode.CBC;
308
+ },
309
+ CTRGLADMAN()
310
+ { // Filename fuckery.
311
+ return load('mode-ctr-gladman', false);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Get a mode plugin.
317
+ *
318
+ * @param {string} name - Name of the plugin.
319
+ * @returns {object}
320
+ */
321
+ exports.mode = function(name)
322
+ {
323
+ name = name.replaceAll('-','').toUpperCase();
324
+
325
+ if (typeof Modes[name] === F)
326
+ { // A custom loader.
327
+ return Modes[name]();
328
+ }
329
+
330
+ return load('mode-'+name);
331
+ }
332
+
333
+ /**
334
+ * Get a format plugin.
335
+ *
336
+ * @param {string} name - Name of the plugin.
337
+ * @returns {object}
338
+ */
339
+ exports.format = function(name)
340
+ {
341
+ return load('format-'+name);
342
+ }
343
+
344
+ // That's all folks.
package/lib/hash.js ADDED
@@ -0,0 +1,244 @@
1
+ const {S,F,isObj,isNil} = require('@lumjs/core/types');
2
+
3
+ const util = require('./util');
4
+ const Base91 = require('./base91');
5
+ const Safe64 = require('./safe64');
6
+ const Crypto = require('./crypto');
7
+ const cload = Crypto.load;
8
+
9
+ const DEFALGO = 'SHA256';
10
+
11
+ /**
12
+ * Hashifier
13
+ *
14
+ * A simple yet flexible class for building cryptographic hashes.
15
+ *
16
+ * @exports module:@lumjs/encode/hash
17
+ */
18
+ module.exports = class
19
+ {
20
+ /**
21
+ * Build a new Hashifier
22
+ *
23
+ * @param {(object|string)} [options] Options
24
+ *
25
+ * If this is a `string`, it's assumed to be the `options.algo` option.
26
+ *
27
+ * @param {string} [options.algo="SHA256"] Hashing algorithm
28
+ *
29
+ * By default we use "SHA256" for backwards compatibility with my
30
+ * older libraries and apps. You can set it to any *hashing algorithm*
31
+ * from the `crypto-js` library (we use our own
32
+ * [crypto]{@link module:@lumjs/encode/crypto} helper.)
33
+ *
34
+ * @param {object} [options.safe64] Default options for `safe64()`
35
+ *
36
+ * If specified, this will become the default options for the
37
+ * [safe64()]{@link module:@lumjs/encode/hash#safe64} method.
38
+ *
39
+ * @param {object} [options.base91] Default options for `base91()`
40
+ *
41
+ * If specified, this will become the default options for the
42
+ * [base91()]{@link module:@lumjs/encode/hash#base9164} method.
43
+ *
44
+ */
45
+ constructor(options={})
46
+ {
47
+ if (typeof options === S)
48
+ {
49
+ options = {algo: options};
50
+ }
51
+
52
+ const algo = options.algo ?? DEFALGO;
53
+
54
+ this.algoLib = cload.algo(algo);
55
+ this.hashFunction = cload.hash(algo);
56
+
57
+ //console.debug("<hash>", {hash: this, algo, options});
58
+
59
+ if (!isObj(this.algoLib) || typeof this.hashFunction !== F)
60
+ {
61
+ throw new Error("Invalid hashing algorithm");
62
+ }
63
+
64
+ this.safe64Options = options.safe64 ?? {};
65
+ this.base91Options = options.base91 ?? {};
66
+
67
+ } // construct()
68
+
69
+ /**
70
+ * Get a cryptographic *hash*.
71
+ *
72
+ * If you pass `input`, it will be hashed *immediately* and returned.
73
+ *
74
+ * Otherwise, if there is a current *progressive hash* in the process of
75
+ * being built, it will be *finalized* and returned.
76
+ *
77
+ * This will return `undefined` if there is no valid data to be hashed.
78
+ *
79
+ * @param {(string|WordArray)} [input] Input to hash immediately.
80
+ *
81
+ * @return {WordArray|undefined}
82
+ */
83
+ hash(input)
84
+ {
85
+ if (isNil(input))
86
+ { // No input, let's see if we have a progrssive hash being built.
87
+ if (typeof this.currentHash === 'object')
88
+ { // Let's return the finalized hash.
89
+ let value = this.currentHash.finalize();
90
+ delete this.currentHash;
91
+ return value;
92
+ }
93
+ }
94
+ else if (typeof input === 'string' || this.valid(hash))
95
+ { // Input data was passed, let's hash it now.
96
+ return this.hashFunction(input);
97
+ }
98
+ }
99
+
100
+ // Not really a useful public method.
101
+ valid(hash)
102
+ { // WordArray is not a standard object and doesn't work with instanceof.
103
+ return (isObj(hash) && typeof hash.toString === 'function');
104
+ }
105
+
106
+ /**
107
+ * Get hash as a Hex string.
108
+ *
109
+ * @param {(string|WordArray)} [input]
110
+ * See [hash()]{@link module:@lumjs/encode/hash#hash}
111
+ * @returns {string}
112
+ *
113
+ * This uses the default `Hex` encoder from `crypto-js` to perform
114
+ * the hashing, so output is directly as if you'd used that library.
115
+ */
116
+ hex(input)
117
+ {
118
+ const hash = this.hash(input);
119
+ if (this.valid(hash)) return hash.toString();
120
+ }
121
+
122
+ /**
123
+ * Get hash as a Base64-encoded string.
124
+ *
125
+ * @param {(string|WordArray)} [input]
126
+ * See [hash()]{@link module:@lumjs/encode/hash#hash}
127
+ * @returns {string}
128
+ *
129
+ * This uses the direct `Base64` encoder from `crypto-js` to perform
130
+ * the hashing, so output is directly as if you'd used that library.
131
+ */
132
+ base64(input)
133
+ {
134
+ const hash = this.hash(input);
135
+ const base64 = Crypto.enc.Base64;
136
+ if (this.valid(hash)) return hash.toString(base64);
137
+ }
138
+
139
+ /**
140
+ * Get hash as a Safe64-encoded string.
141
+ *
142
+ * Calls [base64()]{@link module:@lumjs/encode/hash#base64},
143
+ * then passes the output from that to
144
+ * [urlize()]{@link module:@lumjs/encode/safe64.urlize} to
145
+ * convert into the *Safe64* format.
146
+ *
147
+ * @param {(string|WordArray)} [input]
148
+ * See [hash()]{@link module:@lumjs/encode/hash#hash}
149
+ * @param {object} [opts]
150
+ * Options for `urlize()`.
151
+ * @returns {string}
152
+ *
153
+ * By default it's a raw *Safe64* string with **no** header,
154
+ * and **no** tildes. The `opts` passed can change the format
155
+ * by enabling *tildes* or adding a *header*.
156
+ *
157
+ */
158
+ safe64(input, opts={})
159
+ {
160
+ let base64 = this.base64(input);
161
+ if (base64)
162
+ return Safe64.urlize(base64, opts);
163
+ }
164
+
165
+ /**
166
+ * Get hash as a Base91-encoded string.
167
+ *
168
+ * The `crypto-js` library set currently does not natively support
169
+ * the `base91` encoding format.
170
+ *
171
+ * So this offers a few different ways to encode `crypto-js` hashes
172
+ * in `base91` format using our own
173
+ * [base91]{@link module:@lumjs/encode/base91} module.
174
+ *
175
+ * @param {(string|WordArray)} [input]
176
+ * See [hash()]{@link module:@lumjs/encode/hash#hash}
177
+ * @param {object} [opts] Options for how to encode the hash.
178
+ * @param {boolean} [opts.words=false] Use `hash.words`?
179
+ *
180
+ * If this is `true`, we will pass the `hash.words` array to
181
+ * the `base91.encode()` method to encode them.
182
+ *
183
+ * If this is true, then `opts.enc` and `opts.nba` are ignored.
184
+ *
185
+ * @param {object} [opts.enc] The `crypto-js` encoder.
186
+ *
187
+ * That can be any of the `crypto.enc.*` plugins.
188
+ * If not specified, the default `Hex` encoding format will be used.
189
+ *
190
+ * @param {(boolean|object)} [opts.nba=true] Use `numByteArray()` ?
191
+ *
192
+ * If this is `true` the *hash string* will be passed to `numByteArray()`
193
+ * with the *default options* and the output from that will be passed to
194
+ * `base91.encode()`.
195
+ *
196
+ * If this is an `object`, then the same logic as `true` applies, except this
197
+ * will be used as the *explicit options* for the `numByteArray()` method.
198
+ *
199
+ * If this is `false` the *hash string* itself will be passed directly to
200
+ * `base91.encode()` without further encoding.
201
+ *
202
+ * The default value of `true` is provided for backwards compatibility with
203
+ * the previous version of this library which did not have as many options.
204
+ *
205
+ * @returns {string}
206
+ */
207
+ base91(input, opts={})
208
+ {
209
+ let hash = this.hash(input);
210
+ if (this.valid(hash))
211
+ return Base91.encode(util.numByteArray(hash.toString()))
212
+ }
213
+
214
+ /**
215
+ * Add input to a progressive hash.
216
+ *
217
+ * If there is not already a progressive hash being built, we will
218
+ * create one automatically. Use the `hash()` method with no parameter
219
+ * to retrieve the finalized hash as a `WordArray` object, or one of
220
+ * the other convenience methods to return it in a specific string
221
+ * format.
222
+ *
223
+ * @param {string|WordArray} input A value to add to the hash.
224
+ *
225
+ * @return Lum.Hashifier The current object.
226
+ */
227
+ add(input)
228
+ {
229
+ if (!isObj(this.currentHash))
230
+ { // We need to start a new hash.
231
+ this.currentHash = this.algoLib.create();
232
+ }
233
+
234
+ this.currentHash.update(input);
235
+
236
+ return this;
237
+ }
238
+
239
+ static new(opts={})
240
+ {
241
+ return new this(opts);
242
+ }
243
+
244
+ } // Hashifier class
package/lib/index.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * A full set of encoding utilities.
3
+ * @module @lumjs/encode
4
+ */
5
+
6
+ /**
7
+ * @name module:@lumjs/encode.ord
8
+ * @function
9
+ * @see {@link module:@lumjs/encode/util.ord}
10
+ */
11
+
12
+ /**
13
+ * @name module:@lumjs/encode.numByteArray
14
+ * @function
15
+ * @see {@link module:@lumjs/encode/util.numByteArray}
16
+ */
17
+
18
+ const {ord, numByteArray} = require('./util');
19
+
20
+ /**
21
+ * @see {@link module:@lumjs/encode/base64}
22
+ * @alias module:@lumjs/encode.Base64
23
+ */
24
+ const Base64 = require('./base64');
25
+
26
+ /**
27
+ * @see {@link module:@lumjs/encode/base91}
28
+ * @alias module:@lumjs/encode.Base91
29
+ */
30
+ const Base91 = require('./base91');
31
+
32
+ /**
33
+ * @see {@link module:@lumjs/encode/safe64}
34
+ * @alias module:@lumjs/encode.Safe64
35
+ */
36
+ const Safe64 = require('./safe64');
37
+
38
+ /**
39
+ * @see {@link module:@lumjs/encode/hash}
40
+ * @alias module:@lumjs/encode.Hash
41
+ */
42
+ const Hash = require('./hash');
43
+
44
+ /**
45
+ * @see {@link module:@lumjs/encode/crypto}
46
+ * @alias module:@lumjs/encode.Crypto
47
+ */
48
+ const Crypto = require('./crypto');
49
+
50
+ module.exports = exports =
51
+ {
52
+ ord, numByteArray, Base64, Safe64, Base91, Hash, Crypto,
53
+ }
@@ -0,0 +1,40 @@
1
+ const Enum = require('@lumjs/core/enum');
2
+
3
+ /**
4
+ * The Safe64 version.
5
+ * @alias module:@lumjs/encode/safe64.VERSION
6
+ */
7
+ exports.VERSION = 3;
8
+
9
+ /**
10
+ * An `Enum` of serialization formats.
11
+ *
12
+ * | Enum Name | Value | Description |
13
+ * | --------- | ----- | ----------- |
14
+ * | `NONE` | `0` | No serialization. Used when encoding a string or binary data. |
15
+ * | `JSON` | `1` | Serialize with `JSON`. The simplest, **default** format. |
16
+ * | `PHP` | `2` | PHP `serialize()` format. Can store object class information. |
17
+ * | `UBJSON` | `3` | A binary JSON-like serialization format. |
18
+ *
19
+ * @alias module:@lumjs/encode/safe64.FORMAT
20
+ */
21
+ exports.FORMAT = Enum(['NONE','JSON','PHP','UBJSON']);
22
+
23
+ /**
24
+ * An `Enum` of serialization return types.
25
+ *
26
+ * | Enum Name | Value | Description |
27
+ * | --------- | ----- | ----------- |
28
+ * | `RAW` | `0` | Return the raw string/buffer value. |
29
+ * | `ARR_OBJ` | `1` | In PHP, use an *associative array* for objects. |
30
+ * | `STD_OBJ` | `2` | In PHP, use a *StdClass* instance for objects. |
31
+ *
32
+ * In this Javascript implementation, the `ARR_OBJ` and `STD_OBJ` formats
33
+ * are treated exactly the same. The differenciation only matters in the
34
+ * **PHP** implementation of Safe64.
35
+ *
36
+ * If the `FORMAT` is `NONE` or `PHP`, the `TYPE` is ignored entirely.
37
+ *
38
+ * @alias module:@lumjs/encode/safe64.TYPE
39
+ */
40
+ exports.TYPE = Enum(['RAW', 'ARR_OBJ', 'STD_OBJ']);