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.

191 lines
5.3 KiB

4 years ago
  1. /**
  2. * An API for getting cryptographically-secure random bytes. The bytes are
  3. * generated using the Fortuna algorithm devised by Bruce Schneier and
  4. * Niels Ferguson.
  5. *
  6. * Getting strong random bytes is not yet easy to do in javascript. The only
  7. * truish random entropy that can be collected is from the mouse, keyboard, or
  8. * from timing with respect to page loads, etc. This generator makes a poor
  9. * attempt at providing random bytes when those sources haven't yet provided
  10. * enough entropy to initially seed or to reseed the PRNG.
  11. *
  12. * @author Dave Longley
  13. *
  14. * Copyright (c) 2009-2014 Digital Bazaar, Inc.
  15. */
  16. var forge = require('./forge');
  17. require('./aes');
  18. require('./sha256');
  19. require('./prng');
  20. require('./util');
  21. (function() {
  22. // forge.random already defined
  23. if(forge.random && forge.random.getBytes) {
  24. module.exports = forge.random;
  25. return;
  26. }
  27. (function(jQuery) {
  28. // the default prng plugin, uses AES-128
  29. var prng_aes = {};
  30. var _prng_aes_output = new Array(4);
  31. var _prng_aes_buffer = forge.util.createBuffer();
  32. prng_aes.formatKey = function(key) {
  33. // convert the key into 32-bit integers
  34. var tmp = forge.util.createBuffer(key);
  35. key = new Array(4);
  36. key[0] = tmp.getInt32();
  37. key[1] = tmp.getInt32();
  38. key[2] = tmp.getInt32();
  39. key[3] = tmp.getInt32();
  40. // return the expanded key
  41. return forge.aes._expandKey(key, false);
  42. };
  43. prng_aes.formatSeed = function(seed) {
  44. // convert seed into 32-bit integers
  45. var tmp = forge.util.createBuffer(seed);
  46. seed = new Array(4);
  47. seed[0] = tmp.getInt32();
  48. seed[1] = tmp.getInt32();
  49. seed[2] = tmp.getInt32();
  50. seed[3] = tmp.getInt32();
  51. return seed;
  52. };
  53. prng_aes.cipher = function(key, seed) {
  54. forge.aes._updateBlock(key, seed, _prng_aes_output, false);
  55. _prng_aes_buffer.putInt32(_prng_aes_output[0]);
  56. _prng_aes_buffer.putInt32(_prng_aes_output[1]);
  57. _prng_aes_buffer.putInt32(_prng_aes_output[2]);
  58. _prng_aes_buffer.putInt32(_prng_aes_output[3]);
  59. return _prng_aes_buffer.getBytes();
  60. };
  61. prng_aes.increment = function(seed) {
  62. // FIXME: do we care about carry or signed issues?
  63. ++seed[3];
  64. return seed;
  65. };
  66. prng_aes.md = forge.md.sha256;
  67. /**
  68. * Creates a new PRNG.
  69. */
  70. function spawnPrng() {
  71. var ctx = forge.prng.create(prng_aes);
  72. /**
  73. * Gets random bytes. If a native secure crypto API is unavailable, this
  74. * method tries to make the bytes more unpredictable by drawing from data that
  75. * can be collected from the user of the browser, eg: mouse movement.
  76. *
  77. * If a callback is given, this method will be called asynchronously.
  78. *
  79. * @param count the number of random bytes to get.
  80. * @param [callback(err, bytes)] called once the operation completes.
  81. *
  82. * @return the random bytes in a string.
  83. */
  84. ctx.getBytes = function(count, callback) {
  85. return ctx.generate(count, callback);
  86. };
  87. /**
  88. * Gets random bytes asynchronously. If a native secure crypto API is
  89. * unavailable, this method tries to make the bytes more unpredictable by
  90. * drawing from data that can be collected from the user of the browser,
  91. * eg: mouse movement.
  92. *
  93. * @param count the number of random bytes to get.
  94. *
  95. * @return the random bytes in a string.
  96. */
  97. ctx.getBytesSync = function(count) {
  98. return ctx.generate(count);
  99. };
  100. return ctx;
  101. }
  102. // create default prng context
  103. var _ctx = spawnPrng();
  104. // add other sources of entropy only if window.crypto.getRandomValues is not
  105. // available -- otherwise this source will be automatically used by the prng
  106. var getRandomValues = null;
  107. var globalScope = forge.util.globalScope;
  108. var _crypto = globalScope.crypto || globalScope.msCrypto;
  109. if(_crypto && _crypto.getRandomValues) {
  110. getRandomValues = function(arr) {
  111. return _crypto.getRandomValues(arr);
  112. };
  113. }
  114. if(forge.options.usePureJavaScript ||
  115. (!forge.util.isNodejs && !getRandomValues)) {
  116. // if this is a web worker, do not use weak entropy, instead register to
  117. // receive strong entropy asynchronously from the main thread
  118. if(typeof window === 'undefined' || window.document === undefined) {
  119. // FIXME:
  120. }
  121. // get load time entropy
  122. _ctx.collectInt(+new Date(), 32);
  123. // add some entropy from navigator object
  124. if(typeof(navigator) !== 'undefined') {
  125. var _navBytes = '';
  126. for(var key in navigator) {
  127. try {
  128. if(typeof(navigator[key]) == 'string') {
  129. _navBytes += navigator[key];
  130. }
  131. } catch(e) {
  132. /* Some navigator keys might not be accessible, e.g. the geolocation
  133. attribute throws an exception if touched in Mozilla chrome://
  134. context.
  135. Silently ignore this and just don't use this as a source of
  136. entropy. */
  137. }
  138. }
  139. _ctx.collect(_navBytes);
  140. _navBytes = null;
  141. }
  142. // add mouse and keyboard collectors if jquery is available
  143. if(jQuery) {
  144. // set up mouse entropy capture
  145. jQuery().mousemove(function(e) {
  146. // add mouse coords
  147. _ctx.collectInt(e.clientX, 16);
  148. _ctx.collectInt(e.clientY, 16);
  149. });
  150. // set up keyboard entropy capture
  151. jQuery().keypress(function(e) {
  152. _ctx.collectInt(e.charCode, 8);
  153. });
  154. }
  155. }
  156. /* Random API */
  157. if(!forge.random) {
  158. forge.random = _ctx;
  159. } else {
  160. // extend forge.random with _ctx
  161. for(var key in _ctx) {
  162. forge.random[key] = _ctx[key];
  163. }
  164. }
  165. // expose spawn PRNG
  166. forge.random.createInstance = spawnPrng;
  167. module.exports = forge.random;
  168. })(typeof(jQuery) !== 'undefined' ? jQuery : null);
  169. })();