-
Notifications
You must be signed in to change notification settings - Fork 0
/
seedrandom.js
235 lines (218 loc) · 6.83 KB
/
seedrandom.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
(function (global, pool, math, width, chunks, digits, module, define, rngname) {
//
// The following constants are related to IEEE 754 limits.
//
var startdenom = math.pow(width, chunks),
significance = math.pow(2, digits),
overflow = significance * 2,
mask = width - 1,
nodecrypto;
//
// seedrandom()
// This is the seedrandom function described above.
//
var impl = (math["seed" + rngname] = function (seed, options, callback) {
var key = [];
options = options == true ? { entropy: true } : options || {};
// Flatten the seed string or build one from local entropy if needed.
var shortseed = mixkey(
flatten(
options.entropy
? [seed, tostring(pool)]
: seed == null
? autoseed()
: seed,
3
),
key
);
// Use the seed to initialize an ARC4 generator.
var arc4 = new ARC4(key);
// Mix the randomness into accumulated entropy.
mixkey(tostring(arc4.S), pool);
// Calling convention: what to return as a function of prng, seed, is_math.
return (
options.pass ||
callback ||
// If called as a method of Math (Math.seedrandom()), mutate Math.random
// because that is how seedrandom.js has worked since v1.0. Otherwise,
// it is a newer calling convention, so return the prng directly.
function (prng, seed, is_math_call) {
if (is_math_call) {
math[rngname] = prng;
return seed;
} else return prng;
}
)(
// This function returns a random double in [0, 1) that contains
// randomness in every bit of the mantissa of the IEEE 754 value.
function () {
var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48
d = startdenom, // and denominator d = 2 ^ 48.
x = 0; // and no 'extra last byte'.
while (n < significance) {
// Fill up all significant digits by
n = (n + x) * width; // shifting numerator and
d *= width; // denominator and generating a
x = arc4.g(1); // new least-significant-byte.
}
while (n >= overflow) {
// To avoid rounding up, before adding
n /= 2; // last byte, shift everything
d /= 2; // right using integer math until
x >>>= 1; // we have exactly the desired bits.
}
return (n + x) / d; // Form the number within [0, 1).
},
shortseed,
"global" in options ? options.global : this == math
);
});
//
// ARC4
//
// An ARC4 implementation. The constructor takes a key in the form of
// an array of at most (width) integers that should be 0 <= x < (width).
//
// The g(count) method returns a pseudorandom integer that concatenates
// the next (count) outputs from ARC4. Its return value is a number x
// that is in the range 0 <= x < (width ^ count).
//
/** @constructor */
function ARC4(key) {
var t,
keylen = key.length,
me = this,
i = 0,
j = (me.i = me.j = 0),
s = (me.S = []);
// The empty key [] is treated as [0].
if (!keylen) {
key = [keylen++];
}
// Set up S using the standard key scheduling algorithm.
while (i < width) {
s[i] = i++;
}
for (i = 0; i < width; i++) {
s[i] = s[(j = mask & (j + key[i % keylen] + (t = s[i])))];
s[j] = t;
}
// The "g" method returns the next (count) outputs as one number.
(me.g = function (count) {
// Using instance members instead of closure state nearly doubles speed.
var t,
r = 0,
i = me.i,
j = me.j,
s = me.S;
while (count--) {
t = s[(i = mask & (i + 1))];
r =
r * width + s[mask & ((s[i] = s[(j = mask & (j + t))]) + (s[j] = t))];
}
me.i = i;
me.j = j;
return r;
// For robust unpredictability, the function call below automatically
// discards an initial batch of values. This is called RC4-drop[256].
// See http://google.com/search?q=rsa+fluhrer+response&btnI
})(width);
}
//
// flatten()
// Converts an object tree to nested arrays of strings.
//
function flatten(obj, depth) {
var result = [],
typ = typeof obj,
prop;
if (depth && typ == "object") {
for (prop in obj) {
try {
result.push(flatten(obj[prop], depth - 1));
} catch (e) {}
}
}
return result.length ? result : typ == "string" ? obj : obj + "\0";
}
//
// mixkey()
// Mixes a string seed into a key that is an array of integers, and
// returns a shortened string seed that is equivalent to the result key.
//
function mixkey(seed, key) {
var stringseed = seed + "",
smear,
j = 0;
while (j < stringseed.length) {
key[mask & j] =
mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++));
}
return tostring(key);
}
//
// autoseed()
// Returns an object for autoseeding, using window.crypto if available.
//
/** @param {Uint8Array|Navigator=} seed */
function autoseed(seed) {
try {
if (nodecrypto) return tostring(nodecrypto.randomBytes(width));
global.crypto.getRandomValues((seed = new Uint8Array(width)));
return tostring(seed);
} catch (e) {
return [
+new Date(),
global,
(seed = global.navigator) && seed.plugins,
global.screen,
tostring(pool),
];
}
}
//
// tostring()
// Converts an array of charcodes to a string
//
function tostring(a) {
return String.fromCharCode.apply(0, a);
}
//
// When seedrandom.js is loaded, we immediately mix a few bits
// from the built-in RNG into the entropy pool. Because we do
// not want to interfere with deterministic PRNG state later,
// seedrandom will not call math.random on its own again after
// initialization.
//
mixkey(math[rngname](), pool);
//
// Nodejs and AMD support: export the implementation as a module using
// either convention.
//
if (module && module.exports) {
module.exports = impl;
try {
// When in node.js, try using crypto package for autoseeding.
nodecrypto = require("crypto");
} catch (ex) {}
} else if (define && define.amd) {
define(function () {
return impl;
});
}
//
// Node.js native crypto support.
//
// End anonymous scope, and pass initial values.
})(
this, // global window object
[], // pool: entropy pool starts empty
Math, // math: package containing random, pow, and seedrandom
256, // width: each RC4 output is 0 <= x < 256
6, // chunks: at least six RC4 outputs for each double
52, // digits: there are 52 significant digits in a double
typeof module == "object" && module, // present in node.js
typeof define == "function" && define, // present with an AMD loader
"random" // rngname: name for Math.random and Math.seedrandom
);