summaryrefslogtreecommitdiff
path: root/node_modules/node-forge/lib/prng.js
diff options
context:
space:
mode:
authorakiyamn2023-09-24 23:22:21 +1000
committerakiyamn2023-09-24 23:22:21 +1000
commit4e87195739f2a5d9a05451b48773c8afdc680765 (patch)
tree9cba501844a4a11dcbdffc4050ed8189561c55ed /node_modules/node-forge/lib/prng.js
downloadprice-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.tar.gz
price-tracker-worker-4e87195739f2a5d9a05451b48773c8afdc680765.zip
Initial commit (by create-cloudflare CLI)
Diffstat (limited to 'node_modules/node-forge/lib/prng.js')
-rw-r--r--node_modules/node-forge/lib/prng.js419
1 files changed, 419 insertions, 0 deletions
diff --git a/node_modules/node-forge/lib/prng.js b/node_modules/node-forge/lib/prng.js
new file mode 100644
index 0000000..d3bd22e
--- /dev/null
+++ b/node_modules/node-forge/lib/prng.js
@@ -0,0 +1,419 @@
+/**
+ * A javascript implementation of a cryptographically-secure
+ * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed
+ * here though the use of SHA-256 is not enforced; when generating an
+ * a PRNG context, the hashing algorithm and block cipher used for
+ * the generator are specified via a plugin.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2014 Digital Bazaar, Inc.
+ */
+var forge = require('./forge');
+require('./util');
+
+var _crypto = null;
+if(forge.util.isNodejs && !forge.options.usePureJavaScript &&
+ !process.versions['node-webkit']) {
+ _crypto = require('crypto');
+}
+
+/* PRNG API */
+var prng = module.exports = forge.prng = forge.prng || {};
+
+/**
+ * Creates a new PRNG context.
+ *
+ * A PRNG plugin must be passed in that will provide:
+ *
+ * 1. A function that initializes the key and seed of a PRNG context. It
+ * will be given a 16 byte key and a 16 byte seed. Any key expansion
+ * or transformation of the seed from a byte string into an array of
+ * integers (or similar) should be performed.
+ * 2. The cryptographic function used by the generator. It takes a key and
+ * a seed.
+ * 3. A seed increment function. It takes the seed and returns seed + 1.
+ * 4. An api to create a message digest.
+ *
+ * For an example, see random.js.
+ *
+ * @param plugin the PRNG plugin to use.
+ */
+prng.create = function(plugin) {
+ var ctx = {
+ plugin: plugin,
+ key: null,
+ seed: null,
+ time: null,
+ // number of reseeds so far
+ reseeds: 0,
+ // amount of data generated so far
+ generated: 0,
+ // no initial key bytes
+ keyBytes: ''
+ };
+
+ // create 32 entropy pools (each is a message digest)
+ var md = plugin.md;
+ var pools = new Array(32);
+ for(var i = 0; i < 32; ++i) {
+ pools[i] = md.create();
+ }
+ ctx.pools = pools;
+
+ // entropy pools are written to cyclically, starting at index 0
+ ctx.pool = 0;
+
+ /**
+ * Generates random bytes. The bytes may be generated synchronously or
+ * asynchronously. Web workers must use the asynchronous interface or
+ * else the behavior is undefined.
+ *
+ * @param count the number of random bytes to generate.
+ * @param [callback(err, bytes)] called once the operation completes.
+ *
+ * @return count random bytes as a string.
+ */
+ ctx.generate = function(count, callback) {
+ // do synchronously
+ if(!callback) {
+ return ctx.generateSync(count);
+ }
+
+ // simple generator using counter-based CBC
+ var cipher = ctx.plugin.cipher;
+ var increment = ctx.plugin.increment;
+ var formatKey = ctx.plugin.formatKey;
+ var formatSeed = ctx.plugin.formatSeed;
+ var b = forge.util.createBuffer();
+
+ // paranoid deviation from Fortuna:
+ // reset key for every request to protect previously
+ // generated random bytes should the key be discovered;
+ // there is no 100ms based reseeding because of this
+ // forced reseed for every `generate` call
+ ctx.key = null;
+
+ generate();
+
+ function generate(err) {
+ if(err) {
+ return callback(err);
+ }
+
+ // sufficient bytes generated
+ if(b.length() >= count) {
+ return callback(null, b.getBytes(count));
+ }
+
+ // if amount of data generated is greater than 1 MiB, trigger reseed
+ if(ctx.generated > 0xfffff) {
+ ctx.key = null;
+ }
+
+ if(ctx.key === null) {
+ // prevent stack overflow
+ return forge.util.nextTick(function() {
+ _reseed(generate);
+ });
+ }
+
+ // generate the random bytes
+ var bytes = cipher(ctx.key, ctx.seed);
+ ctx.generated += bytes.length;
+ b.putBytes(bytes);
+
+ // generate bytes for a new key and seed
+ ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
+ ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
+
+ forge.util.setImmediate(generate);
+ }
+ };
+
+ /**
+ * Generates random bytes synchronously.
+ *
+ * @param count the number of random bytes to generate.
+ *
+ * @return count random bytes as a string.
+ */
+ ctx.generateSync = function(count) {
+ // simple generator using counter-based CBC
+ var cipher = ctx.plugin.cipher;
+ var increment = ctx.plugin.increment;
+ var formatKey = ctx.plugin.formatKey;
+ var formatSeed = ctx.plugin.formatSeed;
+
+ // paranoid deviation from Fortuna:
+ // reset key for every request to protect previously
+ // generated random bytes should the key be discovered;
+ // there is no 100ms based reseeding because of this
+ // forced reseed for every `generateSync` call
+ ctx.key = null;
+
+ var b = forge.util.createBuffer();
+ while(b.length() < count) {
+ // if amount of data generated is greater than 1 MiB, trigger reseed
+ if(ctx.generated > 0xfffff) {
+ ctx.key = null;
+ }
+
+ if(ctx.key === null) {
+ _reseedSync();
+ }
+
+ // generate the random bytes
+ var bytes = cipher(ctx.key, ctx.seed);
+ ctx.generated += bytes.length;
+ b.putBytes(bytes);
+
+ // generate bytes for a new key and seed
+ ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
+ ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
+ }
+
+ return b.getBytes(count);
+ };
+
+ /**
+ * Private function that asynchronously reseeds a generator.
+ *
+ * @param callback(err) called once the operation completes.
+ */
+ function _reseed(callback) {
+ if(ctx.pools[0].messageLength >= 32) {
+ _seed();
+ return callback();
+ }
+ // not enough seed data...
+ var needed = (32 - ctx.pools[0].messageLength) << 5;
+ ctx.seedFile(needed, function(err, bytes) {
+ if(err) {
+ return callback(err);
+ }
+ ctx.collect(bytes);
+ _seed();
+ callback();
+ });
+ }
+
+ /**
+ * Private function that synchronously reseeds a generator.
+ */
+ function _reseedSync() {
+ if(ctx.pools[0].messageLength >= 32) {
+ return _seed();
+ }
+ // not enough seed data...
+ var needed = (32 - ctx.pools[0].messageLength) << 5;
+ ctx.collect(ctx.seedFileSync(needed));
+ _seed();
+ }
+
+ /**
+ * Private function that seeds a generator once enough bytes are available.
+ */
+ function _seed() {
+ // update reseed count
+ ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1;
+
+ // goal is to update `key` via:
+ // key = hash(key + s)
+ // where 's' is all collected entropy from selected pools, then...
+
+ // create a plugin-based message digest
+ var md = ctx.plugin.md.create();
+
+ // consume current key bytes
+ md.update(ctx.keyBytes);
+
+ // digest the entropy of pools whose index k meet the
+ // condition 'n mod 2^k == 0' where n is the number of reseeds
+ var _2powK = 1;
+ for(var k = 0; k < 32; ++k) {
+ if(ctx.reseeds % _2powK === 0) {
+ md.update(ctx.pools[k].digest().getBytes());
+ ctx.pools[k].start();
+ }
+ _2powK = _2powK << 1;
+ }
+
+ // get digest for key bytes
+ ctx.keyBytes = md.digest().getBytes();
+
+ // paranoid deviation from Fortuna:
+ // update `seed` via `seed = hash(key)`
+ // instead of initializing to zero once and only
+ // ever incrementing it
+ md.start();
+ md.update(ctx.keyBytes);
+ var seedBytes = md.digest().getBytes();
+
+ // update state
+ ctx.key = ctx.plugin.formatKey(ctx.keyBytes);
+ ctx.seed = ctx.plugin.formatSeed(seedBytes);
+ ctx.generated = 0;
+ }
+
+ /**
+ * The built-in default seedFile. This seedFile is used when entropy
+ * is needed immediately.
+ *
+ * @param needed the number of bytes that are needed.
+ *
+ * @return the random bytes.
+ */
+ function defaultSeedFile(needed) {
+ // use window.crypto.getRandomValues strong source of entropy if available
+ var getRandomValues = null;
+ var globalScope = forge.util.globalScope;
+ var _crypto = globalScope.crypto || globalScope.msCrypto;
+ if(_crypto && _crypto.getRandomValues) {
+ getRandomValues = function(arr) {
+ return _crypto.getRandomValues(arr);
+ };
+ }
+
+ var b = forge.util.createBuffer();
+ if(getRandomValues) {
+ while(b.length() < needed) {
+ // max byte length is 65536 before QuotaExceededError is thrown
+ // http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues
+ var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4);
+ var entropy = new Uint32Array(Math.floor(count));
+ try {
+ getRandomValues(entropy);
+ for(var i = 0; i < entropy.length; ++i) {
+ b.putInt32(entropy[i]);
+ }
+ } catch(e) {
+ /* only ignore QuotaExceededError */
+ if(!(typeof QuotaExceededError !== 'undefined' &&
+ e instanceof QuotaExceededError)) {
+ throw e;
+ }
+ }
+ }
+ }
+
+ // be sad and add some weak random data
+ if(b.length() < needed) {
+ /* Draws from Park-Miller "minimal standard" 31 bit PRNG,
+ implemented with David G. Carta's optimization: with 32 bit math
+ and without division (Public Domain). */
+ var hi, lo, next;
+ var seed = Math.floor(Math.random() * 0x010000);
+ while(b.length() < needed) {
+ lo = 16807 * (seed & 0xFFFF);
+ hi = 16807 * (seed >> 16);
+ lo += (hi & 0x7FFF) << 16;
+ lo += hi >> 15;
+ lo = (lo & 0x7FFFFFFF) + (lo >> 31);
+ seed = lo & 0xFFFFFFFF;
+
+ // consume lower 3 bytes of seed
+ for(var i = 0; i < 3; ++i) {
+ // throw in more pseudo random
+ next = seed >>> (i << 3);
+ next ^= Math.floor(Math.random() * 0x0100);
+ b.putByte(next & 0xFF);
+ }
+ }
+ }
+
+ return b.getBytes(needed);
+ }
+ // initialize seed file APIs
+ if(_crypto) {
+ // use nodejs async API
+ ctx.seedFile = function(needed, callback) {
+ _crypto.randomBytes(needed, function(err, bytes) {
+ if(err) {
+ return callback(err);
+ }
+ callback(null, bytes.toString());
+ });
+ };
+ // use nodejs sync API
+ ctx.seedFileSync = function(needed) {
+ return _crypto.randomBytes(needed).toString();
+ };
+ } else {
+ ctx.seedFile = function(needed, callback) {
+ try {
+ callback(null, defaultSeedFile(needed));
+ } catch(e) {
+ callback(e);
+ }
+ };
+ ctx.seedFileSync = defaultSeedFile;
+ }
+
+ /**
+ * Adds entropy to a prng ctx's accumulator.
+ *
+ * @param bytes the bytes of entropy as a string.
+ */
+ ctx.collect = function(bytes) {
+ // iterate over pools distributing entropy cyclically
+ var count = bytes.length;
+ for(var i = 0; i < count; ++i) {
+ ctx.pools[ctx.pool].update(bytes.substr(i, 1));
+ ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
+ }
+ };
+
+ /**
+ * Collects an integer of n bits.
+ *
+ * @param i the integer entropy.
+ * @param n the number of bits in the integer.
+ */
+ ctx.collectInt = function(i, n) {
+ var bytes = '';
+ for(var x = 0; x < n; x += 8) {
+ bytes += String.fromCharCode((i >> x) & 0xFF);
+ }
+ ctx.collect(bytes);
+ };
+
+ /**
+ * Registers a Web Worker to receive immediate entropy from the main thread.
+ * This method is required until Web Workers can access the native crypto
+ * API. This method should be called twice for each created worker, once in
+ * the main thread, and once in the worker itself.
+ *
+ * @param worker the worker to register.
+ */
+ ctx.registerWorker = function(worker) {
+ // worker receives random bytes
+ if(worker === self) {
+ ctx.seedFile = function(needed, callback) {
+ function listener(e) {
+ var data = e.data;
+ if(data.forge && data.forge.prng) {
+ self.removeEventListener('message', listener);
+ callback(data.forge.prng.err, data.forge.prng.bytes);
+ }
+ }
+ self.addEventListener('message', listener);
+ self.postMessage({forge: {prng: {needed: needed}}});
+ };
+ } else {
+ // main thread sends random bytes upon request
+ var listener = function(e) {
+ var data = e.data;
+ if(data.forge && data.forge.prng) {
+ ctx.seedFile(data.forge.prng.needed, function(err, bytes) {
+ worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});
+ });
+ }
+ };
+ // TODO: do we need to remove the event listener when the worker dies?
+ worker.addEventListener('message', listener);
+ }
+ };
+
+ return ctx;
+};