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.

297 lines
8.6 KiB

4 years ago
  1. /**
  2. * Prime number generation API.
  3. *
  4. * @author Dave Longley
  5. *
  6. * Copyright (c) 2014 Digital Bazaar, Inc.
  7. */
  8. var forge = require('./forge');
  9. require('./util');
  10. require('./jsbn');
  11. require('./random');
  12. (function() {
  13. // forge.prime already defined
  14. if(forge.prime) {
  15. module.exports = forge.prime;
  16. return;
  17. }
  18. /* PRIME API */
  19. var prime = module.exports = forge.prime = forge.prime || {};
  20. var BigInteger = forge.jsbn.BigInteger;
  21. // primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29
  22. var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];
  23. var THIRTY = new BigInteger(null);
  24. THIRTY.fromInt(30);
  25. var op_or = function(x, y) {return x|y;};
  26. /**
  27. * Generates a random probable prime with the given number of bits.
  28. *
  29. * Alternative algorithms can be specified by name as a string or as an
  30. * object with custom options like so:
  31. *
  32. * {
  33. * name: 'PRIMEINC',
  34. * options: {
  35. * maxBlockTime: <the maximum amount of time to block the main
  36. * thread before allowing I/O other JS to run>,
  37. * millerRabinTests: <the number of miller-rabin tests to run>,
  38. * workerScript: <the worker script URL>,
  39. * workers: <the number of web workers (if supported) to use,
  40. * -1 to use estimated cores minus one>.
  41. * workLoad: the size of the work load, ie: number of possible prime
  42. * numbers for each web worker to check per work assignment,
  43. * (default: 100).
  44. * }
  45. * }
  46. *
  47. * @param bits the number of bits for the prime number.
  48. * @param options the options to use.
  49. * [algorithm] the algorithm to use (default: 'PRIMEINC').
  50. * [prng] a custom crypto-secure pseudo-random number generator to use,
  51. * that must define "getBytesSync".
  52. *
  53. * @return callback(err, num) called once the operation completes.
  54. */
  55. prime.generateProbablePrime = function(bits, options, callback) {
  56. if(typeof options === 'function') {
  57. callback = options;
  58. options = {};
  59. }
  60. options = options || {};
  61. // default to PRIMEINC algorithm
  62. var algorithm = options.algorithm || 'PRIMEINC';
  63. if(typeof algorithm === 'string') {
  64. algorithm = {name: algorithm};
  65. }
  66. algorithm.options = algorithm.options || {};
  67. // create prng with api that matches BigInteger secure random
  68. var prng = options.prng || forge.random;
  69. var rng = {
  70. // x is an array to fill with bytes
  71. nextBytes: function(x) {
  72. var b = prng.getBytesSync(x.length);
  73. for(var i = 0; i < x.length; ++i) {
  74. x[i] = b.charCodeAt(i);
  75. }
  76. }
  77. };
  78. if(algorithm.name === 'PRIMEINC') {
  79. return primeincFindPrime(bits, rng, algorithm.options, callback);
  80. }
  81. throw new Error('Invalid prime generation algorithm: ' + algorithm.name);
  82. };
  83. function primeincFindPrime(bits, rng, options, callback) {
  84. if('workers' in options) {
  85. return primeincFindPrimeWithWorkers(bits, rng, options, callback);
  86. }
  87. return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
  88. }
  89. function primeincFindPrimeWithoutWorkers(bits, rng, options, callback) {
  90. // initialize random number
  91. var num = generateRandom(bits, rng);
  92. /* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The
  93. number we are given is always aligned at 30k + 1. Each time the number is
  94. determined not to be prime we add to get to the next 'i', eg: if the number
  95. was at 30k + 1 we add 6. */
  96. var deltaIdx = 0;
  97. // get required number of MR tests
  98. var mrTests = getMillerRabinTests(num.bitLength());
  99. if('millerRabinTests' in options) {
  100. mrTests = options.millerRabinTests;
  101. }
  102. // find prime nearest to 'num' for maxBlockTime ms
  103. // 10 ms gives 5ms of leeway for other calculations before dropping
  104. // below 60fps (1000/60 == 16.67), but in reality, the number will
  105. // likely be higher due to an 'atomic' big int modPow
  106. var maxBlockTime = 10;
  107. if('maxBlockTime' in options) {
  108. maxBlockTime = options.maxBlockTime;
  109. }
  110. _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);
  111. }
  112. function _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback) {
  113. var start = +new Date();
  114. do {
  115. // overflow, regenerate random number
  116. if(num.bitLength() > bits) {
  117. num = generateRandom(bits, rng);
  118. }
  119. // do primality test
  120. if(num.isProbablePrime(mrTests)) {
  121. return callback(null, num);
  122. }
  123. // get next potential prime
  124. num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);
  125. } while(maxBlockTime < 0 || (+new Date() - start < maxBlockTime));
  126. // keep trying later
  127. forge.util.setImmediate(function() {
  128. _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);
  129. });
  130. }
  131. // NOTE: This algorithm is indeterminate in nature because workers
  132. // run in parallel looking at different segments of numbers. Even if this
  133. // algorithm is run twice with the same input from a predictable RNG, it
  134. // may produce different outputs.
  135. function primeincFindPrimeWithWorkers(bits, rng, options, callback) {
  136. // web workers unavailable
  137. if(typeof Worker === 'undefined') {
  138. return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);
  139. }
  140. // initialize random number
  141. var num = generateRandom(bits, rng);
  142. // use web workers to generate keys
  143. var numWorkers = options.workers;
  144. var workLoad = options.workLoad || 100;
  145. var range = workLoad * 30 / 8;
  146. var workerScript = options.workerScript || 'forge/prime.worker.js';
  147. if(numWorkers === -1) {
  148. return forge.util.estimateCores(function(err, cores) {
  149. if(err) {
  150. // default to 2
  151. cores = 2;
  152. }
  153. numWorkers = cores - 1;
  154. generate();
  155. });
  156. }
  157. generate();
  158. function generate() {
  159. // require at least 1 worker
  160. numWorkers = Math.max(1, numWorkers);
  161. // TODO: consider optimizing by starting workers outside getPrime() ...
  162. // note that in order to clean up they will have to be made internally
  163. // asynchronous which may actually be slower
  164. // start workers immediately
  165. var workers = [];
  166. for(var i = 0; i < numWorkers; ++i) {
  167. // FIXME: fix path or use blob URLs
  168. workers[i] = new Worker(workerScript);
  169. }
  170. var running = numWorkers;
  171. // listen for requests from workers and assign ranges to find prime
  172. for(var i = 0; i < numWorkers; ++i) {
  173. workers[i].addEventListener('message', workerMessage);
  174. }
  175. /* Note: The distribution of random numbers is unknown. Therefore, each
  176. web worker is continuously allocated a range of numbers to check for a
  177. random number until one is found.
  178. Every 30 numbers will be checked just 8 times, because prime numbers
  179. have the form:
  180. 30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)
  181. Therefore, if we want a web worker to run N checks before asking for
  182. a new range of numbers, each range must contain N*30/8 numbers.
  183. For 100 checks (workLoad), this is a range of 375. */
  184. var found = false;
  185. function workerMessage(e) {
  186. // ignore message, prime already found
  187. if(found) {
  188. return;
  189. }
  190. --running;
  191. var data = e.data;
  192. if(data.found) {
  193. // terminate all workers
  194. for(var i = 0; i < workers.length; ++i) {
  195. workers[i].terminate();
  196. }
  197. found = true;
  198. return callback(null, new BigInteger(data.prime, 16));
  199. }
  200. // overflow, regenerate random number
  201. if(num.bitLength() > bits) {
  202. num = generateRandom(bits, rng);
  203. }
  204. // assign new range to check
  205. var hex = num.toString(16);
  206. // start prime search
  207. e.target.postMessage({
  208. hex: hex,
  209. workLoad: workLoad
  210. });
  211. num.dAddOffset(range, 0);
  212. }
  213. }
  214. }
  215. /**
  216. * Generates a random number using the given number of bits and RNG.
  217. *
  218. * @param bits the number of bits for the number.
  219. * @param rng the random number generator to use.
  220. *
  221. * @return the random number.
  222. */
  223. function generateRandom(bits, rng) {
  224. var num = new BigInteger(bits, rng);
  225. // force MSB set
  226. var bits1 = bits - 1;
  227. if(!num.testBit(bits1)) {
  228. num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);
  229. }
  230. // align number on 30k+1 boundary
  231. num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);
  232. return num;
  233. }
  234. /**
  235. * Returns the required number of Miller-Rabin tests to generate a
  236. * prime with an error probability of (1/2)^80.
  237. *
  238. * See Handbook of Applied Cryptography Chapter 4, Table 4.4.
  239. *
  240. * @param bits the bit size.
  241. *
  242. * @return the required number of iterations.
  243. */
  244. function getMillerRabinTests(bits) {
  245. if(bits <= 100) return 27;
  246. if(bits <= 150) return 18;
  247. if(bits <= 200) return 15;
  248. if(bits <= 250) return 12;
  249. if(bits <= 300) return 9;
  250. if(bits <= 350) return 8;
  251. if(bits <= 400) return 7;
  252. if(bits <= 500) return 6;
  253. if(bits <= 600) return 5;
  254. if(bits <= 800) return 4;
  255. if(bits <= 1250) return 3;
  256. return 2;
  257. }
  258. })();