prng.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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. /* jshint node:true */
  9. 'use strict';
  10. var Tools = require('./tools');
  11. var validator = require('./input-validator');
  12. var codec = require('scytl-codec');
  13. var forge = require('node-forge');
  14. module.exports = Prng;
  15. /**
  16. * @class Prng
  17. * @classdesc A pseudo-random number generator that generates data, based on a
  18. * hash of the entropy that has been collected by various available
  19. * sources. This object is created and managed internally by the
  20. * secure random service.
  21. * @private
  22. * @param entropyHash
  23. * {Uint8Array} The hash of the collected entropy.
  24. */
  25. function Prng(entropyHash) {
  26. var tools_ = new Tools();
  27. var key_ = '';
  28. var counter_ = 0;
  29. /**
  30. * Generates some random bytes, using the gathered entropy data.
  31. *
  32. * @function nextBytes
  33. * @private
  34. * @param {number}
  35. * numBytes The number of bytes to generate.
  36. * @return {Uint8Array} The random bytes.
  37. */
  38. this.nextBytes = function(numBytes) {
  39. validator.checkIsPositiveNumber(
  40. numBytes, 'Number of random bytes to generate');
  41. var keyAux;
  42. if (key_ === null || key_ === '') {
  43. reseed();
  44. }
  45. // Buffer in which to store the random bytes.
  46. var b = forge.util.createBuffer();
  47. var limitReach = 1;
  48. while (b.length() < numBytes) {
  49. b.putBytes(tools_.cipher(key_, counter_));
  50. counter_++;
  51. if (b.length >= (limitReach * Math.pow(2, 20))) {
  52. keyAux = key_;
  53. key_ = '';
  54. for (var i = 0; i < 2; i++) {
  55. key_ += tools_.cipher(keyAux, counter_);
  56. counter_++;
  57. }
  58. limitReach++;
  59. }
  60. }
  61. // Do it twice to ensure a key with 256 bits.
  62. keyAux = key_;
  63. key_ = '';
  64. for (var j = 0; j < 2; j++) {
  65. key_ += tools_.cipher(keyAux, counter_);
  66. counter_++;
  67. }
  68. return codec.binaryDecode(b.getBytes(numBytes));
  69. };
  70. function reseed() {
  71. var digester = tools_.getReseedDigester();
  72. digester.start();
  73. digester.update(codec.hexEncode(entropyHash));
  74. digester.update(key_);
  75. key_ = codec.hexEncode(codec.binaryDecode(digester.digest().getBytes()));
  76. counter_++;
  77. }
  78. }