| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 | /** * Prime number generation API. * * @author Dave Longley * * Copyright (c) 2014 Digital Bazaar, Inc. */var forge = require('./forge');require('./util');require('./jsbn');require('./random');(function() {// forge.prime already definedif(forge.prime) {  module.exports = forge.prime;  return;}/* PRIME API */var prime = module.exports = forge.prime = forge.prime || {};var BigInteger = forge.jsbn.BigInteger;// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];var THIRTY = new BigInteger(null);THIRTY.fromInt(30);var op_or = function(x, y) {return x|y;};/** * Generates a random probable prime with the given number of bits. * * Alternative algorithms can be specified by name as a string or as an * object with custom options like so: * * { *   name: 'PRIMEINC', *   options: { *     maxBlockTime: <the maximum amount of time to block the main *       thread before allowing I/O other JS to run>, *     millerRabinTests: <the number of miller-rabin tests to run>, *     workerScript: <the worker script URL>, *     workers: <the number of web workers (if supported) to use, *       -1 to use estimated cores minus one>. *     workLoad: the size of the work load, ie: number of possible prime *       numbers for each web worker to check per work assignment, *       (default: 100). *   } * } * * @param bits the number of bits for the prime number. * @param options the options to use. *          [algorithm] the algorithm to use (default: 'PRIMEINC'). *          [prng] a custom crypto-secure pseudo-random number generator to use, *            that must define "getBytesSync". * * @return callback(err, num) called once the operation completes. */prime.generateProbablePrime = function(bits, options, callback) {  if(typeof options === 'function') {    callback = options;    options = {};  }  options = options || {};  // default to PRIMEINC algorithm  var algorithm = options.algorithm || 'PRIMEINC';  if(typeof algorithm === 'string') {    algorithm = {name: algorithm};  }  algorithm.options = algorithm.options || {};  // create prng with api that matches BigInteger secure random  var prng = options.prng || forge.random;  var rng = {    // x is an array to fill with bytes    nextBytes: function(x) {      var b = prng.getBytesSync(x.length);      for(var i = 0; i < x.length; ++i) {        x[i] = b.charCodeAt(i);      }    }  };  if(algorithm.name === 'PRIMEINC') {    return primeincFindPrime(bits, rng, algorithm.options, callback);  }  throw new Error('Invalid prime generation algorithm: ' + algorithm.name);};function primeincFindPrime(bits, rng, options, callback) {  if('workers' in options) {    return primeincFindPrimeWithWorkers(bits, rng, options, callback);  }  return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);}function primeincFindPrimeWithoutWorkers(bits, rng, options, callback) {  // initialize random number  var num = generateRandom(bits, rng);  /* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The  number we are given is always aligned at 30k + 1. Each time the number is  determined not to be prime we add to get to the next 'i', eg: if the number  was at 30k + 1 we add 6. */  var deltaIdx = 0;  // get required number of MR tests  var mrTests = getMillerRabinTests(num.bitLength());  if('millerRabinTests' in options) {    mrTests = options.millerRabinTests;  }  // find prime nearest to 'num' for maxBlockTime ms  // 10 ms gives 5ms of leeway for other calculations before dropping  // below 60fps (1000/60 == 16.67), but in reality, the number will  // likely be higher due to an 'atomic' big int modPow  var maxBlockTime = 10;  if('maxBlockTime' in options) {    maxBlockTime = options.maxBlockTime;  }  _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);}function _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback) {  var start = +new Date();  do {    // overflow, regenerate random number    if(num.bitLength() > bits) {      num = generateRandom(bits, rng);    }    // do primality test    if(num.isProbablePrime(mrTests)) {      return callback(null, num);    }    // get next potential prime    num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);  } while(maxBlockTime < 0 || (+new Date() - start < maxBlockTime));  // keep trying later  forge.util.setImmediate(function() {    _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);  });}// NOTE: This algorithm is indeterminate in nature because workers// run in parallel looking at different segments of numbers. Even if this// algorithm is run twice with the same input from a predictable RNG, it// may produce different outputs.function primeincFindPrimeWithWorkers(bits, rng, options, callback) {  // web workers unavailable  if(typeof Worker === 'undefined') {    return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);  }  // initialize random number  var num = generateRandom(bits, rng);  // use web workers to generate keys  var numWorkers = options.workers;  var workLoad = options.workLoad || 100;  var range = workLoad * 30 / 8;  var workerScript = options.workerScript || 'forge/prime.worker.js';  if(numWorkers === -1) {    return forge.util.estimateCores(function(err, cores) {      if(err) {        // default to 2        cores = 2;      }      numWorkers = cores - 1;      generate();    });  }  generate();  function generate() {    // require at least 1 worker    numWorkers = Math.max(1, numWorkers);    // TODO: consider optimizing by starting workers outside getPrime() ...    // note that in order to clean up they will have to be made internally    // asynchronous which may actually be slower    // start workers immediately    var workers = [];    for(var i = 0; i < numWorkers; ++i) {      // FIXME: fix path or use blob URLs      workers[i] = new Worker(workerScript);    }    var running = numWorkers;    // listen for requests from workers and assign ranges to find prime    for(var i = 0; i < numWorkers; ++i) {      workers[i].addEventListener('message', workerMessage);    }    /* Note: The distribution of random numbers is unknown. Therefore, each    web worker is continuously allocated a range of numbers to check for a    random number until one is found.    Every 30 numbers will be checked just 8 times, because prime numbers    have the form:    30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)    Therefore, if we want a web worker to run N checks before asking for    a new range of numbers, each range must contain N*30/8 numbers.    For 100 checks (workLoad), this is a range of 375. */    var found = false;    function workerMessage(e) {      // ignore message, prime already found      if(found) {        return;      }      --running;      var data = e.data;      if(data.found) {        // terminate all workers        for(var i = 0; i < workers.length; ++i) {          workers[i].terminate();        }        found = true;        return callback(null, new BigInteger(data.prime, 16));      }      // overflow, regenerate random number      if(num.bitLength() > bits) {        num = generateRandom(bits, rng);      }      // assign new range to check      var hex = num.toString(16);      // start prime search      e.target.postMessage({        hex: hex,        workLoad: workLoad      });      num.dAddOffset(range, 0);    }  }}/** * Generates a random number using the given number of bits and RNG. * * @param bits the number of bits for the number. * @param rng the random number generator to use. * * @return the random number. */function generateRandom(bits, rng) {  var num = new BigInteger(bits, rng);  // force MSB set  var bits1 = bits - 1;  if(!num.testBit(bits1)) {    num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);  }  // align number on 30k+1 boundary  num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);  return num;}/** * Returns the required number of Miller-Rabin tests to generate a * prime with an error probability of (1/2)^80. * * See Handbook of Applied Cryptography Chapter 4, Table 4.4. * * @param bits the bit size. * * @return the required number of iterations. */function getMillerRabinTests(bits) {  if(bits <= 100) return 27;  if(bits <= 150) return 18;  if(bits <= 200) return 15;  if(bits <= 250) return 12;  if(bits <= 300) return 9;  if(bits <= 350) return 8;  if(bits <= 400) return 7;  if(bits <= 500) return 6;  if(bits <= 600) return 5;  if(bits <= 800) return 4;  if(bits <= 1250) return 3;  return 2;}})();
 |