Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ultraviolet
GitHub Repository: ultraviolet/bitaddress.org
Path: blob/master/src/securerandom.js
248 views
1
/*!
2
* Random number generator with ArcFour PRNG
3
*
4
* NOTE: For best results, put code like
5
* <body onclick='SecureRandom.seedTime();' onkeypress='SecureRandom.seedTime();'>
6
* in your main HTML document.
7
*
8
* Copyright Tom Wu, bitaddress.org BSD License.
9
* http://www-cs-students.stanford.edu/~tjw/jsbn/LICENSE
10
*/
11
(function () {
12
13
// Constructor function of Global SecureRandom object
14
var sr = window.SecureRandom = function () { };
15
16
// Properties
17
sr.state;
18
sr.pool;
19
sr.pptr;
20
sr.poolCopyOnInit;
21
22
// Pool size must be a multiple of 4 and greater than 32.
23
// An array of bytes the size of the pool will be passed to init()
24
sr.poolSize = 256;
25
26
// --- object methods ---
27
28
// public method
29
// ba: byte array
30
sr.prototype.nextBytes = function (ba) {
31
var i;
32
if (window.crypto && window.crypto.getRandomValues && window.Uint8Array) {
33
try {
34
var rvBytes = new Uint8Array(ba.length);
35
window.crypto.getRandomValues(rvBytes);
36
for (i = 0; i < ba.length; ++i)
37
ba[i] = sr.getByte() ^ rvBytes[i];
38
return;
39
} catch (e) {
40
alert(e);
41
}
42
}
43
for (i = 0; i < ba.length; ++i) ba[i] = sr.getByte();
44
};
45
46
47
// --- static methods ---
48
49
// Mix in the current time (w/milliseconds) into the pool
50
// NOTE: this method should be called from body click/keypress event handlers to increase entropy
51
sr.seedTime = function () {
52
sr.seedInt(new Date().getTime());
53
}
54
55
sr.getByte = function () {
56
if (sr.state == null) {
57
sr.seedTime();
58
sr.state = sr.ArcFour(); // Plug in your RNG constructor here
59
sr.state.init(sr.pool);
60
sr.poolCopyOnInit = [];
61
for (sr.pptr = 0; sr.pptr < sr.pool.length; ++sr.pptr)
62
sr.poolCopyOnInit[sr.pptr] = sr.pool[sr.pptr];
63
sr.pptr = 0;
64
}
65
// TODO: allow reseeding after first request
66
return sr.state.next();
67
}
68
69
// Mix in a 32-bit integer into the pool
70
sr.seedInt = function (x) {
71
sr.seedInt8(x);
72
sr.seedInt8((x >> 8));
73
sr.seedInt8((x >> 16));
74
sr.seedInt8((x >> 24));
75
}
76
77
// Mix in a 16-bit integer into the pool
78
sr.seedInt16 = function (x) {
79
sr.seedInt8(x);
80
sr.seedInt8((x >> 8));
81
}
82
83
// Mix in a 8-bit integer into the pool
84
sr.seedInt8 = function (x) {
85
sr.pool[sr.pptr++] ^= x & 255;
86
if (sr.pptr >= sr.poolSize) sr.pptr -= sr.poolSize;
87
}
88
89
// Arcfour is a PRNG
90
sr.ArcFour = function () {
91
function Arcfour() {
92
this.i = 0;
93
this.j = 0;
94
this.S = new Array();
95
}
96
97
// Initialize arcfour context from key, an array of ints, each from [0..255]
98
function ARC4init(key) {
99
var i, j, t;
100
for (i = 0; i < 256; ++i)
101
this.S[i] = i;
102
j = 0;
103
for (i = 0; i < 256; ++i) {
104
j = (j + this.S[i] + key[i % key.length]) & 255;
105
t = this.S[i];
106
this.S[i] = this.S[j];
107
this.S[j] = t;
108
}
109
this.i = 0;
110
this.j = 0;
111
}
112
113
function ARC4next() {
114
var t;
115
this.i = (this.i + 1) & 255;
116
this.j = (this.j + this.S[this.i]) & 255;
117
t = this.S[this.i];
118
this.S[this.i] = this.S[this.j];
119
this.S[this.j] = t;
120
return this.S[(t + this.S[this.i]) & 255];
121
}
122
123
Arcfour.prototype.init = ARC4init;
124
Arcfour.prototype.next = ARC4next;
125
126
return new Arcfour();
127
};
128
129
130
// Initialize the pool with junk if needed.
131
if (sr.pool == null) {
132
sr.pool = new Array();
133
sr.pptr = 0;
134
var t;
135
if (window.crypto && window.crypto.getRandomValues && window.Uint8Array) {
136
try {
137
// Use webcrypto if available
138
var ua = new Uint8Array(sr.poolSize);
139
window.crypto.getRandomValues(ua);
140
for (t = 0; t < sr.poolSize; ++t)
141
sr.pool[sr.pptr++] = ua[t];
142
} catch (e) { alert(e); }
143
}
144
while (sr.pptr < sr.poolSize) { // extract some randomness from Math.random()
145
t = Math.floor(65536 * Math.random());
146
sr.pool[sr.pptr++] = t >>> 8;
147
sr.pool[sr.pptr++] = t & 255;
148
}
149
sr.pptr = Math.floor(sr.poolSize * Math.random());
150
sr.seedTime();
151
// entropy
152
var entropyStr = "";
153
// screen size and color depth: ~4.8 to ~5.4 bits
154
entropyStr += (window.screen.height * window.screen.width * window.screen.colorDepth);
155
entropyStr += (window.screen.availHeight * window.screen.availWidth * window.screen.pixelDepth);
156
// time zone offset: ~4 bits
157
var dateObj = new Date();
158
var timeZoneOffset = dateObj.getTimezoneOffset();
159
entropyStr += timeZoneOffset;
160
// user agent: ~8.3 to ~11.6 bits
161
entropyStr += navigator.userAgent;
162
// browser plugin details: ~16.2 to ~21.8 bits
163
var pluginsStr = "";
164
for (var i = 0; i < navigator.plugins.length; i++) {
165
pluginsStr += navigator.plugins[i].name + " " + navigator.plugins[i].filename + " " + navigator.plugins[i].description + " " + navigator.plugins[i].version + ", ";
166
}
167
var mimeTypesStr = "";
168
for (var i = 0; i < navigator.mimeTypes.length; i++) {
169
mimeTypesStr += navigator.mimeTypes[i].description + " " + navigator.mimeTypes[i].type + " " + navigator.mimeTypes[i].suffixes + ", ";
170
}
171
entropyStr += pluginsStr + mimeTypesStr;
172
// cookies and storage: 1 bit
173
entropyStr += navigator.cookieEnabled + typeof (sessionStorage) + typeof (localStorage);
174
// language: ~7 bit
175
entropyStr += navigator.language;
176
// history: ~2 bit
177
entropyStr += window.history.length;
178
// location
179
entropyStr += window.location;
180
181
var entropyBytes = Crypto.SHA256(entropyStr, { asBytes: true });
182
for (var i = 0 ; i < entropyBytes.length ; i++) {
183
sr.seedInt8(entropyBytes[i]);
184
}
185
}
186
})();
187