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.
		
		
		
		
		
			
		
			
				
					420 lines
				
				12 KiB
			
		
		
			
		
	
	
					420 lines
				
				12 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * A javascript implementation of a cryptographically-secure
							 | 
						||
| 
								 | 
							
								 * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed
							 | 
						||
| 
								 | 
							
								 * here though the use of SHA-256 is not enforced; when generating an
							 | 
						||
| 
								 | 
							
								 * a PRNG context, the hashing algorithm and block cipher used for
							 | 
						||
| 
								 | 
							
								 * the generator are specified via a plugin.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Dave Longley
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2010-2014 Digital Bazaar, Inc.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var forge = require('./forge');
							 | 
						||
| 
								 | 
							
								require('./util');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _crypto = null;
							 | 
						||
| 
								 | 
							
								if(forge.util.isNodejs && !forge.options.usePureJavaScript &&
							 | 
						||
| 
								 | 
							
								  !process.versions['node-webkit']) {
							 | 
						||
| 
								 | 
							
								  _crypto = require('crypto');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* PRNG API */
							 | 
						||
| 
								 | 
							
								var prng = module.exports = forge.prng = forge.prng || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates a new PRNG context.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * A PRNG plugin must be passed in that will provide:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * 1. A function that initializes the key and seed of a PRNG context. It
							 | 
						||
| 
								 | 
							
								 *   will be given a 16 byte key and a 16 byte seed. Any key expansion
							 | 
						||
| 
								 | 
							
								 *   or transformation of the seed from a byte string into an array of
							 | 
						||
| 
								 | 
							
								 *   integers (or similar) should be performed.
							 | 
						||
| 
								 | 
							
								 * 2. The cryptographic function used by the generator. It takes a key and
							 | 
						||
| 
								 | 
							
								 *   a seed.
							 | 
						||
| 
								 | 
							
								 * 3. A seed increment function. It takes the seed and returns seed + 1.
							 | 
						||
| 
								 | 
							
								 * 4. An api to create a message digest.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For an example, see random.js.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param plugin the PRNG plugin to use.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								prng.create = function(plugin) {
							 | 
						||
| 
								 | 
							
								  var ctx = {
							 | 
						||
| 
								 | 
							
								    plugin: plugin,
							 | 
						||
| 
								 | 
							
								    key: null,
							 | 
						||
| 
								 | 
							
								    seed: null,
							 | 
						||
| 
								 | 
							
								    time: null,
							 | 
						||
| 
								 | 
							
								    // number of reseeds so far
							 | 
						||
| 
								 | 
							
								    reseeds: 0,
							 | 
						||
| 
								 | 
							
								    // amount of data generated so far
							 | 
						||
| 
								 | 
							
								    generated: 0,
							 | 
						||
| 
								 | 
							
								    // no initial key bytes
							 | 
						||
| 
								 | 
							
								    keyBytes: ''
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // create 32 entropy pools (each is a message digest)
							 | 
						||
| 
								 | 
							
								  var md = plugin.md;
							 | 
						||
| 
								 | 
							
								  var pools = new Array(32);
							 | 
						||
| 
								 | 
							
								  for(var i = 0; i < 32; ++i) {
							 | 
						||
| 
								 | 
							
								    pools[i] = md.create();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  ctx.pools = pools;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // entropy pools are written to cyclically, starting at index 0
							 | 
						||
| 
								 | 
							
								  ctx.pool = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Generates random bytes. The bytes may be generated synchronously or
							 | 
						||
| 
								 | 
							
								   * asynchronously. Web workers must use the asynchronous interface or
							 | 
						||
| 
								 | 
							
								   * else the behavior is undefined.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param count the number of random bytes to generate.
							 | 
						||
| 
								 | 
							
								   * @param [callback(err, bytes)] called once the operation completes.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return count random bytes as a string.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  ctx.generate = function(count, callback) {
							 | 
						||
| 
								 | 
							
								    // do synchronously
							 | 
						||
| 
								 | 
							
								    if(!callback) {
							 | 
						||
| 
								 | 
							
								      return ctx.generateSync(count);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // simple generator using counter-based CBC
							 | 
						||
| 
								 | 
							
								    var cipher = ctx.plugin.cipher;
							 | 
						||
| 
								 | 
							
								    var increment = ctx.plugin.increment;
							 | 
						||
| 
								 | 
							
								    var formatKey = ctx.plugin.formatKey;
							 | 
						||
| 
								 | 
							
								    var formatSeed = ctx.plugin.formatSeed;
							 | 
						||
| 
								 | 
							
								    var b = forge.util.createBuffer();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // paranoid deviation from Fortuna:
							 | 
						||
| 
								 | 
							
								    // reset key for every request to protect previously
							 | 
						||
| 
								 | 
							
								    // generated random bytes should the key be discovered;
							 | 
						||
| 
								 | 
							
								    // there is no 100ms based reseeding because of this
							 | 
						||
| 
								 | 
							
								    // forced reseed for every `generate` call
							 | 
						||
| 
								 | 
							
								    ctx.key = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    generate();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function generate(err) {
							 | 
						||
| 
								 | 
							
								      if(err) {
							 | 
						||
| 
								 | 
							
								        return callback(err);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // sufficient bytes generated
							 | 
						||
| 
								 | 
							
								      if(b.length() >= count) {
							 | 
						||
| 
								 | 
							
								        return callback(null, b.getBytes(count));
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // if amount of data generated is greater than 1 MiB, trigger reseed
							 | 
						||
| 
								 | 
							
								      if(ctx.generated > 0xfffff) {
							 | 
						||
| 
								 | 
							
								        ctx.key = null;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(ctx.key === null) {
							 | 
						||
| 
								 | 
							
								        // prevent stack overflow
							 | 
						||
| 
								 | 
							
								        return forge.util.nextTick(function() {
							 | 
						||
| 
								 | 
							
								          _reseed(generate);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate the random bytes
							 | 
						||
| 
								 | 
							
								      var bytes = cipher(ctx.key, ctx.seed);
							 | 
						||
| 
								 | 
							
								      ctx.generated += bytes.length;
							 | 
						||
| 
								 | 
							
								      b.putBytes(bytes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate bytes for a new key and seed
							 | 
						||
| 
								 | 
							
								      ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
							 | 
						||
| 
								 | 
							
								      ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      forge.util.setImmediate(generate);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Generates random bytes synchronously.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param count the number of random bytes to generate.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return count random bytes as a string.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  ctx.generateSync = function(count) {
							 | 
						||
| 
								 | 
							
								    // simple generator using counter-based CBC
							 | 
						||
| 
								 | 
							
								    var cipher = ctx.plugin.cipher;
							 | 
						||
| 
								 | 
							
								    var increment = ctx.plugin.increment;
							 | 
						||
| 
								 | 
							
								    var formatKey = ctx.plugin.formatKey;
							 | 
						||
| 
								 | 
							
								    var formatSeed = ctx.plugin.formatSeed;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // paranoid deviation from Fortuna:
							 | 
						||
| 
								 | 
							
								    // reset key for every request to protect previously
							 | 
						||
| 
								 | 
							
								    // generated random bytes should the key be discovered;
							 | 
						||
| 
								 | 
							
								    // there is no 100ms based reseeding because of this
							 | 
						||
| 
								 | 
							
								    // forced reseed for every `generateSync` call
							 | 
						||
| 
								 | 
							
								    ctx.key = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var b = forge.util.createBuffer();
							 | 
						||
| 
								 | 
							
								    while(b.length() < count) {
							 | 
						||
| 
								 | 
							
								      // if amount of data generated is greater than 1 MiB, trigger reseed
							 | 
						||
| 
								 | 
							
								      if(ctx.generated > 0xfffff) {
							 | 
						||
| 
								 | 
							
								        ctx.key = null;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if(ctx.key === null) {
							 | 
						||
| 
								 | 
							
								        _reseedSync();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate the random bytes
							 | 
						||
| 
								 | 
							
								      var bytes = cipher(ctx.key, ctx.seed);
							 | 
						||
| 
								 | 
							
								      ctx.generated += bytes.length;
							 | 
						||
| 
								 | 
							
								      b.putBytes(bytes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // generate bytes for a new key and seed
							 | 
						||
| 
								 | 
							
								      ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
							 | 
						||
| 
								 | 
							
								      ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return b.getBytes(count);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Private function that asynchronously reseeds a generator.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param callback(err) called once the operation completes.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function _reseed(callback) {
							 | 
						||
| 
								 | 
							
								    if(ctx.pools[0].messageLength >= 32) {
							 | 
						||
| 
								 | 
							
								      _seed();
							 | 
						||
| 
								 | 
							
								      return callback();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // not enough seed data...
							 | 
						||
| 
								 | 
							
								    var needed = (32 - ctx.pools[0].messageLength) << 5;
							 | 
						||
| 
								 | 
							
								    ctx.seedFile(needed, function(err, bytes) {
							 | 
						||
| 
								 | 
							
								      if(err) {
							 | 
						||
| 
								 | 
							
								        return callback(err);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      ctx.collect(bytes);
							 | 
						||
| 
								 | 
							
								      _seed();
							 | 
						||
| 
								 | 
							
								      callback();
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Private function that synchronously reseeds a generator.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function _reseedSync() {
							 | 
						||
| 
								 | 
							
								    if(ctx.pools[0].messageLength >= 32) {
							 | 
						||
| 
								 | 
							
								      return _seed();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    // not enough seed data...
							 | 
						||
| 
								 | 
							
								    var needed = (32 - ctx.pools[0].messageLength) << 5;
							 | 
						||
| 
								 | 
							
								    ctx.collect(ctx.seedFileSync(needed));
							 | 
						||
| 
								 | 
							
								    _seed();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Private function that seeds a generator once enough bytes are available.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function _seed() {
							 | 
						||
| 
								 | 
							
								    // update reseed count
							 | 
						||
| 
								 | 
							
								    ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // goal is to update `key` via:
							 | 
						||
| 
								 | 
							
								    // key = hash(key + s)
							 | 
						||
| 
								 | 
							
								    //   where 's' is all collected entropy from selected pools, then...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // create a plugin-based message digest
							 | 
						||
| 
								 | 
							
								    var md = ctx.plugin.md.create();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // consume current key bytes
							 | 
						||
| 
								 | 
							
								    md.update(ctx.keyBytes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // digest the entropy of pools whose index k meet the
							 | 
						||
| 
								 | 
							
								    // condition 'n mod 2^k == 0' where n is the number of reseeds
							 | 
						||
| 
								 | 
							
								    var _2powK = 1;
							 | 
						||
| 
								 | 
							
								    for(var k = 0; k < 32; ++k) {
							 | 
						||
| 
								 | 
							
								      if(ctx.reseeds % _2powK === 0) {
							 | 
						||
| 
								 | 
							
								        md.update(ctx.pools[k].digest().getBytes());
							 | 
						||
| 
								 | 
							
								        ctx.pools[k].start();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      _2powK = _2powK << 1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // get digest for key bytes
							 | 
						||
| 
								 | 
							
								    ctx.keyBytes = md.digest().getBytes();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // paranoid deviation from Fortuna:
							 | 
						||
| 
								 | 
							
								    // update `seed` via `seed = hash(key)`
							 | 
						||
| 
								 | 
							
								    // instead of initializing to zero once and only
							 | 
						||
| 
								 | 
							
								    // ever incrementing it
							 | 
						||
| 
								 | 
							
								    md.start();
							 | 
						||
| 
								 | 
							
								    md.update(ctx.keyBytes);
							 | 
						||
| 
								 | 
							
								    var seedBytes = md.digest().getBytes();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // update state
							 | 
						||
| 
								 | 
							
								    ctx.key = ctx.plugin.formatKey(ctx.keyBytes);
							 | 
						||
| 
								 | 
							
								    ctx.seed = ctx.plugin.formatSeed(seedBytes);
							 | 
						||
| 
								 | 
							
								    ctx.generated = 0;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * The built-in default seedFile. This seedFile is used when entropy
							 | 
						||
| 
								 | 
							
								   * is needed immediately.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param needed the number of bytes that are needed.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return the random bytes.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  function defaultSeedFile(needed) {
							 | 
						||
| 
								 | 
							
								    // use window.crypto.getRandomValues strong source of entropy if available
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var b = forge.util.createBuffer();
							 | 
						||
| 
								 | 
							
								    if(getRandomValues) {
							 | 
						||
| 
								 | 
							
								      while(b.length() < needed) {
							 | 
						||
| 
								 | 
							
								        // max byte length is 65536 before QuotaExceededError is thrown
							 | 
						||
| 
								 | 
							
								        // http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues
							 | 
						||
| 
								 | 
							
								        var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4);
							 | 
						||
| 
								 | 
							
								        var entropy = new Uint32Array(Math.floor(count));
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          getRandomValues(entropy);
							 | 
						||
| 
								 | 
							
								          for(var i = 0; i < entropy.length; ++i) {
							 | 
						||
| 
								 | 
							
								            b.putInt32(entropy[i]);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        } catch(e) {
							 | 
						||
| 
								 | 
							
								          /* only ignore QuotaExceededError */
							 | 
						||
| 
								 | 
							
								          if(!(typeof QuotaExceededError !== 'undefined' &&
							 | 
						||
| 
								 | 
							
								            e instanceof QuotaExceededError)) {
							 | 
						||
| 
								 | 
							
								            throw e;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // be sad and add some weak random data
							 | 
						||
| 
								 | 
							
								    if(b.length() < needed) {
							 | 
						||
| 
								 | 
							
								      /* Draws from Park-Miller "minimal standard" 31 bit PRNG,
							 | 
						||
| 
								 | 
							
								      implemented with David G. Carta's optimization: with 32 bit math
							 | 
						||
| 
								 | 
							
								      and without division (Public Domain). */
							 | 
						||
| 
								 | 
							
								      var hi, lo, next;
							 | 
						||
| 
								 | 
							
								      var seed = Math.floor(Math.random() * 0x010000);
							 | 
						||
| 
								 | 
							
								      while(b.length() < needed) {
							 | 
						||
| 
								 | 
							
								        lo = 16807 * (seed & 0xFFFF);
							 | 
						||
| 
								 | 
							
								        hi = 16807 * (seed >> 16);
							 | 
						||
| 
								 | 
							
								        lo += (hi & 0x7FFF) << 16;
							 | 
						||
| 
								 | 
							
								        lo += hi >> 15;
							 | 
						||
| 
								 | 
							
								        lo = (lo & 0x7FFFFFFF) + (lo >> 31);
							 | 
						||
| 
								 | 
							
								        seed = lo & 0xFFFFFFFF;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // consume lower 3 bytes of seed
							 | 
						||
| 
								 | 
							
								        for(var i = 0; i < 3; ++i) {
							 | 
						||
| 
								 | 
							
								          // throw in more pseudo random
							 | 
						||
| 
								 | 
							
								          next = seed >>> (i << 3);
							 | 
						||
| 
								 | 
							
								          next ^= Math.floor(Math.random() * 0x0100);
							 | 
						||
| 
								 | 
							
								          b.putByte(next & 0xFF);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return b.getBytes(needed);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  // initialize seed file APIs
							 | 
						||
| 
								 | 
							
								  if(_crypto) {
							 | 
						||
| 
								 | 
							
								    // use nodejs async API
							 | 
						||
| 
								 | 
							
								    ctx.seedFile = function(needed, callback) {
							 | 
						||
| 
								 | 
							
								      _crypto.randomBytes(needed, function(err, bytes) {
							 | 
						||
| 
								 | 
							
								        if(err) {
							 | 
						||
| 
								 | 
							
								          return callback(err);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        callback(null, bytes.toString());
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    // use nodejs sync API
							 | 
						||
| 
								 | 
							
								    ctx.seedFileSync = function(needed) {
							 | 
						||
| 
								 | 
							
								      return _crypto.randomBytes(needed).toString();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    ctx.seedFile = function(needed, callback) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        callback(null, defaultSeedFile(needed));
							 | 
						||
| 
								 | 
							
								      } catch(e) {
							 | 
						||
| 
								 | 
							
								        callback(e);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    ctx.seedFileSync = defaultSeedFile;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Adds entropy to a prng ctx's accumulator.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param bytes the bytes of entropy as a string.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  ctx.collect = function(bytes) {
							 | 
						||
| 
								 | 
							
								    // iterate over pools distributing entropy cyclically
							 | 
						||
| 
								 | 
							
								    var count = bytes.length;
							 | 
						||
| 
								 | 
							
								    for(var i = 0; i < count; ++i) {
							 | 
						||
| 
								 | 
							
								      ctx.pools[ctx.pool].update(bytes.substr(i, 1));
							 | 
						||
| 
								 | 
							
								      ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Collects an integer of n bits.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param i the integer entropy.
							 | 
						||
| 
								 | 
							
								   * @param n the number of bits in the integer.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  ctx.collectInt = function(i, n) {
							 | 
						||
| 
								 | 
							
								    var bytes = '';
							 | 
						||
| 
								 | 
							
								    for(var x = 0; x < n; x += 8) {
							 | 
						||
| 
								 | 
							
								      bytes += String.fromCharCode((i >> x) & 0xFF);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    ctx.collect(bytes);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Registers a Web Worker to receive immediate entropy from the main thread.
							 | 
						||
| 
								 | 
							
								   * This method is required until Web Workers can access the native crypto
							 | 
						||
| 
								 | 
							
								   * API. This method should be called twice for each created worker, once in
							 | 
						||
| 
								 | 
							
								   * the main thread, and once in the worker itself.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param worker the worker to register.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  ctx.registerWorker = function(worker) {
							 | 
						||
| 
								 | 
							
								    // worker receives random bytes
							 | 
						||
| 
								 | 
							
								    if(worker === self) {
							 | 
						||
| 
								 | 
							
								      ctx.seedFile = function(needed, callback) {
							 | 
						||
| 
								 | 
							
								        function listener(e) {
							 | 
						||
| 
								 | 
							
								          var data = e.data;
							 | 
						||
| 
								 | 
							
								          if(data.forge && data.forge.prng) {
							 | 
						||
| 
								 | 
							
								            self.removeEventListener('message', listener);
							 | 
						||
| 
								 | 
							
								            callback(data.forge.prng.err, data.forge.prng.bytes);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        self.addEventListener('message', listener);
							 | 
						||
| 
								 | 
							
								        self.postMessage({forge: {prng: {needed: needed}}});
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      // main thread sends random bytes upon request
							 | 
						||
| 
								 | 
							
								      var listener = function(e) {
							 | 
						||
| 
								 | 
							
								        var data = e.data;
							 | 
						||
| 
								 | 
							
								        if(data.forge && data.forge.prng) {
							 | 
						||
| 
								 | 
							
								          ctx.seedFile(data.forge.prng.needed, function(err, bytes) {
							 | 
						||
| 
								 | 
							
								            worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});
							 | 
						||
| 
								 | 
							
								          });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								      // TODO: do we need to remove the event listener when the worker dies?
							 | 
						||
| 
								 | 
							
								      worker.addEventListener('message', listener);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return ctx;
							 | 
						||
| 
								 | 
							
								};
							 |