/** * 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 { //