From a9c99261bd6d68caa9e87188db50861e43c9e6ce Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Tue, 20 Aug 2013 10:45:30 -0700 Subject: [PATCH 1/3] Use crypto.getRandomValues for RNG if available window.crypto.random is really old and depreciated. Should use crypto.getRandomValues instead. If we are able to generate entropy from crypto.getRandomValues, we should not add entropy from Math.random, as it may be unreliable. --- jsbn/rng.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/jsbn/rng.js b/jsbn/rng.js index 03afc3a..be6fd3b 100644 --- a/jsbn/rng.js +++ b/jsbn/rng.js @@ -27,16 +27,18 @@ if(rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; - if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { - // Extract entropy (256 bits) from NS4 RNG if available - var z = window.crypto.random(32); - for(t = 0; t < z.length; ++t) - rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; - } - while(rng_pptr < rng_psize) { // extract some randomness from Math.random() - t = Math.floor(65536 * Math.random()); - rng_pool[rng_pptr++] = t >>> 8; - rng_pool[rng_pptr++] = t & 255; + if(window.crypto && window.crypto.getRandomValues) { + // Extract entropy (256 bits) from RNG if available + var randomBits = window.crypto.getRandomValues(new Uint32Array(32)); + for (t = 0; t < randomBits.length; ++t) + rng_pool[rng_pptr++] = z[t] & 255; + } else { + // Otherwise, extract some randomness from Math.random() + while(rng_pptr < rng_psize) { + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } } rng_pptr = 0; rng_seed_time(); From 2cdd2f740d8d92adcdd554ee845f0554b751d147 Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Tue, 20 Aug 2013 11:31:14 -0700 Subject: [PATCH 2/3] Use better entropy generation methods Using time to generate entropy is not optimal, as time is predictible and not random. This change prefers crypto.getRandomValues, falls back to using mouse movement, and ultimately if enough entropy is not generated by the time we need a key, it will fall back to Math.random. --- jsbn/rng.js | 58 +++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/jsbn/rng.js b/jsbn/rng.js index be6fd3b..7ae84ec 100644 --- a/jsbn/rng.js +++ b/jsbn/rng.js @@ -1,60 +1,48 @@ // Random number generator - requires a PRNG backend, e.g. prng4.js - -// For best results, put code like -// -// in your main HTML document. - var rng_state; var rng_pool; var rng_pptr; -// Mix in a 32-bit integer into the pool -function rng_seed_int(x) { - rng_pool[rng_pptr++] ^= x & 255; - rng_pool[rng_pptr++] ^= (x >> 8) & 255; - rng_pool[rng_pptr++] ^= (x >> 16) & 255; - rng_pool[rng_pptr++] ^= (x >> 24) & 255; - if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; -} - -// Mix in the current time (w/milliseconds) into the pool -function rng_seed_time() { - rng_seed_int(new Date().getTime()); -} - // Initialize the pool with junk if needed. if(rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; if(window.crypto && window.crypto.getRandomValues) { - // Extract entropy (256 bits) from RNG if available - var randomBits = window.crypto.getRandomValues(new Uint32Array(32)); - for (t = 0; t < randomBits.length; ++t) + // Extract entropy (2048 bits) from RNG if available + var z = window.crypto.getRandomValues(new Uint32Array(256)); + for (t = 0; t < z.length; ++t) rng_pool[rng_pptr++] = z[t] & 255; - } else { - // Otherwise, extract some randomness from Math.random() - while(rng_pptr < rng_psize) { - t = Math.floor(65536 * Math.random()); - rng_pool[rng_pptr++] = t >>> 8; - rng_pool[rng_pptr++] = t & 255; + } + + // Use mouse events for entropy, if we do not have enough entropy by the time + // we need it, entropy will be generated by Math.random. + var onMouseMoveListener = function(ev) { + this.count = this.count || 0; + if (this.count >= 256 || rng_pptr >= rng_psize) { + window.removeEventListener("mousemove", onMouseMoveListener); + return; } - } - rng_pptr = 0; - rng_seed_time(); - //rng_seed_int(window.screenX); - //rng_seed_int(window.screenY); + this.count += 1; + var mouseCoordinates = ev.x + ev.y; + rng_pool[rng_pptr++] = mouseCoordinates & 255; + }; + + window.addEventListener("mousemove", onMouseMoveListener); } function rng_get_byte() { if(rng_state == null) { - rng_seed_time(); rng_state = prng_newstate(); + // At this point, we may not have collected enough entropy. If not, fall back to Math.random + while (rng_pptr < rng_psize) { + var random = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = random & 255; + } rng_state.init(rng_pool); for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) rng_pool[rng_pptr] = 0; rng_pptr = 0; - //rng_pool = null; } // TODO: allow reseeding after first request return rng_state.next(); From c3be58aea26a271b640b43c4f72861ea6fafd80b Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Tue, 20 Aug 2013 12:31:01 -0700 Subject: [PATCH 3/3] Initialize Uint32Array before getRandomValues This seems to cause PhantomJS to fail, as getRandomValues does not return the array. --- jsbn/rng.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jsbn/rng.js b/jsbn/rng.js index 7ae84ec..2169874 100644 --- a/jsbn/rng.js +++ b/jsbn/rng.js @@ -10,7 +10,8 @@ if(rng_pool == null) { var t; if(window.crypto && window.crypto.getRandomValues) { // Extract entropy (2048 bits) from RNG if available - var z = window.crypto.getRandomValues(new Uint32Array(256)); + var z = new Uint32Array(256); + window.crypto.getRandomValues(z); for (t = 0; t < z.length; ++t) rng_pool[rng_pptr++] = z[t] & 255; }