238 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			238 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | /** | ||
|  |  * 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. | ||
|  |  */ | ||
|  | (function() { | ||
|  | /* ########## Begin module implementation ########## */ | ||
|  | function initModule(forge) { | ||
|  | 
 | ||
|  | // forge.random already defined
 | ||
|  | if(forge.random && forge.random.getBytes) { | ||
|  |   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 _nodejs = ( | ||
|  |   typeof process !== 'undefined' && process.versions && process.versions.node); | ||
|  | var getRandomValues = null; | ||
|  | if(typeof window !== 'undefined') { | ||
|  |   var _crypto = window.crypto || window.msCrypto; | ||
|  |   if(_crypto && _crypto.getRandomValues) { | ||
|  |     getRandomValues = function(arr) { | ||
|  |       return _crypto.getRandomValues(arr); | ||
|  |     }; | ||
|  |   } | ||
|  | } | ||
|  | if(forge.disableNativeCode || (!_nodejs && !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; | ||
|  | 
 | ||
|  | })(typeof(jQuery) !== 'undefined' ? jQuery : null); | ||
|  | 
 | ||
|  | } // end module implementation
 | ||
|  | 
 | ||
|  | /* ########## Begin module wrapper ########## */ | ||
|  | var name = 'random'; | ||
|  | if(typeof define !== 'function') { | ||
|  |   // NodeJS -> AMD
 | ||
|  |   if(typeof module === 'object' && module.exports) { | ||
|  |     var nodeJS = true; | ||
|  |     define = function(ids, factory) { | ||
|  |       factory(require, module); | ||
|  |     }; | ||
|  |   } else { | ||
|  |     // <script>
 | ||
|  |     if(typeof forge === 'undefined') { | ||
|  |       forge = {}; | ||
|  |     } | ||
|  |     return initModule(forge); | ||
|  |   } | ||
|  | } | ||
|  | // AMD
 | ||
|  | var deps; | ||
|  | var defineFunc = function(require, module) { | ||
|  |   module.exports = function(forge) { | ||
|  |     var mods = deps.map(function(dep) { | ||
|  |       return require(dep); | ||
|  |     }).concat(initModule); | ||
|  |     // handle circular dependencies
 | ||
|  |     forge = forge || {}; | ||
|  |     forge.defined = forge.defined || {}; | ||
|  |     if(forge.defined[name]) { | ||
|  |       return forge[name]; | ||
|  |     } | ||
|  |     forge.defined[name] = true; | ||
|  |     for(var i = 0; i < mods.length; ++i) { | ||
|  |       mods[i](forge); | ||
|  |     } | ||
|  |     return forge[name]; | ||
|  |   }; | ||
|  | }; | ||
|  | var tmpDefine = define; | ||
|  | define = function(ids, factory) { | ||
|  |   deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2); | ||
|  |   if(nodeJS) { | ||
|  |     delete define; | ||
|  |     return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0)); | ||
|  |   } | ||
|  |   define = tmpDefine; | ||
|  |   return define.apply(null, Array.prototype.slice.call(arguments, 0)); | ||
|  | }; | ||
|  | define(['require', 'module', './aes', './md', './prng', './util'], function() { | ||
|  |   defineFunc.apply(null, Array.prototype.slice.call(arguments, 0)); | ||
|  | }); | ||
|  | })(); |