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