@sunney/flareutils
Version:
Small Utilities and little goodies that make developing with Cloudflare easier and faster.
181 lines • 7.22 kB
JavaScript
/* ----------------------------------------------------------------------
* Copyright (c) 2012 Yves-Marie K. Rinquin
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* ----------------------------------------------------------------------
*
* ISAAC is a cryptographically secure pseudo-random number generator
* (or CSPRNG for short) designed by Robert J. Jenkins Jr. in 1996 and
* based on RC4. It is designed for speed and security.
*
* ISAAC's informations & analysis:
* http://burtleburtle.net/bob/rand/isaac.html
* ISAAC's implementation details:
* http://burtleburtle.net/bob/rand/isaacafa.html
*
* ISAAC succesfully passed TestU01
*/
import { toIntArray, add, seed_mix } from "./helpers";
/**
* Cryptographically-secure random number generator. Based on the [ISAAC algorithm](http://burtleburtle.net/bob/rand/isaac.html) by [Bob Jenkins](http://burtleburtle.net/bob/), and the JS implementation by [Yves-Marie K. Rinquin](https://github.com/rubycon). Backed by [crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues), and the [DRAND](https://drand.love) network.
*/
export class Isaac {
runs;
m;
acc;
brs;
cnt;
r;
gnt;
/**
* This promise represents whether the seeding process has been completed. It is recommended that you create the Isaac object as early as possible, do other tasks as needed, and then *await* the promise afterward to ensure that the seeding process has completed. If it is *false*, then the seeding process has completed, and no *await* is necessary.
*/
seeding;
/**
* Creates a new Isaac CSPRNG. Note that you must await `seeding` before using the generator.
* @param {IsaacSeed} seed Seed to be fed into the generator.
* @param {number} runs Number of times to re-run the generator.
* @constructor
*/
constructor(seed, runs) {
this.runs = runs || 1;
this.m = this.r = Array(256);
this.acc = this.brs = this.cnt = this.gnt = 0;
this.seeding = this.seed(seed);
}
/**
* Batch-generates 256 random numbers, and stores them in the number buffer.
* @private
*/
prng() {
let i;
let x;
let y;
let n = this.runs;
while (n--) {
this.cnt = add(this.cnt, 1);
this.brs = add(this.brs, this.cnt);
for (i = 0; i < 256; i++) {
switch (i & 3) {
case 0:
this.acc ^= this.acc << 13;
break;
case 1:
this.acc ^= this.acc >>> 6;
break;
case 2:
this.acc ^= this.acc << 2;
break;
case 3:
this.acc ^= this.acc >>> 16;
break;
}
this.acc = add(this.m[(i + 128) & 0xff], this.acc);
x = this.m[i];
this.m[i] = y = add(this.m[(x >>> 2) & 0xff], add(this.acc, this.brs));
this.r[i] = this.brs = add(this.m[(y >>> 10) & 0xff], x);
}
}
}
/**
* Shuffles the given array with the seed array, to ensure even mix.
* @param {number[]} arr Array filled with mixed numbers
* @param {number[]} s The seed, as an array of numbers
* @returns {number[]} The mixed array
* @private
*/
superShuffle(arr, s) {
for (let i = 0; i < 256; i += 8) {
if (s)
/* use all the information in the seed */
for (let j = 0; j < 8; j++)
arr[j] = add(arr[j], this.r[i + j]);
arr = seed_mix(arr);
/* fill in m[] with messy stuff */
for (let j = 0; j < 8; j++)
this.m[i + j] = arr[j];
}
return arr;
}
/**
* Resets the internal state of the generator.
* @private
*/
reset() {
this.acc = this.brs = this.cnt = this.gnt = 0;
for (let i = 0; i < 256; ++i)
this.m[i] = this.r[i] = 0;
}
/**
* Seeds the generator. Note that you must await `seeding` before using the generator, if not manually seeded and awaited.
* @param {IsaacSeed} seed Seed to be fed into the generator
* @returns {Promise<void>} Promise that resolves when the generator has been seeded
* @async
* @example ```ts
* await isaac.seed(seed);
* ```
*/
async seed(seed) {
let arr = Array.from(crypto.getRandomValues(new Uint32Array(9)));
let s = [];
let i;
/* seeding the seeds of love */
// a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */
if (seed && typeof seed === "string")
s = toIntArray(seed);
if (seed && typeof seed === "number")
s = [seed];
try {
s.push(Number((await (await fetch("https://drand.cloudflare.com/public/latest")).json()).signature));
}
catch (e) {
// If DRAND doesn't respond, fall back on rest of seed.
}
s.push(Date.now());
this.reset();
for (i = 0; i < s.length; i++)
this.r[i & 0xff] += typeof s[i] === "number" ? s[i] : 0;
for (i = 0; i < 4; i++ /* scramble it */)
arr = seed_mix(arr);
arr = this.superShuffle(arr, s);
/* if more of the seed still exists, do a second pass to make all of the seed affect all of m[] */
if (s)
arr = this.superShuffle(arr, s);
this.prng(); /* fill in the first set of results */
this.gnt = 256; /* prepare to use the first set of results */
this.seeding = false;
}
/**
* Returns a pre-generated random number from the number buffer.
* @returns {number} A random number between 0 and 1
* @example ```ts
* const num = isaac.rand();
* ```
*/
rand() {
if (!this.gnt--) {
this.prng();
this.gnt = 255;
}
return 0.5 + this.r[this.gnt] * 2.3283064365386963e-10;
}
}
//# sourceMappingURL=index.js.map