You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					192 lines
				
				5.3 KiB
			
		
		
			
		
	
	
					192 lines
				
				5.3 KiB
			| 
											2 years ago
										 | /** | ||
|  |  * An API for getting cryptographically-secure random bytes. The bytes are | ||
|  |  * generated using the Fortuna algorithm devised by Bruce Schneier and | ||
|  |  * Niels Ferguson. | ||
|  |  * | ||
|  |  * Getting strong random bytes is not yet easy to do in javascript. The only | ||
|  |  * truish random entropy that can be collected is from the mouse, keyboard, or | ||
|  |  * from timing with respect to page loads, etc. This generator makes a poor | ||
|  |  * attempt at providing random bytes when those sources haven't yet provided | ||
|  |  * enough entropy to initially seed or to reseed the PRNG. | ||
|  |  * | ||
|  |  * @author Dave Longley | ||
|  |  * | ||
|  |  * Copyright (c) 2009-2014 Digital Bazaar, Inc. | ||
|  |  */ | ||
|  | var forge = require('./forge'); | ||
|  | require('./aes'); | ||
|  | require('./sha256'); | ||
|  | require('./prng'); | ||
|  | require('./util'); | ||
|  | 
 | ||
|  | (function() { | ||
|  | 
 | ||
|  | // forge.random already defined
 | ||
|  | if(forge.random && forge.random.getBytes) { | ||
|  |   module.exports = forge.random; | ||
|  |   return; | ||
|  | } | ||
|  | 
 | ||
|  | (function(jQuery) { | ||
|  | 
 | ||
|  | // the default prng plugin, uses AES-128
 | ||
|  | var prng_aes = {}; | ||
|  | var _prng_aes_output = new Array(4); | ||
|  | var _prng_aes_buffer = forge.util.createBuffer(); | ||
|  | prng_aes.formatKey = function(key) { | ||
|  |   // convert the key into 32-bit integers
 | ||
|  |   var tmp = forge.util.createBuffer(key); | ||
|  |   key = new Array(4); | ||
|  |   key[0] = tmp.getInt32(); | ||
|  |   key[1] = tmp.getInt32(); | ||
|  |   key[2] = tmp.getInt32(); | ||
|  |   key[3] = tmp.getInt32(); | ||
|  | 
 | ||
|  |   // return the expanded key
 | ||
|  |   return forge.aes._expandKey(key, false); | ||
|  | }; | ||
|  | prng_aes.formatSeed = function(seed) { | ||
|  |   // convert seed into 32-bit integers
 | ||
|  |   var tmp = forge.util.createBuffer(seed); | ||
|  |   seed = new Array(4); | ||
|  |   seed[0] = tmp.getInt32(); | ||
|  |   seed[1] = tmp.getInt32(); | ||
|  |   seed[2] = tmp.getInt32(); | ||
|  |   seed[3] = tmp.getInt32(); | ||
|  |   return seed; | ||
|  | }; | ||
|  | prng_aes.cipher = function(key, seed) { | ||
|  |   forge.aes._updateBlock(key, seed, _prng_aes_output, false); | ||
|  |   _prng_aes_buffer.putInt32(_prng_aes_output[0]); | ||
|  |   _prng_aes_buffer.putInt32(_prng_aes_output[1]); | ||
|  |   _prng_aes_buffer.putInt32(_prng_aes_output[2]); | ||
|  |   _prng_aes_buffer.putInt32(_prng_aes_output[3]); | ||
|  |   return _prng_aes_buffer.getBytes(); | ||
|  | }; | ||
|  | prng_aes.increment = function(seed) { | ||
|  |   // FIXME: do we care about carry or signed issues?
 | ||
|  |   ++seed[3]; | ||
|  |   return seed; | ||
|  | }; | ||
|  | prng_aes.md = forge.md.sha256; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Creates a new PRNG. | ||
|  |  */ | ||
|  | function spawnPrng() { | ||
|  |   var ctx = forge.prng.create(prng_aes); | ||
|  | 
 | ||
|  |   /** | ||
|  |    * Gets random bytes. If a native secure crypto API is unavailable, this | ||
|  |    * method tries to make the bytes more unpredictable by drawing from data that | ||
|  |    * can be collected from the user of the browser, eg: mouse movement. | ||
|  |    * | ||
|  |    * If a callback is given, this method will be called asynchronously. | ||
|  |    * | ||
|  |    * @param count the number of random bytes to get. | ||
|  |    * @param [callback(err, bytes)] called once the operation completes. | ||
|  |    * | ||
|  |    * @return the random bytes in a string. | ||
|  |    */ | ||
|  |   ctx.getBytes = function(count, callback) { | ||
|  |     return ctx.generate(count, callback); | ||
|  |   }; | ||
|  | 
 | ||
|  |   /** | ||
|  |    * Gets random bytes asynchronously. If a native secure crypto API is | ||
|  |    * unavailable, this method tries to make the bytes more unpredictable by | ||
|  |    * drawing from data that can be collected from the user of the browser, | ||
|  |    * eg: mouse movement. | ||
|  |    * | ||
|  |    * @param count the number of random bytes to get. | ||
|  |    * | ||
|  |    * @return the random bytes in a string. | ||
|  |    */ | ||
|  |   ctx.getBytesSync = function(count) { | ||
|  |     return ctx.generate(count); | ||
|  |   }; | ||
|  | 
 | ||
|  |   return ctx; | ||
|  | } | ||
|  | 
 | ||
|  | // create default prng context
 | ||
|  | var _ctx = spawnPrng(); | ||
|  | 
 | ||
|  | // add other sources of entropy only if window.crypto.getRandomValues is not
 | ||
|  | // available -- otherwise this source will be automatically used by the prng
 | ||
|  | 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); | ||
|  |   }; | ||
|  | } | ||
|  | 
 | ||
|  | if(forge.options.usePureJavaScript || | ||
|  |   (!forge.util.isNodejs && !getRandomValues)) { | ||
|  |   // if this is a web worker, do not use weak entropy, instead register to
 | ||
|  |   // receive strong entropy asynchronously from the main thread
 | ||
|  |   if(typeof window === 'undefined' || window.document === undefined) { | ||
|  |     // FIXME:
 | ||
|  |   } | ||
|  | 
 | ||
|  |   // get load time entropy
 | ||
|  |   _ctx.collectInt(+new Date(), 32); | ||
|  | 
 | ||
|  |   // add some entropy from navigator object
 | ||
|  |   if(typeof(navigator) !== 'undefined') { | ||
|  |     var _navBytes = ''; | ||
|  |     for(var key in navigator) { | ||
|  |       try { | ||
|  |         if(typeof(navigator[key]) == 'string') { | ||
|  |           _navBytes += navigator[key]; | ||
|  |         } | ||
|  |       } catch(e) { | ||
|  |         /* Some navigator keys might not be accessible, e.g. the geolocation | ||
|  |           attribute throws an exception if touched in Mozilla chrome://
 | ||
|  |           context. | ||
|  | 
 | ||
|  |           Silently ignore this and just don't use this as a source of | ||
|  |           entropy. */ | ||
|  |       } | ||
|  |     } | ||
|  |     _ctx.collect(_navBytes); | ||
|  |     _navBytes = null; | ||
|  |   } | ||
|  | 
 | ||
|  |   // add mouse and keyboard collectors if jquery is available
 | ||
|  |   if(jQuery) { | ||
|  |     // set up mouse entropy capture
 | ||
|  |     jQuery().mousemove(function(e) { | ||
|  |       // add mouse coords
 | ||
|  |       _ctx.collectInt(e.clientX, 16); | ||
|  |       _ctx.collectInt(e.clientY, 16); | ||
|  |     }); | ||
|  | 
 | ||
|  |     // set up keyboard entropy capture
 | ||
|  |     jQuery().keypress(function(e) { | ||
|  |       _ctx.collectInt(e.charCode, 8); | ||
|  |     }); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /* Random API */ | ||
|  | if(!forge.random) { | ||
|  |   forge.random = _ctx; | ||
|  | } else { | ||
|  |   // extend forge.random with _ctx
 | ||
|  |   for(var key in _ctx) { | ||
|  |     forge.random[key] = _ctx[key]; | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | // expose spawn PRNG
 | ||
|  | forge.random.createInstance = spawnPrng; | ||
|  | 
 | ||
|  | module.exports = forge.random; | ||
|  | 
 | ||
|  | })(typeof(jQuery) !== 'undefined' ? jQuery : null); | ||
|  | 
 | ||
|  | })(); |