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));
 | |
| });
 | |
| })();
 |