cryptoprng.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. /*
  2. * Copyright 2018 Scytl Secure Electronic Voting SA
  3. *
  4. * All rights reserved
  5. *
  6. * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
  7. */
  8. var cryptoPRNG = (function() {
  9. 'use strict';
  10. return {
  11. _collectorsStarted: false,
  12. _collectors: [],
  13. _events: [],
  14. _entropy: '',
  15. _entropyCounter: 0,
  16. _maxEntropyCounter: 0,
  17. _collectorHashUpdater: null,
  18. _hashUpdaterInterval: 0,
  19. _fullEntropyCallback: null,
  20. _tools: null,
  21. _seedLength: 0,
  22. _prng: null,
  23. _privateKeyPem: '',
  24. _pinEntropy: 0,
  25. _usePrivateKeyEntropy: false,
  26. /**
  27. * Initializes all the round object fields.
  28. *
  29. * @function
  30. * @param hashUpdaterInterval
  31. * the interval, in millis, that the function that replaces the
  32. * current entropy content by an hash is called. By default it is
  33. * initialized to 5000.
  34. * @param maxEntropyCounter
  35. * number of maximum bits used to determine when the collectors
  36. * should stop collecting entropy. By default it is initialized
  37. * to 256.
  38. */
  39. _initAndStart: function(hashUpdaterInterval, fullEntropyCallback) {
  40. this._collectorsStarted = false;
  41. this._collectors = [];
  42. this._events = [];
  43. this._entropy = '';
  44. this._entropyCounter = 0;
  45. this._maxEntropyCounter = 256;
  46. this._collectorHashUpdater = null;
  47. this._hashUpdaterInterval =
  48. (hashUpdaterInterval ? hashUpdaterInterval : 1000);
  49. this._fullEntropyCallback = fullEntropyCallback;
  50. this._tools = cryptoPRNG.Tools.init();
  51. this._initCollectors();
  52. this._startCollectors();
  53. },
  54. /**
  55. * adds a new collector to the list of available connectors
  56. *
  57. * @param collector
  58. * must return an object that implements the startCollector and
  59. * stopCollector methods
  60. */
  61. _addCollector: function(collector) {
  62. this._collectors.push(collector);
  63. },
  64. /**
  65. * adds a new event to the list of available events
  66. *
  67. * @param type
  68. * event type
  69. * @param listener
  70. * function that will be executed
  71. */
  72. _addEvent: function(type, listener) {
  73. this._events.push({'type': type, 'listener': listener});
  74. },
  75. /**
  76. * Initializes the random generator with the web kit random collector if
  77. * available.
  78. *
  79. * If it is not, it considers the rest of collectors: ajax calls collector,
  80. * JS calls execution collector, navigator information collector, math
  81. * random collector, web kit random collector, mouse events collectors, key
  82. * events collectors, load event collector, scroll collector)
  83. */
  84. _initCollectors: function() {
  85. if (this._noWindow()) {
  86. this._addNonWindowEntropyCollectors();
  87. return;
  88. }
  89. var _crypto = this._getCrypto();
  90. if ((_crypto) && (_crypto.getRandomValues)) {
  91. this._addCollector(cryptoPRNG.Collectors.getWebKitRandomCollector(
  92. _crypto, this._maxEntropyCounter));
  93. } else {
  94. this._addNonWindowEntropyCollectors();
  95. // The API detects and delivers accelerometer data 50 times per
  96. // second
  97. if (window.DeviceOrientationEvent) {
  98. // Listen for the device orientation event and handle
  99. // DeviceOrientationEvent object
  100. this._addEvent(
  101. 'deviceorientation',
  102. cryptoPRNG.Collectors.getDeviceOrientationCollector);
  103. } else if (window.OrientationEvent) {
  104. // Listen for the MozOrientation event and handle
  105. // OrientationData object
  106. this._addEvent(
  107. 'MozOrientation',
  108. cryptoPRNG.Collectors.getDeviceOrientationCollector);
  109. }
  110. if (window.DeviceMotionEvent) {
  111. this._addEvent(
  112. 'devicemotion', cryptoPRNG.Collectors.getDeviceMotionCollector);
  113. }
  114. }
  115. },
  116. _addNonWindowEntropyCollectors: function() {
  117. if (this._usePrivateKeyEntropy) {
  118. this._addCollector(cryptoPRNG.Collectors.getPrivateKeyCollector(
  119. this._privateKeyPem, this._pinEntropy));
  120. }
  121. this._addCollector(cryptoPRNG.Collectors.getAjaxCollector());
  122. this._addCollector(cryptoPRNG.Collectors.getJSExecutionCollector());
  123. this._addCollector(cryptoPRNG.Collectors.getNavigatorInfoCollector());
  124. this._addCollector(cryptoPRNG.Collectors.getMathRandomCollector());
  125. // mouse events collectors
  126. this._addEvent('mousemove', cryptoPRNG.Collectors.getMouseMoveCollector);
  127. this._addEvent(
  128. 'mousewheel', cryptoPRNG.Collectors.getMouseWheelCollector);
  129. this._addEvent('mouseup', cryptoPRNG.Collectors.getMouseUpCollector);
  130. this._addEvent('mousedown', cryptoPRNG.Collectors.getMouseDownCollector);
  131. this._addEvent(
  132. 'touchstart', cryptoPRNG.Collectors.getTouchStartCollector);
  133. this._addEvent('touchmove', cryptoPRNG.Collectors.getTouchMoveCollector);
  134. this._addEvent('touchend', cryptoPRNG.Collectors.getTouchEndCollector);
  135. this._addEvent(
  136. 'gesturestart', cryptoPRNG.Collectors.getGestureStartCollector);
  137. this._addEvent(
  138. 'gestureend', cryptoPRNG.Collectors.getGestureEndCollector);
  139. // keyboard events collectors
  140. this._addEvent('keyup', cryptoPRNG.Collectors.getKeyUpCollector);
  141. this._addEvent('keydown', cryptoPRNG.Collectors.getKeyDownCollector);
  142. // page events collectors
  143. this._addEvent('load', cryptoPRNG.Collectors.getLoadCollector);
  144. this._addEvent('scroll', cryptoPRNG.Collectors.getScrollCollector);
  145. // requests collector collectors
  146. this._addEvent('beforeload', cryptoPRNG.Collectors.getRequestsCollector);
  147. },
  148. _getCrypto: function() {
  149. return window.crypto || window.msCrypto;
  150. },
  151. _noWindow: function() {
  152. return (typeof window === 'undefined');
  153. },
  154. /**
  155. * Start the collectors
  156. */
  157. _startCollectors: function() {
  158. if (this._collectorsStarted) {
  159. return;
  160. }
  161. var i = 0;
  162. // start all the collectors
  163. while (i < this._collectors.length) {
  164. try {
  165. this._collectors[i].startCollector();
  166. } catch (e) {
  167. // do not do anything about any exception that is thrown
  168. // by the execution of the collectors
  169. }
  170. i++;
  171. }
  172. i = 0;
  173. // start all the events
  174. while (i < this._events.length) {
  175. try {
  176. if (window.addEventListener) {
  177. window.addEventListener(
  178. this._events[i].type, this._events[i].listener, false);
  179. } else if (document.attachEvent) {
  180. document.attachEvent(
  181. 'on' + this._events[i].type, this._events[i].listener);
  182. }
  183. } catch (e) {
  184. // do not do anything about any exception that is thrown
  185. // by the execution of the events
  186. }
  187. i++;
  188. }
  189. this._tools._mdUpdate.start();
  190. this._collectorHashUpdater = setInterval(function() {
  191. cryptoPRNG._hashUpdater();
  192. }, this._hashUpdaterInterval);
  193. this._collectorsStarted = true;
  194. },
  195. /**
  196. * Stop the collectors
  197. */
  198. _stopCollectors: function() {
  199. if (!this._collectorsStarted) {
  200. return;
  201. }
  202. var i = 0;
  203. while (i < this._collectors.length) {
  204. try {
  205. this._collectors[i].stopCollector();
  206. } catch (e) {
  207. // do not do anything about any exception that is thrown
  208. // by the execution of the collectors
  209. }
  210. i++;
  211. }
  212. i = 0;
  213. while (i < this._events.length) {
  214. try {
  215. if (window.removeEventListener) {
  216. window.removeEventListener(
  217. this._events[i].type, this._events[i].listener, false);
  218. } else if (document.detachEvent) {
  219. document.detachEvent(
  220. 'on' + this._events[i].type, this._events[i].listener);
  221. }
  222. } catch (e) {
  223. // do not do anything about any exception that is thrown
  224. // by the remove of the events
  225. }
  226. i++;
  227. }
  228. if (this._collectorHashUpdater) {
  229. clearInterval(this._collectorHashUpdater);
  230. }
  231. this._collectorsStarted = false;
  232. },
  233. /**
  234. * Usually this method is called from the collectors and events. It adds
  235. * entropy to the already collected entropy and increments the entropy
  236. * counter
  237. *
  238. * @param data
  239. * the entropy data to be added
  240. * @param entropyCounter
  241. * the entropy counter to be added
  242. */
  243. _collectEntropy: function(data, entropyCounter) {
  244. this._entropy += data;
  245. this._entropyCounter += (entropyCounter ? entropyCounter : 0);
  246. // if the entropy counter has reach the limit, update the hash and stop
  247. // the collectors
  248. if (this._entropyCounter >= this._maxEntropyCounter &&
  249. this._collectorsStarted) {
  250. this._hashUpdater();
  251. }
  252. },
  253. /**
  254. * Sets the entropy data with the hash that is created using the current
  255. * entropy data value. If the entropy counter has reached the max entropy
  256. * counter set, it stops the collectors
  257. *
  258. * @return the generated entropy hash
  259. */
  260. _hashUpdater: function() {
  261. var entropyHash = this._updateEntropyHash();
  262. if (this._entropyCounter >= this._maxEntropyCounter &&
  263. this._collectorsStarted) {
  264. entropyHash = this._stopCollectEntropy();
  265. var newPRNG = new cryptoPRNG.PRNG(entropyHash, this._tools);
  266. if (this._fullEntropyCallback) {
  267. this._fullEntropyCallback(newPRNG);
  268. } else {
  269. this._prng = newPRNG;
  270. }
  271. }
  272. return entropyHash;
  273. },
  274. _stopCollectEntropy: function() {
  275. // stop collecting entropy
  276. this._stopCollectors();
  277. var entropyHash = this._updateEntropyHash();
  278. this._entropy = '';
  279. this._entropyCounter = 0;
  280. return entropyHash;
  281. },
  282. _updateEntropyHash: function() {
  283. var digester = this._tools._mdUpdate;
  284. digester.update(this._entropy);
  285. var entropyHash = forge.util.bytesToHex(digester.digest().getBytes());
  286. this._entropy = entropyHash;
  287. return entropyHash;
  288. },
  289. /**
  290. * Set Private Key and an Entropy it adds to be used to collect Entropy. The
  291. * amount of randomness(entropy) depends on the pin that is used for Private
  292. * Key generating.
  293. *
  294. * @function
  295. * @param privateKey
  296. * {string} private key, as string in PEM format.
  297. * @param pinEntropy
  298. * the amount of randomness that can be extracted from the
  299. * Private Key.
  300. */
  301. setDataForPrivateKeyEntropyCollector: function(privateKeyPem, pinEntropy) {
  302. if (privateKeyPem && (!isNaN(pinEntropy)) && (pinEntropy > 0)) {
  303. this._privateKeyPem = privateKeyPem;
  304. this._pinEntropy = pinEntropy;
  305. this._usePrivateKeyEntropy = true;
  306. }
  307. },
  308. /**
  309. * Initializes collectors and starts them. Set Private Key and Entropy
  310. * before if this information should be used to collect Entropy.
  311. */
  312. startEntropyCollection: function(entropyCollectorCallback) {
  313. var hashUpdaterInterval = 1000;
  314. this._initAndStart(hashUpdaterInterval, entropyCollectorCallback);
  315. },
  316. /**
  317. * In the case that the maximum amount of entropy had been reached before
  318. * calling this method, it does not do anything, and a false is returned.
  319. * Otherwise, this method creates a PRNG if the collectors have not gathered
  320. * the maximum amount of entropy. In addition, it stops the collectors.
  321. *
  322. * This method return a true if the maximum entropy was already reached and
  323. * false in other case.
  324. */
  325. stopEntropyCollectionAndCreatePRNG: function() {
  326. var generatedWithMaximumEntropy = true;
  327. if (!this._prng) {
  328. var entropyHash = this._stopCollectEntropy();
  329. this._prng = new cryptoPRNG.PRNG(entropyHash, this._tools);
  330. generatedWithMaximumEntropy = false;
  331. }
  332. return generatedWithMaximumEntropy;
  333. },
  334. /**
  335. * This method creates a PRNG from a given seed, in Hexadecimal format. This
  336. * method is to be used from a Worker, which has received the seed from the
  337. * main thread.
  338. *
  339. * @param seed
  340. * A string of 64 characters. It represents a 32-byte array in
  341. * Hexadecimal.
  342. *
  343. */
  344. createPRNGFromSeed: function(seed) {
  345. this._prng = new cryptoPRNG.PRNG(seed, this._tools);
  346. },
  347. /**
  348. * Generates a random array of bytes, which is then formatted to hexadecimal
  349. * This method is to be called from the main thread, and the given string is
  350. * to be passed to a Worker.
  351. */
  352. generateRandomSeedInHex: function(lengthSeed) {
  353. this._seedLength = (lengthSeed ? lengthSeed : 32);
  354. var seedInBytes = this._prng.generate(this._seedLength);
  355. return forge.util.bytesToHex(seedInBytes);
  356. },
  357. getPRNG: function() {
  358. if (!this._prng) {
  359. throw new Error('The PRNG has not been initialized yet');
  360. }
  361. return this._prng;
  362. },
  363. getEntropyCollectedAsPercentage: function() {
  364. if (this._entropyCounter >= this._maxEntropyCounter) {
  365. return 100;
  366. }
  367. return this._entropyCounter * 100 / this._maxEntropyCounter;
  368. }
  369. };
  370. })();
  371. cryptoPRNG.Tools = (function() {
  372. 'use strict';
  373. return {
  374. _mdUpdate: null,
  375. _mdReseed: null,
  376. _formatKey: null,
  377. _formatSeed: null,
  378. _cipher: null,
  379. init: function() {
  380. this._mdUpdate = forge.md.sha256.create();
  381. this._mdReseed = forge.md.sha256.create();
  382. this._formatKey = function(key) {
  383. // convert the key into 32-bit integers
  384. var tmp = forge.util.createBuffer(key);
  385. key = [
  386. tmp.getInt32(),
  387. tmp.getInt32(),
  388. tmp.getInt32(),
  389. tmp.getInt32(),
  390. tmp.getInt32(),
  391. tmp.getInt32(),
  392. tmp.getInt32(),
  393. tmp.getInt32()
  394. ];
  395. // return the expanded key
  396. return forge.aes._expandKey(key, false);
  397. };
  398. this._formatSeed = function(seed) {
  399. // convert seed into 32-bit integers
  400. var tmp = forge.util.createBuffer(seed);
  401. seed = [
  402. tmp.getInt32(),
  403. tmp.getInt32(),
  404. tmp.getInt32(),
  405. tmp.getInt32()
  406. ];
  407. return seed;
  408. };
  409. this._cipher = function(key, counter) {
  410. var aes_output = [];
  411. forge.aes._updateBlock(
  412. this._formatKey(key), this._formatSeed('' + counter), aes_output,
  413. false);
  414. var aes_buffer = forge.util.createBuffer();
  415. aes_buffer.putInt32(aes_output[0]);
  416. aes_buffer.putInt32(aes_output[1]);
  417. aes_buffer.putInt32(aes_output[2]);
  418. aes_buffer.putInt32(aes_output[3]);
  419. return aes_buffer.getBytes();
  420. };
  421. return this;
  422. }
  423. };
  424. })();
  425. cryptoPRNG.PRNG = function(entropyHash, tools) {
  426. 'use strict';
  427. this._key = '';
  428. this._counter = 0;
  429. this._tools = null;
  430. if (tools) {
  431. this._tools = tools;
  432. } else {
  433. this._tools = cryptoPRNG.Tools.init();
  434. }
  435. this._entropyHash = entropyHash;
  436. };
  437. cryptoPRNG.PRNG.prototype = (function() {
  438. 'use strict';
  439. return {
  440. /**
  441. * Generates a random array of bytes using the gathered entropy data.
  442. *
  443. * @param count
  444. * the total number of random bytes to generate
  445. */
  446. generate: function(count) {
  447. var keyAux;
  448. if (this._key === null || this._key === '') {
  449. this._reseed();
  450. }
  451. // buffer where to store the random bytes
  452. var b = forge.util.createBuffer();
  453. var limitReach = 1;
  454. while (b.length() < count) {
  455. b.putBytes(this._tools._cipher(this._key, this._counter));
  456. this._counter++;
  457. if (b.length >= (limitReach * Math.pow(2, 20))) {
  458. keyAux = this._key;
  459. this._key = '';
  460. for (var i = 0; i < 2; i++) {
  461. this._key += this._tools._cipher(keyAux, this._counter);
  462. this._counter++;
  463. }
  464. limitReach++;
  465. }
  466. }
  467. // do it two times to ensure a key with 256 bits
  468. keyAux = this._key;
  469. this._key = '';
  470. for (var j = 0; j < 2; j++) {
  471. this._key += this._tools._cipher(keyAux, this._counter);
  472. this._counter++;
  473. }
  474. return b.getBytes(count);
  475. },
  476. /**
  477. * Reseeds the generator
  478. */
  479. _reseed: function() {
  480. var digester = this._tools._mdReseed;
  481. digester.start();
  482. digester.update(this._entropyHash);
  483. digester.update(this._key);
  484. this._key = forge.util.bytesToHex(digester.digest().getBytes());
  485. this._counter++;
  486. }
  487. };
  488. })();
  489. cryptoPRNG.Collectors = (function() {
  490. 'use strict';
  491. var MIN_MATH_ROUND_ENTROPY_FACTOR = 1000000;
  492. function _getMathRoundWithEntropy(data) {
  493. return Math.round(data * MIN_MATH_ROUND_ENTROPY_FACTOR);
  494. }
  495. return {
  496. getAjaxCollector: function() {
  497. return {
  498. startCollector: function() {
  499. // if jQuery is included
  500. if (window.jQuery !== undefined) {
  501. $(window)
  502. .ajaxStart(function() {
  503. cryptoPRNG._collectEntropy((+new Date()), 1);
  504. })
  505. .ajaxComplete(function() {
  506. cryptoPRNG._collectEntropy((+new Date()), 1);
  507. });
  508. }
  509. },
  510. stopCollector: function() {
  511. // if jQuery is included
  512. if (window.jQuery !== undefined) {
  513. $(window).unbind('ajaxStart');
  514. $(window).unbind('ajaxComplete');
  515. }
  516. return true;
  517. }
  518. };
  519. },
  520. getJSExecutionCollector: function() {
  521. return {
  522. _collectTimeout: null,
  523. startCollector: function() {
  524. var timer_start = (+new Date()), timer_end;
  525. this._collectTimeout = (function collect() {
  526. timer_end = (+new Date());
  527. var total_time = timer_end - timer_start;
  528. // because of browser baseline time checks we limit it
  529. if (total_time > 20) {
  530. cryptoPRNG._collectEntropy((+new Date()), 1);
  531. }
  532. timer_start = timer_end;
  533. return setTimeout(collect, 0);
  534. })();
  535. },
  536. stopCollector: function() {
  537. clearTimeout(this._collectTimeout);
  538. }
  539. };
  540. },
  541. getNavigatorInfoCollector: function() {
  542. return {
  543. startCollector: function() {
  544. if (typeof(navigator) !== 'undefined') {
  545. var _navString = '';
  546. for (var key in navigator) {
  547. if (typeof key !== 'undefined') {
  548. try {
  549. if (typeof(navigator[key]) === 'string') {
  550. _navString += navigator[key];
  551. }
  552. } catch (e) {
  553. // ignore any kind of exception
  554. }
  555. }
  556. }
  557. cryptoPRNG._collectEntropy(_navString, 1);
  558. }
  559. },
  560. stopCollector: function() {
  561. // just executed once, so no need to execute nothing more
  562. return true;
  563. }
  564. };
  565. },
  566. getMathRandomCollector: function() {
  567. return {
  568. startCollector: function() {
  569. cryptoPRNG._collectEntropy(Math.random(), 0);
  570. },
  571. stopCollector: function() {
  572. // just executed once, so no need to execute nothing more
  573. return true;
  574. }
  575. };
  576. },
  577. getPrivateKeyCollector: function(privateKeyPem, pinEntropy) {
  578. var privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
  579. return {
  580. pkCollector: true,
  581. startCollector: function() {
  582. cryptoPRNG._collectEntropy(privateKey.d, pinEntropy);
  583. },
  584. stopCollector: function() {
  585. // just executed once, so no need to execute nothing more
  586. return true;
  587. }
  588. };
  589. },
  590. getWebKitRandomCollector: function(globalCrypto, entropyQuantity) {
  591. return {
  592. startCollector: function() {
  593. var numPositions = entropyQuantity / 32;
  594. // get cryptographically strong entropy in Webkit
  595. var ab = new Uint32Array(numPositions);
  596. globalCrypto.getRandomValues(ab);
  597. var data = '';
  598. for (var i = 0; i < ab.length; i++) {
  599. data += '' + (ab[i]);
  600. }
  601. cryptoPRNG._collectEntropy(data, entropyQuantity);
  602. },
  603. stopCollector: function() {
  604. // just executed once, so no need to execute nothing more
  605. return true;
  606. }
  607. };
  608. },
  609. getMouseMoveCollector: function(ev) {
  610. // to ensure compatibility with IE 8
  611. ev = ev || window.event;
  612. cryptoPRNG._collectEntropy(
  613. (ev.x || ev.clientX || ev.offsetX || 0) +
  614. (ev.y || ev.clientY || ev.offsetY || 0) + (+new Date()),
  615. 3);
  616. },
  617. getMouseWheelCollector: function(ev) {
  618. ev = ev || window.event;
  619. cryptoPRNG._collectEntropy((ev.offsetY || 0) + (+new Date()), 3);
  620. },
  621. getMouseDownCollector: function(ev) {
  622. ev = ev || window.event;
  623. cryptoPRNG._collectEntropy(
  624. (ev.x || ev.clientX || ev.offsetX || 0) +
  625. (ev.y || ev.clientY || ev.offsetY || 0) + (+new Date()),
  626. 3);
  627. },
  628. getMouseUpCollector: function(ev) {
  629. ev = ev || window.event;
  630. cryptoPRNG._collectEntropy(
  631. (ev.x || ev.clientX || ev.offsetX || 0) +
  632. (ev.y || ev.clientY || ev.offsetY || 0) + (+new Date()),
  633. 3);
  634. },
  635. getTouchStartCollector: function(ev) {
  636. ev = ev || window.event;
  637. cryptoPRNG._collectEntropy(
  638. (ev.touches[0].pageX || ev.clientX || 0) +
  639. (ev.touches[0].pageY || ev.clientY || 0) + (+new Date()),
  640. 5);
  641. },
  642. getTouchMoveCollector: function(ev) {
  643. ev = ev || window.event;
  644. cryptoPRNG._collectEntropy(
  645. (ev.touches[0].pageX || ev.clientX || 0) +
  646. (ev.touches[0].pageY || ev.clientY || 0) + (+new Date()),
  647. 3);
  648. },
  649. getTouchEndCollector: function() {
  650. cryptoPRNG._collectEntropy((+new Date()), 1);
  651. },
  652. getGestureStartCollector: function(ev) {
  653. ev = ev || window.event;
  654. cryptoPRNG._collectEntropy(
  655. (ev.touches[0].pageX || ev.clientX || 0) +
  656. (ev.touches[0].pageY || ev.clientY || 0) + (+new Date()),
  657. 5);
  658. },
  659. getGestureEndCollector: function() {
  660. cryptoPRNG._collectEntropy((+new Date()), 1);
  661. },
  662. motionX: null,
  663. motionY: null,
  664. motionZ: null,
  665. getDeviceMotionCollector: function(ev) {
  666. ev = ev || window.event;
  667. var acceleration = ev.accelerationIncludingGravity;
  668. var currentX = (_getMathRoundWithEntropy(acceleration.x) || 0);
  669. var currentY = (_getMathRoundWithEntropy(acceleration.y) || 0);
  670. var currentZ = (_getMathRoundWithEntropy(acceleration.z) || 0);
  671. var rotation = ev.rotationRate;
  672. if (rotation !== null) {
  673. currentX += _getMathRoundWithEntropy(rotation.alpha);
  674. currentY += _getMathRoundWithEntropy(rotation.beta);
  675. currentZ += _getMathRoundWithEntropy(rotation.gamma);
  676. }
  677. // The API detects and delivers accelerometer data 50 times per
  678. // second
  679. // even if there is any event related, so this is
  680. // a way to control if it really changed or not
  681. if ((cryptoPRNG.Collectors.motionX === null) ||
  682. (cryptoPRNG.Collectors.motionY === null) ||
  683. (cryptoPRNG.Collectors.motionZ === null) ||
  684. (cryptoPRNG.Collectors.motionX !== currentX) ||
  685. (cryptoPRNG.Collectors.motionY !== currentY) ||
  686. (cryptoPRNG.Collectors.motionZ !== currentZ)) {
  687. cryptoPRNG.Collectors.motionX = currentX;
  688. cryptoPRNG.Collectors.motionY = currentY;
  689. cryptoPRNG.Collectors.motionZ = currentZ;
  690. cryptoPRNG._collectEntropy(
  691. currentX + currentY + currentZ + (+new Date()), 1);
  692. }
  693. },
  694. deviceOrientationX: null,
  695. deviceOrientationY: null,
  696. deviceOrientationZ: null,
  697. getDeviceOrientationCollector: function(ev) {
  698. ev = ev || window.event;
  699. var currentX =
  700. (_getMathRoundWithEntropy(ev.gamma) ||
  701. _getMathRoundWithEntropy(ev.x) || 0);
  702. var currentY =
  703. (_getMathRoundWithEntropy(ev.beta) ||
  704. _getMathRoundWithEntropy(ev.y) || 0);
  705. var currentZ =
  706. (_getMathRoundWithEntropy(ev.alpha) ||
  707. _getMathRoundWithEntropy(ev.z) || 0);
  708. // The API detects and delivers accelerometer data 50 times per
  709. // second
  710. // even if there is any event related, so this is
  711. // a way to control if it really changed or not
  712. if ((cryptoPRNG.Collectors.deviceOrientationX === null) ||
  713. (cryptoPRNG.Collectors.deviceOrientationY === null) ||
  714. (cryptoPRNG.Collectors.deviceOrientationZ === null) ||
  715. (cryptoPRNG.Collectors.deviceOrientationX !== currentX) ||
  716. (cryptoPRNG.Collectors.deviceOrientationY !== currentY) ||
  717. (cryptoPRNG.Collectors.deviceOrientationZ !== currentZ)) {
  718. cryptoPRNG.Collectors.deviceOrientationX = currentX;
  719. cryptoPRNG.Collectors.deviceOrientationY = currentY;
  720. cryptoPRNG.Collectors.deviceOrientationZ = currentZ;
  721. cryptoPRNG._collectEntropy(
  722. currentX + currentY + currentZ + (+new Date()), 1);
  723. }
  724. },
  725. getKeyDownCollector: function(ev) {
  726. ev = ev || window.event;
  727. cryptoPRNG._collectEntropy((ev.keyCode) + (+new Date()), 3);
  728. },
  729. getKeyUpCollector: function(ev) {
  730. ev = ev || window.event;
  731. cryptoPRNG._collectEntropy((ev.keyCode) + (+new Date()), 3);
  732. },
  733. getLoadCollector: function(ev) {
  734. ev = ev || window.event;
  735. cryptoPRNG._collectEntropy((ev.keyCode) + (+new Date()), 3);
  736. },
  737. getScrollCollector: function(ev) {
  738. ev = ev || window.event;
  739. cryptoPRNG._collectEntropy((ev.keyCode) + (+new Date()), 3);
  740. },
  741. getRequestsCollector: function(ev) {
  742. ev = ev || window.event;
  743. cryptoPRNG._collectEntropy((ev.keyCode) + (+new Date()), 3);
  744. }
  745. };
  746. })();